summaryrefslogtreecommitdiff
path: root/objectapp
diff options
context:
space:
mode:
authorgnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
committergnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
commit7a4f561e851fdc7246d804c3abb6748b8a4199a6 (patch)
treed2afc3463fd49625a9be482012f5c3bfcf7c42b9 /objectapp
downloadgnowsys-7a4f561e851fdc7246d804c3abb6748b8a4199a6.tar.gz
master trunk of gnowsys-studio
Diffstat (limited to 'objectapp')
-rw-r--r--objectapp/TAGS1945
-rw-r--r--objectapp/__init__.py55
-rw-r--r--objectapp/admin/__init__.py79
-rw-r--r--objectapp/admin/forms.py153
-rw-r--r--objectapp/admin/gbobject.py378
-rw-r--r--objectapp/admin/process.py373
-rw-r--r--objectapp/admin/system.py356
-rw-r--r--objectapp/admin/widgets.py171
-rw-r--r--objectapp/comparison.py93
-rw-r--r--objectapp/context_processors.py7
-rw-r--r--objectapp/feeds.py398
-rw-r--r--objectapp/fixtures/helloworld.json129
-rw-r--r--objectapp/forms.py35
-rw-r--r--objectapp/locale/de/LC_MESSAGES/django.mobin0 -> 15661 bytes
-rw-r--r--objectapp/locale/de/LC_MESSAGES/django.po1200
-rw-r--r--objectapp/locale/es/LC_MESSAGES/django.mobin0 -> 17490 bytes
-rw-r--r--objectapp/locale/es/LC_MESSAGES/django.po1199
-rw-r--r--objectapp/locale/fr/LC_MESSAGES/django.mobin0 -> 17740 bytes
-rw-r--r--objectapp/locale/fr/LC_MESSAGES/django.po1199
-rw-r--r--objectapp/locale/hu/LC_MESSAGES/django.mobin0 -> 15892 bytes
-rw-r--r--objectapp/locale/hu/LC_MESSAGES/django.po1194
-rw-r--r--objectapp/locale/it/LC_MESSAGES/django.mobin0 -> 14684 bytes
-rw-r--r--objectapp/locale/it/LC_MESSAGES/django.po1196
-rw-r--r--objectapp/locale/nl/LC_MESSAGES/django.mobin0 -> 17072 bytes
-rw-r--r--objectapp/locale/nl/LC_MESSAGES/django.po1195
-rw-r--r--objectapp/locale/pl/LC_MESSAGES/django.mobin0 -> 2088 bytes
-rw-r--r--objectapp/locale/pl/LC_MESSAGES/django.po1194
-rw-r--r--objectapp/locale/pt_BR/LC_MESSAGES/django.mobin0 -> 16686 bytes
-rw-r--r--objectapp/locale/pt_BR/LC_MESSAGES/django.po1195
-rw-r--r--objectapp/locale/ru/LC_MESSAGES/django.mobin0 -> 19225 bytes
-rw-r--r--objectapp/locale/ru/LC_MESSAGES/django.po1200
-rw-r--r--objectapp/locale/zh_CN/LC_MESSAGES/django.mobin0 -> 5431 bytes
-rw-r--r--objectapp/locale/zh_CN/LC_MESSAGES/django.po1182
-rw-r--r--objectapp/management/__init__.py1
-rw-r--r--objectapp/management/commands/__init__.py1
-rw-r--r--objectapp/management/commands/blogger2objectapp.py319
-rw-r--r--objectapp/management/commands/feed2objectapp.py209
-rw-r--r--objectapp/management/commands/objectapp2wp.py96
-rw-r--r--objectapp/management/commands/spam_cleanup.py72
-rw-r--r--objectapp/management/commands/wp2objectapp.py406
-rw-r--r--objectapp/managers.py146
-rw-r--r--objectapp/migrations/0001_initial.py459
-rw-r--r--objectapp/migrations/__init__.py0
-rw-r--r--objectapp/models.py700
-rw-r--r--objectapp/moderator.py194
-rw-r--r--objectapp/ping.py231
-rw-r--r--objectapp/plugins/__init__.py1
-rw-r--r--objectapp/plugins/admin.py78
-rw-r--r--objectapp/plugins/cms_app.py63
-rw-r--r--objectapp/plugins/cms_plugins.py182
-rw-r--r--objectapp/plugins/menu.py180
-rw-r--r--objectapp/plugins/migrations/0001_initial.py151
-rw-r--r--objectapp/plugins/migrations/0002_auto__add_field_latestgbobjectsplugin_template_to_render__add_field_sele.py132
-rw-r--r--objectapp/plugins/migrations/0003_auto__del_field_latestgbobjectsplugin_objecttype__del_field_latestgbobjectsp.py172
-rw-r--r--objectapp/plugins/migrations/0004_auto__add_field_latestgbobjectsplugin_subobjecttypes.py142
-rw-r--r--objectapp/plugins/migrations/0005_auto__add_randomgbobjectsplugin.py151
-rw-r--r--objectapp/plugins/migrations/__init__.py1
-rw-r--r--objectapp/plugins/models.py152
-rw-r--r--objectapp/plugins/placeholder.py61
-rw-r--r--objectapp/plugins/settings.py74
-rw-r--r--objectapp/search.py180
-rw-r--r--objectapp/settings.py135
-rw-r--r--objectapp/signals.py102
-rw-r--r--objectapp/sitemaps.py183
-rw-r--r--objectapp/spam_checker/__init__.py82
-rw-r--r--objectapp/spam_checker/backends/__init__.py1
-rw-r--r--objectapp/spam_checker/backends/all_is_spam.py53
-rw-r--r--objectapp/spam_checker/backends/automattic.py106
-rw-r--r--objectapp/spam_checker/backends/mollom.py106
-rw-r--r--objectapp/spam_checker/backends/typepad.py94
-rw-r--r--objectapp/static/objectapp/css/config.rb9
-rw-r--r--objectapp/static/objectapp/css/dashboard_objectapp.css32
-rw-r--r--objectapp/static/objectapp/css/ie.css39
-rw-r--r--objectapp/static/objectapp/css/jquery.autocomplete.css48
-rw-r--r--objectapp/static/objectapp/css/print.css33
-rw-r--r--objectapp/static/objectapp/css/screen.css569
-rw-r--r--objectapp/static/objectapp/css/screen1.css505
-rw-r--r--objectapp/static/objectapp/css/slider.css12
-rw-r--r--objectapp/static/objectapp/css/src/_base.sass32
-rw-r--r--objectapp/static/objectapp/css/src/_calendar.sass15
-rw-r--r--objectapp/static/objectapp/css/src/_gbobject.sass35
-rw-r--r--objectapp/static/objectapp/css/src/_paginator.sass19
-rw-r--r--objectapp/static/objectapp/css/src/_tag-cloud.sass21
-rw-r--r--objectapp/static/objectapp/css/src/dashboard_objectapp.sass66
-rw-r--r--objectapp/static/objectapp/css/src/ie.sass3
-rw-r--r--objectapp/static/objectapp/css/src/print.sass22
-rw-r--r--objectapp/static/objectapp/css/src/screen.sass160
-rw-r--r--objectapp/static/objectapp/css/src/slider.sass74
-rw-r--r--objectapp/static/objectapp/css/structures.css662
-rw-r--r--objectapp/static/objectapp/css/wymeditor_styles.css45
-rw-r--r--objectapp/static/objectapp/img/background.gifbin0 -> 196 bytes
-rw-r--r--objectapp/static/objectapp/img/bullet.pngbin0 -> 289 bytes
-rw-r--r--objectapp/static/objectapp/img/comments.pngbin0 -> 426 bytes
-rw-r--r--objectapp/static/objectapp/img/community.pngbin0 -> 4394 bytes
-rw-r--r--objectapp/static/objectapp/img/favicon.icobin0 -> 1150 bytes
-rw-r--r--objectapp/static/objectapp/img/favicon.pngbin0 -> 660 bytes
-rw-r--r--objectapp/static/objectapp/img/grid.pngbin0 -> 206 bytes
-rw-r--r--objectapp/static/objectapp/img/help.pngbin0 -> 764 bytes
-rw-r--r--objectapp/static/objectapp/img/logo.pngbin0 -> 4864 bytes
-rw-r--r--objectapp/static/objectapp/img/manage.pngbin0 -> 228 bytes
-rw-r--r--objectapp/static/objectapp/img/objecttype.pngbin0 -> 438 bytes
-rw-r--r--objectapp/static/objectapp/img/plugin.pngbin0 -> 3530 bytes
-rw-r--r--objectapp/static/objectapp/img/preview.pngbin0 -> 470 bytes
-rw-r--r--objectapp/static/objectapp/img/rss.pngbin0 -> 561 bytes
-rw-r--r--objectapp/static/objectapp/img/shorturl.pngbin0 -> 343 bytes
-rw-r--r--objectapp/static/objectapp/img/sitemap.pngbin0 -> 3215 bytes
-rw-r--r--objectapp/static/objectapp/img/tags.pngbin0 -> 667 bytes
-rw-r--r--objectapp/static/objectapp/img/trans.pngbin0 -> 115 bytes
-rw-r--r--objectapp/static/objectapp/img/wlw/comments.pngbin0 -> 1442 bytes
-rw-r--r--objectapp/static/objectapp/img/wlw/objectapp.pngbin0 -> 660 bytes
-rw-r--r--objectapp/static/objectapp/img/wlw/watermark.pngbin0 -> 12376 bytes
-rw-r--r--objectapp/static/objectapp/js/jquery.autocomplete.js13
-rw-r--r--objectapp/static/objectapp/js/jquery.bgiframe.js10
-rw-r--r--objectapp/static/objectapp/js/jquery.js167
-rw-r--r--objectapp/static/objectapp/js/jquery.masonry.js12
-rw-r--r--objectapp/static/objectapp/js/jquery.ui.js68
-rw-r--r--objectapp/static/objectapp/js/markitup/jquery.markitup.js574
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/bold.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/clean.pngbin0 -> 667 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/h1.pngbin0 -> 276 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/h2.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/h3.pngbin0 -> 306 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/h4.pngbin0 -> 293 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/h5.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/h6.pngbin0 -> 310 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/image.pngbin0 -> 516 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/italic.pngbin0 -> 223 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/link.pngbin0 -> 343 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/list-bullet.pngbin0 -> 344 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/list-item.pngbin0 -> 248 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/list-numeric.pngbin0 -> 357 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/paragraph.pngbin0 -> 361 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/picture.pngbin0 -> 606 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/preview.pngbin0 -> 537 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/images/stroke.pngbin0 -> 269 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/set.js38
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/html/style.css59
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/bold.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/code.pngbin0 -> 859 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/h1.pngbin0 -> 276 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/h2.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/h3.pngbin0 -> 306 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/h4.pngbin0 -> 293 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/h5.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/h6.pngbin0 -> 310 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/italic.pngbin0 -> 223 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/link.pngbin0 -> 343 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/list-bullet.pngbin0 -> 344 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/list-numeric.pngbin0 -> 357 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/picture.pngbin0 -> 606 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/preview.pngbin0 -> 537 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/images/quotes.pngbin0 -> 743 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/set.js49
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/markdown/style.css54
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/bold.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/code.pngbin0 -> 859 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h1.pngbin0 -> 276 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h2.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h3.pngbin0 -> 306 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h4.pngbin0 -> 293 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h5.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/italic.pngbin0 -> 223 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/link.pngbin0 -> 343 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/list-bullet.pngbin0 -> 344 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/list-numeric.pngbin0 -> 357 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/picture.pngbin0 -> 606 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/preview.pngbin0 -> 537 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/quotes.pngbin0 -> 743 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/set.js48
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/restructuredtext/style.css53
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/bold.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/code.pngbin0 -> 859 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/h1.pngbin0 -> 276 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/h2.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/h3.pngbin0 -> 306 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/h4.pngbin0 -> 293 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/h5.pngbin0 -> 304 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/h6.pngbin0 -> 310 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/italic.pngbin0 -> 223 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/link.pngbin0 -> 343 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/list-bullet.pngbin0 -> 344 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/list-numeric.pngbin0 -> 357 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/paragraph.pngbin0 -> 361 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/picture.pngbin0 -> 606 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/preview.pngbin0 -> 537 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/quotes.pngbin0 -> 743 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/images/stroke.pngbin0 -> 269 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/set.js37
-rw-r--r--objectapp/static/objectapp/js/markitup/sets/textile/style.css60
-rw-r--r--objectapp/static/objectapp/js/markitup/skins/django/images/handle.pngbin0 -> 258 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/skins/django/images/menu.pngbin0 -> 27151 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/skins/django/images/submenu.pngbin0 -> 240 bytes
-rw-r--r--objectapp/static/objectapp/js/markitup/skins/django/style.css128
-rw-r--r--objectapp/static/objectapp/js/markitup/templates/preview.css5
-rw-r--r--objectapp/static/objectapp/js/markitup/templates/preview.html11
-rw-r--r--objectapp/static/objectapp/js/mptt_m2m_selectbox.js133
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-blockquote.pngbin0 -> 196 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h1.pngbin0 -> 166 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h2.pngbin0 -> 172 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h3.pngbin0 -> 170 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h4.pngbin0 -> 172 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h5.pngbin0 -> 172 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h6.pngbin0 -> 171 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-p.pngbin0 -> 3607 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-pre.pngbin0 -> 177 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/wymiframe.css90
-rw-r--r--objectapp/static/objectapp/js/wymeditor/iframe/default/wymiframe.html26
-rw-r--r--objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.js4688
-rw-r--r--objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.min.js1
-rw-r--r--objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.pack.js1
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/bg.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/ca.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/cs.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/cy.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/de.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/en.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/es.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/fa.js46
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/fi.js44
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/fr.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/gl.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/he.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/hr.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/hu.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/it.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/nb.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/nl.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/nn.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/pl.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/pt-br.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/pt.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/ru.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/sv.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/tr.js45
-rw-r--r--objectapp/static/objectapp/js/wymeditor/lang/zh_cn.js47
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js52
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/fullscreen/icon_fullscreen.gifbin0 -> 509 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js127
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js57
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js91
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/resizable/readme.txt124
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/tidy/README19
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js82
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/tidy/tidy.php36
-rw-r--r--objectapp/static/objectapp/js/wymeditor/plugins/tidy/wand.pngbin0 -> 715 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/compact/icons.pngbin0 -> 3651 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/compact/skin.css134
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/compact/skin.js35
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/default/icons.pngbin0 -> 3651 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/default/skin.css133
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/default/skin.js40
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/django/icons.pngbin0 -> 7236 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/django/skin.css136
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/django/skin.js42
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.header.gifbin0 -> 781 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.selector.silver.gifbin0 -> 1621 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.wymeditor.pngbin0 -> 498 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/minimal/images/icons.silver.gifbin0 -> 15382 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/minimal/skin.css131
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/minimal/skin.js30
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/silver/COPYING674
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/silver/README27
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.header.gifbin0 -> 781 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.selector.silver.gifbin0 -> 1621 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.wymeditor.pngbin0 -> 498 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/silver/images/icons.silver.gifbin0 -> 15382 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/silver/skin.css297
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/silver/skin.js61
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/twopanels/icons.pngbin0 -> 3651 bytes
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/twopanels/skin.css134
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/twopanels/skin.js39
-rw-r--r--objectapp/static/objectapp/js/wymeditor/skins/wymeditor_icon.pngbin0 -> 1028 bytes
-rw-r--r--objectapp/templates/404.html46
-rw-r--r--objectapp/templates/500.html46
-rw-r--r--objectapp/templates/admin/objectapp/app_index.html41
-rw-r--r--objectapp/templates/admin/objectapp/gbobject/autocomplete_tags.js11
-rw-r--r--objectapp/templates/admin/objectapp/gbobject/markitup.js3
-rw-r--r--objectapp/templates/admin/objectapp/gbobject/tinymce_textareas.js23
-rw-r--r--objectapp/templates/admin/objectapp/gbobject/wymeditor.js10
-rw-r--r--objectapp/templates/admin/objectapp/widgets/_content_stats.html38
-rw-r--r--objectapp/templates/admin/objectapp/widgets/_draft_gbobjects.html37
-rw-r--r--objectapp/templates/admin/objectapp/widgets/_recent_comments.html45
-rw-r--r--objectapp/templates/admin/objectapp/widgets/_recent_linkbacks.html31
-rw-r--r--objectapp/templates/admin/objectapp/widgets/base.html6
-rw-r--r--objectapp/templates/admin/objectapp/widgets/content_stats.html12
-rw-r--r--objectapp/templates/admin/objectapp/widgets/draft_gbobjects.html12
-rw-r--r--objectapp/templates/admin/objectapp/widgets/quickpost.html40
-rw-r--r--objectapp/templates/admin/objectapp/widgets/recent_comments.html12
-rw-r--r--objectapp/templates/admin/objectapp/widgets/recent_linkbacks.html12
-rw-r--r--objectapp/templates/comments/comment_authors_email.txt6
-rw-r--r--objectapp/templates/comments/comment_notification_email.txt14
-rw-r--r--objectapp/templates/comments/comment_reply_email.txt6
-rw-r--r--objectapp/templates/comments/objectapp/gbobject/form.html22
-rw-r--r--objectapp/templates/comments/objectapp/gbobject/posted.html13
-rw-r--r--objectapp/templates/comments/objectapp_gbobject_preview.html37
-rw-r--r--objectapp/templates/feeds/comment_description.html1
-rw-r--r--objectapp/templates/feeds/comment_title.html2
-rw-r--r--objectapp/templates/feeds/discussion_description.html1
-rw-r--r--objectapp/templates/feeds/discussion_title.html2
-rw-r--r--objectapp/templates/feeds/gbobject_description.html1
-rw-r--r--objectapp/templates/feeds/gbobject_title.html1
-rw-r--r--objectapp/templates/feeds/pingback_description.html1
-rw-r--r--objectapp/templates/feeds/pingback_title.html2
-rw-r--r--objectapp/templates/feeds/trackback_description.html1
-rw-r--r--objectapp/templates/feeds/trackback_title.html2
-rw-r--r--objectapp/templates/objectapp/_gbobject_detail.html148
-rw-r--r--objectapp/templates/objectapp/_header.html56
-rw-r--r--objectapp/templates/objectapp/_narrative_detail.html117
-rw-r--r--objectapp/templates/objectapp/_subtype_detail.html1
-rw-r--r--objectapp/templates/objectapp/author/gbobject_list.html1
-rw-r--r--objectapp/templates/objectapp/author_list.html27
-rw-r--r--objectapp/templates/objectapp/base.html107
-rw-r--r--objectapp/templates/objectapp/cms/gbobject_detail.html8
-rw-r--r--objectapp/templates/objectapp/cms/gbobject_list.html12
-rw-r--r--objectapp/templates/objectapp/cms/random_gbobjects.html4
-rw-r--r--objectapp/templates/objectapp/gbobject_archive.html30
-rw-r--r--objectapp/templates/objectapp/gbobject_archive_day.html23
-rw-r--r--objectapp/templates/objectapp/gbobject_archive_month.html31
-rw-r--r--objectapp/templates/objectapp/gbobject_archive_year.html21
-rw-r--r--objectapp/templates/objectapp/gbobject_detail.html242
-rw-r--r--objectapp/templates/objectapp/gbobject_list.html73
-rw-r--r--objectapp/templates/objectapp/gbobject_search.html49
-rw-r--r--objectapp/templates/objectapp/gbobject_trackback.xml9
-rw-r--r--objectapp/templates/objectapp/login.html40
-rw-r--r--objectapp/templates/objectapp/objecttype/gbobject_list.html1
-rw-r--r--objectapp/templates/objectapp/objecttype_list.html25
-rw-r--r--objectapp/templates/objectapp/opensearch.xml24
-rw-r--r--objectapp/templates/objectapp/password.html35
-rw-r--r--objectapp/templates/objectapp/rsd.xml16
-rw-r--r--objectapp/templates/objectapp/sitemap.html70
-rw-r--r--objectapp/templates/objectapp/skeleton.html488
-rw-r--r--objectapp/templates/objectapp/tag/gbobject_list.html1
-rw-r--r--objectapp/templates/objectapp/tag_list.html24
-rw-r--r--objectapp/templates/objectapp/tags/archives_gbobjects.html15
-rw-r--r--objectapp/templates/objectapp/tags/archives_gbobjects_link.html3
-rw-r--r--objectapp/templates/objectapp/tags/archives_gbobjects_tree.html43
-rw-r--r--objectapp/templates/objectapp/tags/authors.html15
-rw-r--r--objectapp/templates/objectapp/tags/breadcrumbs.html7
-rw-r--r--objectapp/templates/objectapp/tags/calendar.html17
-rw-r--r--objectapp/templates/objectapp/tags/dummy.html1
-rw-r--r--objectapp/templates/objectapp/tags/featured_gbobjects.html12
-rw-r--r--objectapp/templates/objectapp/tags/gbobjects.html16
-rw-r--r--objectapp/templates/objectapp/tags/objecttypes.html16
-rw-r--r--objectapp/templates/objectapp/tags/pagination.html59
-rw-r--r--objectapp/templates/objectapp/tags/popular_gbobjects.html16
-rw-r--r--objectapp/templates/objectapp/tags/random_gbobjects.html12
-rw-r--r--objectapp/templates/objectapp/tags/recent_comments.html20
-rw-r--r--objectapp/templates/objectapp/tags/recent_gbobjects.html12
-rw-r--r--objectapp/templates/objectapp/tags/recent_linkbacks.html21
-rw-r--r--objectapp/templates/objectapp/tags/similar_gbobjects.html12
-rw-r--r--objectapp/templates/objectapp/tags/slider_gbobjects.html27
-rw-r--r--objectapp/templates/objectapp/tags/tag_cloud.html13
-rw-r--r--objectapp/templates/objectapp/wlwmanifest.xml63
-rw-r--r--objectapp/templates/objectapp/wxr.xml125
-rw-r--r--objectapp/templates/objectappforms/gbobjectform.html22
-rw-r--r--objectapp/templates/objectappforms/processform.html22
-rw-r--r--objectapp/templates/objectappforms/systemform.html22
-rw-r--r--objectapp/templatetags/__init__.py1
-rw-r--r--objectapp/templatetags/objectapp_admin_tags.py89
-rw-r--r--objectapp/templatetags/objectapp_tags.py375
-rw-r--r--objectapp/templatetags/zbreadcrumbs.py147
-rw-r--r--objectapp/templatetags/zcalendar.py109
-rw-r--r--objectapp/tests/__init__.py104
-rw-r--r--objectapp/tests/admin.py133
-rw-r--r--objectapp/tests/comparison.py50
-rw-r--r--objectapp/tests/custom_spam_checker.py10
-rw-r--r--objectapp/tests/custom_url_shortener.py57
-rw-r--r--objectapp/tests/custom_views_detail_urls.py91
-rw-r--r--objectapp/tests/feeds.py343
-rw-r--r--objectapp/tests/gbobject.py331
-rw-r--r--objectapp/tests/managers.py296
-rw-r--r--objectapp/tests/metaweblog.py343
-rw-r--r--objectapp/tests/moderator.py186
-rw-r--r--objectapp/tests/objecttype.py116
-rw-r--r--objectapp/tests/ping.py187
-rw-r--r--objectapp/tests/pingback.py253
-rw-r--r--objectapp/tests/quick_gbobject.py120
-rw-r--r--objectapp/tests/signals.py154
-rw-r--r--objectapp/tests/sitemaps.py145
-rw-r--r--objectapp/tests/spam_checker.py104
-rw-r--r--objectapp/tests/templates/objectapp/_gbobject_detail.html3
-rw-r--r--objectapp/tests/templates/objectapp/base.html9
-rw-r--r--objectapp/tests/templates/objectapp/gbobject_detail.html7
-rw-r--r--objectapp/tests/templates/objectapp/gbobject_list.html10
-rw-r--r--objectapp/tests/templates/objectapp/gbobject_search.html1
-rw-r--r--objectapp/tests/templatetags.py590
-rw-r--r--objectapp/tests/url_shortener.py112
-rw-r--r--objectapp/tests/urls.py85
-rw-r--r--objectapp/tests/utils.py90
-rw-r--r--objectapp/tests/views.py363
-rw-r--r--objectapp/testsettings.py103
-rw-r--r--objectapp/url_shortener/__init__.py88
-rw-r--r--objectapp/url_shortener/backends/__init__.py1
-rw-r--r--objectapp/url_shortener/backends/bitly.py84
-rw-r--r--objectapp/url_shortener/backends/default.py75
-rw-r--r--objectapp/urls/__init__.py84
-rw-r--r--objectapp/urls/add.py78
-rw-r--r--objectapp/urls/authors.py78
-rw-r--r--objectapp/urls/capabilities.py98
-rw-r--r--objectapp/urls/discussions.py73
-rw-r--r--objectapp/urls/feeds.py113
-rw-r--r--objectapp/urls/gbobjects.py114
-rw-r--r--objectapp/urls/objecttypes.py84
-rw-r--r--objectapp/urls/quick_gbobject.py72
-rw-r--r--objectapp/urls/search.py71
-rw-r--r--objectapp/urls/sitemap.py73
-rw-r--r--objectapp/urls/tags.py76
-rw-r--r--objectapp/urls/trackback.py72
-rw-r--r--objectapp/views/__init__.py1
-rw-r--r--objectapp/views/add.py126
-rw-r--r--objectapp/views/authors.py92
-rw-r--r--objectapp/views/channels.py58
-rw-r--r--objectapp/views/decorators.py151
-rw-r--r--objectapp/views/gbobjects.py99
-rw-r--r--objectapp/views/objecttypes.py94
-rw-r--r--objectapp/views/quick_gbobject.py118
-rw-r--r--objectapp/views/search.py91
-rw-r--r--objectapp/views/sitemap.py77
-rw-r--r--objectapp/views/tags.py97
-rw-r--r--objectapp/views/trackback.py109
-rw-r--r--objectapp/xmlrpc/__init__.py81
-rw-r--r--objectapp/xmlrpc/metaweblog.py344
-rw-r--r--objectapp/xmlrpc/pingback.py187
423 files changed, 44883 insertions, 0 deletions
diff --git a/objectapp/TAGS b/objectapp/TAGS
new file mode 100644
index 0000000..82ecaf2
--- /dev/null
+++ b/objectapp/TAGS
@@ -0,0 +1,1945 @@
+
+fixtures/helloworld.json,158
+ "login_required": false,67,1886
+ "comment_enabled": true,70,1990
+ "login_required": false,101,5063
+ "comment_enabled": true,104,5167
+
+feeds.py,2021
+class ObjectappFeed(28,992
+ def __init__(32,1085
+class GbobjectFeed(40,1342
+ def item_pubdate(45,1517
+ def item_objecttypes(49,1632
+ def item_author_name(53,1783
+ def item_author_email(59,2007
+ def item_author_link(63,2132
+ def item_enclosure_url(72,2460
+ def item_enclosure_length(81,2733
+ def item_enclosure_mime_type(85,2842
+class LatestGbobjects(90,2961
+ def link(93,3039
+ def items(97,3157
+ def title(101,3281
+ def description(105,3402
+class ObjecttypeGbobjects(110,3543
+ def get_object(113,3625
+ def items(117,3760
+ def link(121,3912
+ def title(125,4012
+ def description(129,4135
+class AuthorGbobjects(134,4282
+ def get_object(137,4357
+ def items(141,4511
+ def link(145,4668
+ def title(149,4797
+ def description(153,4915
+class TagGbobjects(158,5049
+ def get_object(161,5117
+ def items(165,5251
+ def link(170,5446
+ def title(174,5565
+ def description(178,5680
+class SearchGbobjects(183,5819
+ def get_object(186,5901
+ def items(193,6131
+ def link(197,6277
+ def title(201,6420
+ def description(205,6536
+class GbobjectDiscussions(210,6676
+ def get_object(215,6879
+ def items(222,7238
+ def item_pubdate(226,7370
+ def item_link(230,7484
+ def link(234,7591
+ def item_author_name(238,7689
+ def item_author_email(242,7804
+ def item_author_link(246,7929
+ def title(250,8049
+ def description(254,8158
+class GbobjectComments(259,8305
+ def items(264,8502
+ def item_link(268,8628
+ def title(272,8749
+ def description(276,8855
+ def item_enclosure_url(280,8998
+ def item_enclosure_length(284,9144
+ def item_enclosure_mime_type(288,9253
+class GbobjectPingbacks(293,9372
+ def items(298,9573
+ def item_link(302,9701
+ def title(306,9824
+ def description(310,9931
+class GbobjectTrackbacks(315,10076
+ def items(320,10281
+ def item_link(324,10411
+ def title(328,10536
+ def description(332,10644
+
+signals.py,175
+def disable_for_loaddata(10,160
+ def wrapper(16,363
+def ping_directories_handler(26,598
+def ping_external_urls_handler(38,976
+def disconnect_objectapp_signals(48,1276
+
+management/commands/feed2objectapp.py,206
+class Command(23,748
+ def __init__(42,1550
+ def write_out(50,1851
+ def handle_label(56,2096
+ def import_gbobjects(80,3061
+ def import_objecttypes(130,5400
+ def import_tags(138,5733
+
+management/commands/spam_cleanup.py,51
+class Command(9,238
+ def handle_noargs(14,393
+
+management/commands/wp2objectapp.py,421
+class Command(29,919
+ def __init__(56,2052
+ def write_out(64,2353
+ def handle_label(70,2598
+ def import_authors(97,3610
+ def migrate_author(120,4480
+ def import_objecttypes(151,5881
+ def import_tags(175,6979
+ def get_gbobject_tags(188,7607
+ def get_gbobject_objecttypes(198,8031
+ def import_gbobject(208,8455
+ def import_gbobjects(261,10905
+ def import_comments(283,11937
+
+management/commands/blogger2objectapp.py,497
+class Command(26,810
+ def __init__(44,1673
+ def write_out(51,1935
+ def handle_noargs(57,2180
+ def select_blog_id(108,4201
+ def get_Objecttype(129,5020
+ def import_posts(139,5289
+ def import_comments(176,7171
+def convert_blogger_timestamp(211,8393
+def is_draft(217,8572
+def get_blog_id(225,8740
+def get_post_id(229,8815
+class BloggerManager(233,8890
+ def __init__(235,8921
+ def get_blogs(241,9166
+ def get_posts(246,9306
+ def get_comments(251,9468
+
+management/commands/objectapp2wp.py,52
+class Command(16,459
+ def handle_noargs(21,649
+
+context_processors.py,18
+def version(5,75
+
+tests/feeds.py,833
+class ObjectappFeedsTestCase(33,1155
+ def setUp(37,1281
+ def create_published_gbobject(44,1637
+ def create_discussions(58,2215
+ def test_gbobject_feed(75,3344
+ def test_gbobject_feed_enclosure(94,4296
+ def test_latest_gbobjects(118,5477
+ def test_Objecttype_gbobjects(129,5921
+ def test_author_gbobjects(142,6564
+ def test_tag_gbobjects(153,7149
+ def test_search_gbobjects(165,7716
+ class FakeRequest:FakeRequest166,7753
+ def __init__(167,7780
+ def test_gbobject_discussions(182,8492
+ def test_gbobject_comments(204,9660
+ def test_gbobject_pingbacks(223,10639
+ def test_gbobject_trackbacks(236,11280
+ def test_gbobject_feed_no_authors(249,11926
+ def test_gbobject_feed_rss_or_atom(258,12286
+ def test_discussion_feed_with_same_slugs(269,12736
+
+tests/signals.py,286
+class SignalsTestCase(12,356
+ def test_disable_for_loaddata(15,423
+ def make_top(19,516
+ def call(22,567
+ def test_ping_directories_handler(29,712
+ def fake_pinger(33,826
+ def test_ping_external_urls_handler(61,1918
+ def fake_pinger(65,2037
+
+tests/metaweblog.py,491
+class MetaWeblogTestCase(23,737
+ def setUp(27,844
+ def test_authenticate(63,2573
+ def test_get_users_blogs(79,3433
+ def test_get_user_info(88,3866
+ def test_get_authors(102,4563
+ def test_get_objecttypes(112,5073
+ def test_new_Objecttype(151,7147
+ def test_get_recent_posts(167,8077
+ def test_delete_post(173,8365
+ def test_get_post(182,8798
+ def test_new_post(207,10234
+ def test_edit_post(224,11049
+ def test_new_media_object(281,13891
+
+tests/spam_checker.py,72
+class SpamCheckerTestCase(11,253
+ def test_get_spam_checker(14,339
+
+tests/quick_gbobject.py,119
+class QuickGbobjectTestCase(10,234
+ def setUp(14,353
+ def tearDown(18,456
+ def test_quick_gbobject(21,530
+
+tests/url_shortener.py,121
+class URLShortenerTestCase(12,323
+ def setUp(15,411
+ def tearDown(18,499
+ def test_get_url_shortener(21,590
+
+tests/__init__.py,19
+def suite(32,1630
+
+tests/managers.py,585
+class ManagersTestCase(18,482
+ def setUp(20,517
+ def test_tags_published(51,2027
+ def test_author_published_manager_get_query_set(56,2251
+ def test_gbobjects_published(65,2651
+ def test_gbobject_published_manager_get_query_set(86,3805
+ def test_gbobject_published_manager_on_site(107,4805
+ def test_gbobject_published_manager_basic_search(115,5187
+ def test_gbobject_published_manager_advanced_search(123,5634
+ def test_gbobject_published_manager_advanced_search_with_punctuation(227,11470
+ def test_gbobject_published_manager_search(238,12007
+
+tests/custom_views_detail_urls.py,73
+def call_with_template_and_extra_context(13,380
+ def wrapper(18,537
+
+tests/views.py,1337
+class ViewsBaseCase(17,494
+ def setUp(22,584
+ def tearDown(62,2283
+ def create_published_gbobject(66,2445
+ def check_publishing_context(79,2966
+class ObjectappViewsTestCase(92,3513
+ def test_objectapp_gbobject_archive_index(100,3780
+ def test_objectapp_gbobject_archive_year(103,3883
+ def test_objectapp_gbobject_archive_month(106,3990
+ def test_objectapp_gbobject_archive_day(109,4101
+ def test_objectapp_gbobject_shortlink(112,4213
+ def test_objectapp_gbobject_detail(117,4444
+ def test_objectapp_gbobject_detail_login(135,5278
+ def test_objectapp_gbobject_detail_password(142,5580
+ def test_objectapp_gbobject_channel(157,6395
+ def test_objectapp_Objecttype_list(160,6505
+ def test_objectapp_Objecttype_detail(167,6873
+ def test_objectapp_Objecttype_detail_paginated(172,7157
+ def test_objectapp_author_list(193,8274
+ def test_objectapp_author_detail(200,8617
+ def test_objectapp_tag_list(205,8889
+ def test_objectapp_tag_detail(212,9137
+ def test_objectapp_gbobject_search(217,9393
+ def test_objectapp_sitemap(228,9970
+ def test_objectapp_trackback(239,10581
+class ObjectappCustomDetailViews(275,12268
+ def test_custom_Objecttype_detail(283,12586
+ def test_custom_author_detail(289,12920
+ def test_custom_tag_detail(295,13246
+
+tests/moderator.py,249
+class GbobjectCommentModeratorTestCase(14,428
+ def setUp(17,518
+ def test_email(31,1123
+ def test_do_email_notification(48,1938
+ def test_do_email_authors(58,2465
+ def test_do_email_reply(72,3198
+ def test_moderate(103,4855
+
+tests/objecttype.py,138
+class ObjecttypeTestCase(10,244
+ def setUp(12,281
+ def test_gbobjects_published(27,977
+ def test_gbobjects_tree_path(47,1818
+
+tests/templates/objectapp/_gbobject_detail.html,23
+{{ object.title }}1,0
+
+tests/templates/objectapp/base.html,56
+Objectapp's Blog - {% block title %}{% endblock %}3,16
+
+tests/custom_url_shortener.py,19
+def backend(8,184
+
+tests/admin.py,238
+class GbobjectAdminTestCase(10,228
+ def setUp(14,341
+ def tearDown(24,798
+ def test_gbobject_add_and_change(27,872
+class ObjecttypeAdminTestCase(54,2153
+ def setUp(58,2271
+ def test_Objecttype_add_and_change(62,2438
+
+tests/custom_spam_checker.py,19
+def backend(8,183
+
+tests/utils.py,76
+class TestTransport(8,123
+ def __init__(12,236
+ def request(16,359
+
+tests/gbobject.py,827
+class GbobjectTestCase(24,761
+ def setUp(26,796
+ def test_discussions(32,1002
+ def test_str(85,3847
+ def test_word_count(88,3940
+ def test_comments_are_open(91,4027
+ def test_is_actual(102,4523
+ def test_is_visible(111,4935
+ def test_short_url(118,5224
+ def test_previous_gbobject(128,5770
+ def test_next_gbobject(149,6858
+ def test_related_published(170,7926
+class GbobjectHtmlContentTestCase(192,8877
+ def setUp(194,8923
+ def tearDown(203,9256
+ def test_html_content_default(207,9392
+ def test_html_content_textitle(216,9789
+ def test_html_content_markdown(231,10506
+ def test_html_content_restructuredtext(246,11203
+class GbobjectGetBaseModelTestCase(262,11934
+ def setUp(264,11981
+ def tearDown(267,12083
+ def test_get_base_model(270,12188
+
+tests/ping.py,387
+class DirectoryPingerTestCase(13,318
+ def setUp(15,400
+ def test_ping_gbobject(24,781
+class ExternalUrlsPingerTestCase(31,998
+ def setUp(34,1087
+ def test_is_external_url(42,1411
+ def test_find_external_urls(57,2172
+ def test_find_pingback_href(71,2829
+ def fake_urlopen(89,3551
+ def test_find_pingback_urls(105,4301
+ def test_pingback_url(120,4944
+
+tests/sitemaps.py,274
+class ObjectappSitemapsTestCase(18,537
+ def setUp(22,666
+ def test_gbobject_sitemap(43,1722
+ def test_Objecttype_sitemap(49,1962
+ def test_author_sitemap(58,2390
+ def test_tag_sitemap(68,2851
+ def test_Objecttype_sitemap_zero_division_error(77,3284
+
+tests/pingback.py,251
+class PingBackTestCase(24,738
+ def fake_urlopen(28,842
+ def setUp(39,1281
+ def tearDown(85,3648
+ def test_generate_pingback_content(90,3855
+ def test_pingback_ping(113,4909
+ def test_pingback_extensions_get_pingbacks(166,7319
+
+tests/comparison.py,142
+class ComparisonTestCase(10,260
+ def test_pearson_score(13,339
+ def test_clustered_model(21,722
+ def test_vector_builder(35,1514
+
+tests/templatetags.py,955
+class TemplateTagsTestCase(40,1849
+ def setUp(43,1927
+ def publish_gbobject(51,2235
+ def test_get_objecttypes(57,2436
+ def test_get_authors(67,2914
+ def test_get_recent_gbobjects(80,3496
+ def test_get_featured_gbobjects(93,4072
+ def test_get_random_gbobjects(106,4658
+ def test_get_popular_gbobjects(119,5234
+ def test_get_similar_gbobjects(152,6853
+ def test_get_archives_gbobjects(176,7972
+ def test_get_archives_tree(199,9028
+ def test_get_calendar_gbobjects(222,10096
+ def test_get_recent_comments(268,12343
+ def test_get_recent_linkbacks(296,13612
+ def test_objectapp_pagination(324,14902
+ class FakeRequest(325,14943
+ def __init__(326,14978
+ def test_objectapp_breadcrumbs(411,19121
+ class FakeRequest(412,19163
+ def __init__(413,19198
+ def test_get_gravatar(486,22745
+ def test_get_tags(496,23187
+ def test_get_tag_cloud(519,23988
+
+managers.py,310
+def tags_published(12,171
+class AuthorPublishedManager(22,555
+ def get_query_set(25,650
+def gbobjects_published(36,1045
+class GbobjectPublishedManager(45,1366
+ def get_query_set(48,1465
+ def on_site(53,1639
+ def search(58,1843
+ def advanced_search(65,2050
+ def basic_search(70,2227
+
+search.py,70
+def createQ(23,634
+def unionQ(81,2728
+def advanced_search(129,4198
+
+views/quick_gbobject.py,64
+class QuickGbobjectForm(18,542
+def view_quick_gbobject(27,839
+
+views/authors.py,26
+def author_detail(14,425
+
+views/trackback.py,31
+def gbobject_trackback(14,427
+
+views/search.py,27
+def gbobject_search(9,234
+
+views/gbobjects.py,31
+def gbobject_shortlink(26,895
+
+views/tags.py,44
+def tag_list(15,418
+def tag_detail(24,819
+
+views/objecttypes.py,64
+def get_Objecttype_or_404(10,311
+def Objecttype_detail(16,499
+
+views/sitemap.py,19
+def sitemap(8,174
+
+views/channels.py,28
+def gbobject_channel(7,133
+
+views/decorators.py,187
+def update_queryset(15,493
+ def wrapper(23,813
+def password(33,1050
+def protect_gbobject(47,1623
+ def wrapper(53,1839
+def template_name_for_gbobject_queryset_filtered(72,2640
+
+moderator.py,194
+class GbobjectCommentModerator(23,910
+ def email(34,1388
+ def do_email_notification(50,2108
+ def do_email_authors(67,2993
+ def do_email_reply(89,4151
+ def moderate(116,5607
+
+admin/process.py,802
+class ProcessAdmin(24,773
+ def __init__(76,3621
+ def get_title(81,3790
+ def get_authors(92,4279
+ def get_objecttypes(105,4852
+ def get_tags(118,5452
+ def get_sites(130,5961
+ def get_comments_are_open(138,6281
+ def get_is_actual(144,6535
+ def get_is_visible(150,6743
+ def get_link(156,6957
+ def get_short_url(163,7233
+ def save_model(174,7625
+ def queryset(188,8235
+ def formfield_for_manytomany(195,8519
+ def get_actions(206,9018
+ def make_mine(220,9549
+ def make_published(229,9944
+ def make_hidden(237,10337
+ def make_tweet(244,10641
+ def close_comments(260,11391
+ def close_pingbacks(268,11778
+ def put_on_top(276,12136
+ def ping_directories(285,12582
+ def get_urls(308,13643
+ def _media(326,14564
+
+admin/system.py,801
+class SystemAdmin(24,772
+ def __init__(76,3646
+ def get_title(81,3814
+ def get_authors(92,4299
+ def get_objecttypes(105,4869
+ def get_tags(118,5466
+ def get_sites(130,5972
+ def get_comments_are_open(138,6290
+ def get_is_actual(144,6541
+ def get_is_visible(150,6746
+ def get_link(156,6957
+ def get_short_url(163,7230
+ def save_model(174,7620
+ def queryset(188,8222
+ def formfield_for_manytomany(195,8505
+ def get_actions(206,9003
+ def make_mine(220,9533
+ def make_published(229,9919
+ def make_hidden(237,10306
+ def make_tweet(244,10610
+ def close_comments(260,11355
+ def close_pingbacks(268,11735
+ def put_on_top(276,12085
+ def ping_directories(285,12525
+ def get_urls(308,13579
+ def _media(326,14491
+
+admin/forms.py,327
+class ProcessAdminForm(28,954
+ class Meta:Meta30,996
+class SystemAdminForm(34,1074
+ class Meta:Meta36,1115
+class ObjecttypeAdminForm(42,1194
+ def __init__(49,1465
+ def clean_parent(55,1749
+ class Meta:Meta63,2035
+class GbobjectAdminForm(68,2121
+ def __init__(77,2472
+ class Meta:Meta87,2836
+
+admin/widgets.py,462
+class TreeNodeChoiceField(13,346
+ def __init__(16,542
+ def label_from_instance(22,836
+class MPTTModelChoiceIterator(29,1114
+ def choice(31,1225
+class MPTTModelMultipleChoiceField(39,1551
+ def __init__(41,1670
+ def label_from_instance(45,1859
+ def _get_choices(51,2136
+class MPTTFilteredSelectMultiple(60,2390
+ def __init__(62,2505
+ def render_options(66,2692
+ def render_option(74,3076
+ class Media:Media103,4437
+
+admin/gbobject.py,801
+class GbobjectAdmin(24,774
+ def __init__(64,3043
+ def get_title(69,3213
+ def get_authors(80,3706
+ def get_objecttypes(93,4282
+ def get_tags(106,4885
+ def get_sites(118,5397
+ def get_comments_are_open(126,5719
+ def get_is_actual(132,5976
+ def get_is_visible(138,6187
+ def get_link(144,6404
+ def get_short_url(151,6683
+ def save_model(162,7077
+ def queryset(176,7695
+ def formfield_for_manytomany(183,7982
+ def get_actions(194,8482
+ def make_mine(208,9014
+ def make_published(217,9412
+ def make_hidden(225,9805
+ def make_tweet(232,10115
+ def close_comments(248,10870
+ def close_pingbacks(256,11256
+ def put_on_top(264,11612
+ def ping_directories(273,12058
+ def get_urls(296,13118
+ def _media(314,14048
+
+url_shortener/__init__.py,30
+def get_url_shortener(11,292
+
+url_shortener/backends/bitly.py,20
+def backend(17,513
+
+url_shortener/backends/default.py,19
+def backend(8,183
+
+templatetags/objectapp_tags.py,713
+def get_objecttypes(40,1325
+def get_this_nodes_uri(46,1529
+def get_authors(52,1674
+def get_recent_gbobjects(59,1906
+def get_featured_gbobjects(66,2183
+def get_random_gbobjects(74,2502
+def get_popular_gbobjects(84,2867
+def get_similar_gbobjects(109,3829
+ def compute_related(120,4183
+def get_archives_gbobjects(152,5322
+def get_archives_gbobjects_tree(160,5662
+def get_calendar_gbobjects(169,6044
+def get_recent_comments(198,7130
+def get_recent_linkbacks(215,7887
+def objectapp_pagination(234,8628
+def objectapp_breadcrumbs(273,10381
+def get_gravatar(287,10945
+class TagsNode(299,11316
+ def __init__(300,11338
+ def render(303,11415
+def get_tags(309,11533
+def get_tag_cloud(323,11945
+
+templatetags/objectapp_admin_tags.py,62
+def get_draft_gbobjects(16,460
+def get_content_stats(24,764
+
+templatetags/zbreadcrumbs.py,147
+class Crumb(9,190
+ def __init__(11,245
+def year_crumb(16,335
+def month_crumb(23,546
+def day_crumb(32,879
+def retrieve_breadcrumbs(62,2278
+
+templatetags/zcalendar.py,176
+class ObjectappCalendar(15,379
+ def __init__(18,454
+ def formatday(24,683
+ def formatmonth(39,1467
+ def formatweekday(52,2067
+ def formatmonthname(58,2312
+
+migrations/0001_initial.py,76
+class Migration(7,125
+ def forwards(9,160
+ def backwards(174,10519
+
+templates/objectapp/password.html,72
+{% trans "Password required" %}7,137
+{% endblock %}id_password34,820
+
+templates/objectapp/opensearch.xml,97
+<?xml version=1,0
+<OpenSearchDescription xmlns=4,97
+ <Url type=11,502
+ <Query role=15,1047
+
+templates/objectapp/tags/slider_gbobjects.html,170
+{% for gbobject in gbobjects %}slider4,64
+{{ gbobject.title }}nav-slider-{{ forloop.counter }}9,394
+{{ gbobject.title }}gbobject-slider-{{ forloop.counter }}20,734
+
+templates/objectapp/_gbobject_detail.html,50
+{{ object.title }}gbobject-{{ object.pk }}6,315
+
+templates/objectapp/gbobject_trackback.xml,19
+<?xml version=1,0
+
+templates/objectapp/skeleton.html,319
+Gnowledge Studio - {% block title %}{% endblock %}75,4675
+{% include "gstudio/_header.html" %}gstudio80,4806
+/ {% block breadcrumbs %}{% endblock %}breadcrumbs84,4896
+{% block content %}content89,5052
+The content block need to be overrided!90,5075
+{% block sidebar %}sidebar94,5193
+Powered by footer99,5300
+
+templates/objectapp/sitemap.html,187
+{% trans "Sitemap" %}7,152
+{% trans "Gbobjects per objecttypes" %}10,221
+{% trans "All the gbobjects" %}31,847
+{% trans "Objecttypes" %}50,1420
+{% trans "Monthly archives" %}67,2087
+
+templates/objectapp/objecttype_list.html,36
+{% trans "Objecttype list" %}9,214
+
+templates/objectapp/gbobject_list.html,169
+{{ Objecttype.title }}23,1597
+{% trans "Tag" %} : {{ tag }}30,1750
+{% blocktrans with author.username as author %}Gbobjects by {{ author }}{% endblocktrans %}34,1818
+
+templates/objectapp/gbobject_archive_year.html,78
+{% trans "Archives" %} {{ year }}9,225
+{% trans "Monthly archives" %}13,288
+
+templates/objectapp/tag_list.html,29
+{% trans "Tag list" %}9,190
+
+templates/objectapp/rsd.xml,42
+<?xml version=1,0
+ <api name=8,336
+
+templates/objectapp/wlwmanifest.xml,34
+{% load i18n %}<?xml version=1,0
+
+templates/objectapp/wxr.xml,142
+<?xml version=1,0
+<!-- generator=8,421
+ <Objecttype domain=44,2185
+ <Objecttype domain=46,2390
+ <guid isPermaLink=48,2496
+
+templates/objectapp/login.html,35
+{% trans "Login required" %}7,134
+
+templates/objectapp/base.html,414
+{% trans "Search" %}23,956
+{% trans "Objecttypes" %}searchbox35,1611
+{% trans "Authors" %}39,1701
+{% trans "Calendar" %}43,1784
+{% trans "Tags" %}47,1875
+{% trans "Recent gbobjects" %}51,1956
+{% trans "Recent comments" %}55,2059
+{% trans "Recent linkbacks" %}59,2161
+{% trans "Random gbobjects" %}63,2263
+{% trans "Popular gbobjects" %}67,2366
+{% trans "Archives" %}71,2471
+{% trans "Tools" %}76,2599
+
+templates/objectapp/gbobject_search.html,128
+{% trans "Search results for" %} {% if pattern %}'{{ pattern }}'{% endif %}14,814
+{% if is_paginated %}main_searchbox46,2028
+
+templates/objectapp/gbobject_archive_day.html,58
+{% trans "Archives" %} {{ day|date:"DATE_FORMAT" }}9,261
+
+templates/objectapp/_header.html,81
+&nbsp;header4,44
+{% firstof user.first_name user.username %}user-tools11,413
+
+templates/objectapp/author_list.html,32
+{% trans "Author list" %}9,196
+
+templates/objectapp/gbobject_detail.html,640
+{% trans "Next gbobject" %}45,2337
+{% trans "Previous gbobject" %}59,2710
+{% trans "Related gbobjects" %}72,3046
+{% trans "Similar gbobjects" %}83,3373
+{% trans "Comments" %}comments91,3555
+{% for comment in comment_list %}comment-list95,3710
+{% if comment.url %}comment_{{ comment.pk }}102,4146
+{% trans "Pingbacks" %}pingbacks132,4897
+{% for pingback in pingback_list %}pingback-list136,5057
+{{ pingback.user_name }}pingback_{{ pingback.pk }}140,5281
+{% trans "Trackbacks" %}trackbacks166,5947
+{% for trackback in trackback_list %}trackback-list169,6063
+{{ trackback.user_name }}trackback_{{ trackback.pk }}173,6294
+
+templates/objectapp/gbobject_archive_month.html,102
+{% trans "Archives" %} {{ month|date:"YEAR_MONTH_FORMAT" }}9,277
+{% trans "Daily archives" %}13,366
+
+templates/comments/objectapp/gbobject/posted.html,44
+{% trans "Thanks for your comment" %}7,143
+
+templates/comments/objectapp_gbobject_preview.html,250
+{% blocktrans count form.errors|length as errors %}Please correct following error.{% plural %}Please correct following errors.{% endblocktrans %}9,189
+{% trans "Preview of the comment" %}12,360
+{% if form.cleaned_data.url %}comment_preview20,787
+
+templates/admin/objectapp/app_index.html,80
+{% include "admin/objectapp/widgets/content_stats.html" %}content-main28,1062
+
+templates/admin/objectapp/widgets/quickpost.html,110
+{% csrf_token %}quickpost4,124
+{% trans "Content" %}:id_title17,527
+{% trans "Tags" %}:id_content25,782
+
+templates/admin/objectapp/widgets/base.html,74
+{% block title %}{% endblock %}{% block module_id %}{% endblock %}3,116
+
+templates/500.html,167
+{% trans "Server error" %}11,217
+{% trans "Useful links" %}15,411
+{% trans "Recent gbobjects" %}31,781
+{% trans "Search" %}36,882
+{% endblock %}searchbox46,1228
+
+templates/404.html,169
+{% trans "Page not found" %}11,219
+{% trans "Useful links" %}15,330
+{% trans "Recent gbobjects" %}31,700
+{% trans "Search" %}36,801
+{% endblock %}searchbox46,1147
+
+TAGS,200
+fixtures/helloworld.json,json2,2
+ "login_required": false,3,31
+ "comment_enabled": true,4,70
+ "login_required": false,5,109
+ "comment_enabled": true,6,149
+feeds.py,py8,191
+
+spam_checker/__init__.py,55
+def get_spam_checker(10,211
+def check_is_spam(26,713
+
+spam_checker/backends/mollom.py,20
+def backend(22,707
+
+spam_checker/backends/all_is_spam.py,18
+def backend(4,56
+
+spam_checker/backends/automattic.py,20
+def backend(21,611
+
+spam_checker/backends/typepad.py,42
+class TypePad(21,611
+def backend(26,731
+
+static/objectapp/img/sitemap.png,41
+d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸U7,703
+
+static/objectapp/img/objecttype.png,94
+(Ë’öû=m6ò}_ÙàB¦Ó©Çø`fÀëÉܲê`·ÛÑd2¡0 ͇¸® BaøªRÇgÁMÓPQÊbE‘Ò‡T iÛÖ$iÛÖ5,253
+
+static/objectapp/img/wlw/watermark.png,608
+jTÐÄÙ¢0EP^HÁS´§©/Iýåih‘¾Á­.‘èA s¨!:JŠ¤»?Êpñ!Êñ{X,X4,899
+‚ä†<VÜV5,995
+3Y¨V·,¶é±ëDN5%FÒPÐnÄÆ‘<6d_¡ê+¬™PcM7S¯,ÿøt üÖ¿šú+ÿKÀ>jhª dCi÷”T(–i( Ó ÷:j" ÷¾~›ËožP.Zê½#9EŽ ZidhÈA!2/ÒgÊÜ OöˆQ0}¦Œ-Q„ª$L5’¬Q„ª$L5’10,1712
+®Û7GLç7 èƒpR49×è"˜vƒkö„’º8t0Z“ç†8u¤aIž–”©E¡°ÍSïH®&wï§1ßÏêèýØæú(Üî ⯕ïÌF~î¿,¼ö⣨õ)^¯È»cÆÍ !,!ytQå( D?¡t&ïÖäÝ ’ía“Ê VY\PF@ìñº“ qènS…ÂÆš’<q¿B©¹CJ²Û]!}‡žÇO}…ööOúÕ®» æÍuƒ®gtÎhèD c€:¡-ȸ$Ï)4”¹£‹ 0n­6‡YYš£‘k×w¬× Å^§ï+VjæîÿþÆwè—þRÏþ»HyðUYS r Bc‡r Bc15,3313
+·š1«‘Ø/‰ç5 ÍŠì)+L6H†’-)5Ž,-JÕ83â‡.j¼F% ¿èÑÑ"C‹‰ÍA5ªÚÓ#ä«¡\@fÃðfÃ21,4864
+
+static/objectapp/img/wlw/comments.png,84
+ÔV”VV”5,151
+ÔV”V”" Rh£òpš´MC";ib;5,151
+ÔV”V”" Rh£òpš´MC";ib;~Åkï®wg~Åkï®w5,151
+
+static/objectapp/img/plugin.png,992
+ìšìí9öaö%ömöÏÌÖ;t;|rtuÌvlp¼ë¤á4éĩÃéWgg¡só5¦KË—v—Sm§Š§nŸzË•åîºÒµÓõ£›»›Ü­ÙmÔÝÌ=Å}«ûM.›É]Ã=ïAôð÷XâqÌ㧛§Âóç/^v^Y^û½O³œ&žÖ0mÈÛÄ[à½Ë{`:>=eúÎé>Æ>ŸzŸ‡¾¦¾"ß=¾#~Ö~™~üžû;úËýø¿áyòñN`Áå½³k™¥5»/ >B Yr“oÀòùc3Üg,šÑÊZú0Ì&LÖŽ†Ïß~o¦ùLé̶ˆàGlˆ¸i™ù})*2ª.êQ´StqtQ´Stq12,1526
+ìšìí9öaö%ömöÏÌÖ;t;|rtuÌvlp¼ë¤á4éĩÃéWgg¡só5¦KË—v—Sm§Š§nŸzË•åîºÒµÓõ£›»›Ü­ÙmÔÝÌ=Å}«ûM.›É]Ã=ïAôð÷XâqÌ㧛§Âóç/^v^Y^û½O³œ&žÖ0mÈÛÄ[à½Ë{`:>=eúÎé>Æ>ŸzŸ‡¾¦¾"ß=¾#~Ö~™~üžû;úËýø¿áyòñN`Áå½³k™¥5»/ >B Yr“oÀòùc3Üg,šÑÊZú0Ì&LÖŽ†Ïß~o¦ùLé̶ˆàGlˆ¸i™ù})*2ª.êQ´Stqt÷,Ö¬äYûgYû12,1526
+ìšìí9öaö%ömöÏÌÖ;t;|rtuÌvlp¼ë¤á4éĩÃéWgg¡só5¦KË—v—Sm§Š§nŸzË•åîºÒµÓõ£›»›Ü­ÙmÔÝÌ=Å}«ûM.›É]Ã=ïAôð÷XâqÌ㧛§Âóç/^v^Y^û½O³œ&žÖ0mÈÛÄ[à½Ë{`:>=eúÎé>Æ>ŸzŸ‡¾¦¾"ß=¾#~Ö~™~üžû;úËýø¿áyòñN`Áå½³k™¥5»/ >B Yr“oÀòùc3Üg,šÑÊZú0Ì&LÖŽ†Ïß~o¦ùLé̶ˆàGlˆ¸i™ù})*2ª.êQ´Stqt÷,Ö¬äYûg½Žñ©Œ¹;Ûj¶rvg¬jlRlc웸j¶rvg¬jlRlcì›12,1526
+
+static/objectapp/css/src/_base.sass,40
+$blueprint_font_family: unquote(13,424
+
+static/objectapp/css/src/screen.sass,106
+ul,13,265
+ h1,49,907
+ h1, h2,49,907
+ ul,62,1087
+ li ul,65,1165
+ +tag-cloud(cloud84,1495
+
+static/objectapp/css/src/print.sass,179
+ a,10,125
+ a, a:link,link10,125
+ a, a:link, a:visited,visited10,125
+ a, a:link, a:visited, a:focus,focus10,125
+ a, a:link, a:visited, a:focus, a:active,active10,125
+
+static/objectapp/css/src/dashboard_objectapp.sass,41
+ th,11,117
+ background: url(15,163
+
+static/objectapp/css/print.css,354
+h1,8,330
+h1, h2,8,330
+h1, h2, h3,8,330
+h1, h2, h3, h4,8,330
+h1, h2, h3, h4, h5,8,330
+a:link,link13,528
+body a,28,874
+body a, body a:link,link28,874
+body a, body a:link, body a:visited,visited28,874
+body a, body a:link, body a:visited, body a:focus,focus28,874
+body a, body a:link, body a:visited, body a:focus, body a:active,active28,874
+
+static/objectapp/css/config.rb,19
+project_type 2,47
+
+static/objectapp/css/ie.css,32
+fieldset,19,419
+legend,21,464
+
+static/objectapp/css/screen.css,14875
+h1,3,103
+h1, h2,3,103
+h1, h2, h3,3,103
+h1, h2, h3, h4,3,103
+h1, h2, h3, h4, h5,3,103
+h1 img,4,167
+h1 img, h2 img,4,167
+h1 img, h2 img, h3 img,4,167
+h1 img, h2 img, h3 img, h4 img,4,167
+h1 img, h2 img, h3 img, h4 img, h5 img,4,167
+strong,30,1009
+em,32,1045
+sup,34,1078
+abbr,36,1108
+pre,44,1283
+pre, code,44,1283
+li ul,46,1374
+ul,48,1403
+th,65,1720
+th, td,65,1720
+table.striped tr:nth-child(child67,1768
+.column,column97,2475
+.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 { display: inline;97,2475
+.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 { display: inline; float: left;97,2475
+.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 { display: inline; float: left; margin-right: 10px;px97,2475
+* html .column,column98,2768
+* html .column, * html .span-1, * html .span-2, * html .span-3, * html .span-4, * html .span-5, * html .span-6, * html .span-7, * html .span-8, * html .span-9, * html .span-10, * html .span-11, * html .span-12, * html .span-13, * html .span-14, * html .span-15, * html .span-16, * html .span-17, * html .span-18, * html .span-19, * html .span-20, * html .span-21, * html .span-22, * html .span-23, * html .span-24 { overflow-x: hidden;98,2768
+.span-1 { width: 30px;px102,3235
+.span-2 { width: 70px;px104,3261
+.span-3 { width: 110px;px106,3287
+.span-4 { width: 150px;px108,3314
+.span-5 { width: 190px;px110,3341
+.span-6 { width: 230px;px112,3368
+.span-7 { width: 270px;px114,3395
+.span-8 { width: 310px;px116,3422
+.span-9 { width: 350px;px118,3449
+.span-10 { width: 390px;px120,3476
+.span-11 { width: 430px;px122,3504
+.span-12 { width: 470px;px124,3532
+.span-13 { width: 510px;px126,3560
+.span-14 { width: 550px;px128,3588
+.span-15 { width: 590px;px130,3616
+.span-16 { width: 630px;px132,3644
+.span-17 { width: 670px;px134,3672
+.span-18 { width: 710px;px136,3700
+.span-19 { width: 750px;px138,3728
+.span-20 { width: 790px;px140,3756
+.span-21 { width: 830px;px142,3784
+.span-22 { width: 870px;px144,3812
+.span-23 { width: 910px;px146,3840
+.span-24 { width: 950px;px148,3868
+.span-24 { width: 950px; margin:margin148,3868
+input.span-1, textarea.span-1, select.span-1 { width: 30px;px150,3907
+input.span-2, textarea.span-2, select.span-2 { width: 70px;px151,3969
+input.span-3, textarea.span-3, select.span-3 { width: 110px;px152,4031
+input.span-4, textarea.span-4, select.span-4 { width: 150px;px153,4094
+input.span-5, textarea.span-5, select.span-5 { width: 190px;px154,4157
+input.span-6, textarea.span-6, select.span-6 { width: 230px;px155,4220
+input.span-7, textarea.span-7, select.span-7 { width: 270px;px156,4283
+input.span-8, textarea.span-8, select.span-8 { width: 310px;px157,4346
+input.span-9, textarea.span-9, select.span-9 { width: 350px;px158,4409
+input.span-10, textarea.span-10, select.span-10 { width: 390px;px159,4472
+input.span-11, textarea.span-11, select.span-11 { width: 430px;px160,4538
+input.span-12, textarea.span-12, select.span-12 { width: 470px;px161,4604
+input.span-13, textarea.span-13, select.span-13 { width: 510px;px162,4670
+input.span-14, textarea.span-14, select.span-14 { width: 550px;px163,4736
+input.span-15, textarea.span-15, select.span-15 { width: 590px;px164,4802
+input.span-16, textarea.span-16, select.span-16 { width: 630px;px165,4868
+input.span-17, textarea.span-17, select.span-17 { width: 670px;px166,4934
+input.span-18, textarea.span-18, select.span-18 { width: 710px;px167,5000
+input.span-19, textarea.span-19, select.span-19 { width: 750px;px168,5066
+input.span-20, textarea.span-20, select.span-20 { width: 790px;px169,5132
+input.span-21, textarea.span-21, select.span-21 { width: 830px;px170,5198
+input.span-22, textarea.span-22, select.span-22 { width: 870px;px171,5264
+input.span-23, textarea.span-23, select.span-23 { width: 910px;px172,5330
+input.span-24, textarea.span-24, select.span-24 { width: 950px;px173,5396
+.append-1 { padding-right: 40px;px175,5463
+.append-2 { padding-right: 80px;px177,5499
+.append-3 { padding-right: 120px;px179,5535
+.append-4 { padding-right: 160px;px181,5572
+.append-5 { padding-right: 200px;px183,5609
+.append-6 { padding-right: 240px;px185,5646
+.append-7 { padding-right: 280px;px187,5683
+.append-8 { padding-right: 320px;px189,5720
+.append-9 { padding-right: 360px;px191,5757
+.append-10 { padding-right: 400px;px193,5794
+.append-11 { padding-right: 440px;px195,5832
+.append-12 { padding-right: 480px;px197,5870
+.append-13 { padding-right: 520px;px199,5908
+.append-14 { padding-right: 560px;px201,5946
+.append-15 { padding-right: 600px;px203,5984
+.append-16 { padding-right: 640px;px205,6022
+.append-17 { padding-right: 680px;px207,6060
+.append-18 { padding-right: 720px;px209,6098
+.append-19 { padding-right: 760px;px211,6136
+.append-20 { padding-right: 800px;px213,6174
+.append-21 { padding-right: 840px;px215,6212
+.append-22 { padding-right: 880px;px217,6250
+.append-23 { padding-right: 920px;px219,6288
+.prepend-1 { padding-left: 40px;px221,6326
+.prepend-2 { padding-left: 80px;px223,6362
+.prepend-3 { padding-left: 120px;px225,6398
+.prepend-4 { padding-left: 160px;px227,6435
+.prepend-5 { padding-left: 200px;px229,6472
+.prepend-6 { padding-left: 240px;px231,6509
+.prepend-7 { padding-left: 280px;px233,6546
+.prepend-8 { padding-left: 320px;px235,6583
+.prepend-9 { padding-left: 360px;px237,6620
+.prepend-10 { padding-left: 400px;px239,6657
+.prepend-11 { padding-left: 440px;px241,6695
+.prepend-12 { padding-left: 480px;px243,6733
+.prepend-13 { padding-left: 520px;px245,6771
+.prepend-14 { padding-left: 560px;px247,6809
+.prepend-15 { padding-left: 600px;px249,6847
+.prepend-16 { padding-left: 640px;px251,6885
+.prepend-17 { padding-left: 680px;px253,6923
+.prepend-18 { padding-left: 720px;px255,6961
+.prepend-19 { padding-left: 760px;px257,6999
+.prepend-20 { padding-left: 800px;px259,7037
+.prepend-21 { padding-left: 840px;px261,7075
+.prepend-22 { padding-left: 880px;px263,7113
+.prepend-23 { padding-left: 920px;px265,7151
+.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 { display: inline;267,7189
+.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 { display: inline; float: left;267,7189
+.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 { display: inline; float: left; position: relative;267,7189
+.pull-1 { margin-left: -40px;px269,7474
+.pull-2 { margin-left: -80px;px271,7507
+.pull-3 { margin-left: -120px;px273,7540
+.pull-4 { margin-left: -160px;px275,7574
+.pull-5 { margin-left: -200px;px277,7608
+.pull-6 { margin-left: -240px;px279,7642
+.pull-7 { margin-left: -280px;px281,7676
+.pull-8 { margin-left: -320px;px283,7710
+.pull-9 { margin-left: -360px;px285,7744
+.pull-10 { margin-left: -400px;px287,7778
+.pull-11 { margin-left: -440px;px289,7813
+.pull-12 { margin-left: -480px;px291,7848
+.pull-13 { margin-left: -520px;px293,7883
+.pull-14 { margin-left: -560px;px295,7918
+.pull-15 { margin-left: -600px;px297,7953
+.pull-16 { margin-left: -640px;px299,7988
+.pull-17 { margin-left: -680px;px301,8023
+.pull-18 { margin-left: -720px;px303,8058
+.pull-19 { margin-left: -760px;px305,8093
+.pull-20 { margin-left: -800px;px307,8128
+.pull-21 { margin-left: -840px;px309,8163
+.pull-22 { margin-left: -880px;px311,8198
+.pull-23 { margin-left: -920px;px313,8233
+.pull-24 { margin-left: -960px;px315,8268
+.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 { display: inline;317,8303
+.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 { display: inline; float: left;317,8303
+.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 { display: inline; float: left; position: relative;317,8303
+.push-1 { margin: 0 -40px 1.5em 40px;px319,8588
+.push-2 { margin: 0 -80px 1.5em 80px;px321,8629
+.push-3 { margin: 0 -120px 1.5em 120px;px323,8670
+.push-4 { margin: 0 -160px 1.5em 160px;px325,8713
+.push-5 { margin: 0 -200px 1.5em 200px;px327,8756
+.push-6 { margin: 0 -240px 1.5em 240px;px329,8799
+.push-7 { margin: 0 -280px 1.5em 280px;px331,8842
+.push-8 { margin: 0 -320px 1.5em 320px;px333,8885
+.push-9 { margin: 0 -360px 1.5em 360px;px335,8928
+.push-10 { margin: 0 -400px 1.5em 400px;px337,8971
+.push-11 { margin: 0 -440px 1.5em 440px;px339,9015
+.push-12 { margin: 0 -480px 1.5em 480px;px341,9059
+.push-13 { margin: 0 -520px 1.5em 520px;px343,9103
+.push-14 { margin: 0 -560px 1.5em 560px;px345,9147
+.push-15 { margin: 0 -600px 1.5em 600px;px347,9191
+.push-16 { margin: 0 -640px 1.5em 640px;px349,9235
+.push-17 { margin: 0 -680px 1.5em 680px;px351,9279
+.push-18 { margin: 0 -720px 1.5em 720px;px353,9323
+.push-19 { margin: 0 -760px 1.5em 760px;px355,9367
+.push-20 { margin: 0 -800px 1.5em 800px;px357,9411
+.push-21 { margin: 0 -840px 1.5em 840px;px359,9455
+.push-22 { margin: 0 -880px 1.5em 880px;px361,9499
+.push-23 { margin: 0 -920px 1.5em 920px;px363,9543
+.push-24 { margin: 0 -960px 1.5em 960px;px365,9587
+.feedback,feedback373,9773
+.feedback, .error,error373,9773
+.feedback, .error, .alert,alert373,9773
+.feedback, .error, .alert, .notice,notice373,9773
+.feedback, .error, .alert, .notice, .success,success373,9773
+.error,error375,9893
+.error a,376,9972
+input.text,text401,10607
+input.text, input.title,title401,10607
+input.text, input.title, input[input401,10607
+input.text, input.title, input[type=email], input[input401,10607
+input.text,text410,10900
+input.text, input.title,title410,10900
+input.text, input.title, input[input410,10900
+input.text, input.title, input[type=email], input[input410,10900
+input.text, input.title, input[type=email], input[type=text], input[input410,10900
+input.text:focus,focus411,11048
+input.text:focus, input.title:focus,focus411,11048
+input.text:focus, input.title:focus, input[type=email]:focus,focus411,11048
+input.text:focus, input.title:focus, input[type=email]:focus, input[type=text]:focus,focus411,11048
+input.text:focus, input.title:focus, input[type=email]:focus, input[type=text]:focus, input[type=password]:focus,focus411,11048
+input.text,text415,11285
+input.text, input.title,title415,11285
+input.text, input.title, input[input415,11285
+input.text, input.title, input[type=email], input[input415,11285
+ul,421,11517
+div.paginator span.page:hover,hover470,14146
+div.paginator span.page:hover, div.paginator span.next:hover,hover470,14146
+div.paginator span.page:hover a,471,14273
+div.paginator span.page:hover a, div.paginator span.next:hover a,471,14273
+.hgbobject .gbobject-content pre,488,15666
+.gbobject-widgets ul,494,16239
+ol#comment-list,list498,16409
+ol#comment-list, ol#pingback-list,list498,16409
+ol#comment-list li,499,16516
+ol#comment-list li, ol#pingback-list li,499,16516
+ol#comment-list li.box2,box2500,16629
+ol#comment-list li.box2, ol#pingback-list li.box2,box2500,16629
+ol#comment-list li.post-author,author501,16760
+ol#comment-list li.post-author, ol#pingback-list li.post-author,author501,16760
+ol#comment-list li p,502,16878
+ol#comment-list li p, ol#pingback-list li p,502,16878
+ol#comment-list li img,503,16972
+ol#comment-list li img, ol#pingback-list li img,503,16972
+ol#comment-list li .pingback-body,body504,17064
+ol#comment-list li .pingback-body, ol#comment-list li .trackback-body,body504,17064
+ol#comment-list li .pingback-body, ol#comment-list li .trackback-body, ol#pingback-list li .pingback-body,body504,17064
+ol#comment-list li .pingback-body, ol#comment-list li .trackback-body, ol#pingback-list li .pingback-body, ol#pingback-list li .trackback-body,body504,17064
+ol#comment-list li .pingback-body, ol#comment-list li .trackback-body, ol#pingback-list li .pingback-body, ol#pingback-list li .trackback-body, ol#trackback-list li .pingback-body,body504,17064
+ol#comment-list li .comment-author,author505,17300
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author,author505,17300
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author, ol#comment-list li .trackback-author,author505,17300
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author, ol#comment-list li .trackback-author, ol#pingback-list li .comment-author,author505,17300
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author, ol#comment-list li .trackback-author, ol#pingback-list li .comment-author, ol#pingback-list li .pingback-author,author505,17300
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author, ol#comment-list li .trackback-author, ol#pingback-list li .comment-author, ol#pingback-list li .pingback-author, ol#pingback-list li .trackback-author,author505,17300
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author, ol#comment-list li .trackback-author, ol#pingback-list li .comment-author, ol#pingback-list li .pingback-author, ol#pingback-list li .trackback-author, ol#trackback-list li .comment-author,author505,17300
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author, ol#comment-list li .trackback-author, ol#pingback-list li .comment-author, ol#pingback-list li .pingback-author, ol#pingback-list li .trackback-author, ol#trackback-list li .comment-author, ol#trackback-list li .pingback-author,author505,17300
+
+static/objectapp/css/dashboard_objectapp.css,147
+.module table th,5,117
+.managelink managelink7,174
+input,11,400
+input, textarea,11,400
+.vTextField,vTextField13,456
+.required label,15,504
+
+static/objectapp/js/wymeditor/iframe/default/wymiframe.html,24
+WYMeditor iframe21,646
+
+static/objectapp/js/wymeditor/iframe/default/lbl-p.png,992
+ìšìí9öaö%ömöÏÌÖ;t;|rtuÌvlp¼ë¤á4éĩÃéWgg¡só5¦KË—v—Sm§Š§nŸzË•åîºÒµÓõ£›»›Ü­ÙmÔÝÌ=Å}«ûM.›É]Ã=ïAôð÷XâqÌ㧛§Âóç/^v^Y^û½O³œ&žÖ0mÈÛÄ[à½Ë{`:>=eúÎé>Æ>ŸzŸ‡¾¦¾"ß=¾#~Ö~™~üžû;úËýø¿áyòñN`Áå½³k™¥5»/ >B Yr“oÀòùc3Üg,šÑÊZú0Ì&LÖŽ†Ïß~o¦ùLé̶ˆàGlˆ¸i™ù})*2ª.êQ´StqtQ´Stq12,1547
+ìšìí9öaö%ömöÏÌÖ;t;|rtuÌvlp¼ë¤á4éĩÃéWgg¡só5¦KË—v—Sm§Š§nŸzË•åîºÒµÓõ£›»›Ü­ÙmÔÝÌ=Å}«ûM.›É]Ã=ïAôð÷XâqÌ㧛§Âóç/^v^Y^û½O³œ&žÖ0mÈÛÄ[à½Ë{`:>=eúÎé>Æ>ŸzŸ‡¾¦¾"ß=¾#~Ö~™~üžû;úËýø¿áyòñN`Áå½³k™¥5»/ >B Yr“oÀòùc3Üg,šÑÊZú0Ì&LÖŽ†Ïß~o¦ùLé̶ˆàGlˆ¸i™ù})*2ª.êQ´Stqt÷,Ö¬äYûgYû12,1547
+ìšìí9öaö%ömöÏÌÖ;t;|rtuÌvlp¼ë¤á4éĩÃéWgg¡só5¦KË—v—Sm§Š§nŸzË•åîºÒµÓõ£›»›Ü­ÙmÔÝÌ=Å}«ûM.›É]Ã=ïAôð÷XâqÌ㧛§Âóç/^v^Y^û½O³œ&žÖ0mÈÛÄ[à½Ë{`:>=eúÎé>Æ>ŸzŸ‡¾¦¾"ß=¾#~Ö~™~üžû;úËýø¿áyòñN`Áå½³k™¥5»/ >B Yr“oÀòùc3Üg,šÑÊZú0Ì&LÖŽ†Ïß~o¦ùLé̶ˆàGlˆ¸i™ù})*2ª.êQ´Stqt÷,Ö¬äYûg½Žñ©Œ¹;Ûj¶rvg¬jlRlc웸j¶rvg¬jlRlcì›12,1547
+
+static/objectapp/js/wymeditor/iframe/default/wymiframe.css,3744
+ p,26,671
+ h1,27,676
+ h2,28,682
+ h3,29,688
+ h4,30,694
+ h5,31,700
+ h6,32,706
+ ul,33,712
+ ol,34,718
+ table,35,724
+ blockquote,36,733
+ ul,42,958
+ h1 h1,77,2463
+ h1 h1, h1 h2,77,2463
+ h1 h1, h1 h2, h1 h3,77,2463
+ h1 h1, h1 h2, h1 h3, h1 h4,77,2463
+ h1 h1, h1 h2, h1 h3, h1 h4, h1 h5,77,2463
+ h1 h1, h1 h2, h1 h3, h1 h4, h1 h5, h1 h6,77,2463
+ h1 h1, h1 h2, h1 h3, h1 h4, h1 h5, h1 h6, h1 p,77,2463
+ h1 h1, h1 h2, h1 h3, h1 h4, h1 h5, h1 h6, h1 p, h1 pre,77,2463
+ h1 h1, h1 h2, h1 h3, h1 h4, h1 h5, h1 h6, h1 p, h1 pre, h1 address,77,2463
+ h2 h1,78,2533
+ h2 h1, h2 h2,78,2533
+ h2 h1, h2 h2, h2 h3,78,2533
+ h2 h1, h2 h2, h2 h3, h2 h4,78,2533
+ h2 h1, h2 h2, h2 h3, h2 h4, h2 h5,78,2533
+ h2 h1, h2 h2, h2 h3, h2 h4, h2 h5, h2 h6,78,2533
+ h2 h1, h2 h2, h2 h3, h2 h4, h2 h5, h2 h6, h2 p,78,2533
+ h2 h1, h2 h2, h2 h3, h2 h4, h2 h5, h2 h6, h2 p, h2 pre,78,2533
+ h2 h1, h2 h2, h2 h3, h2 h4, h2 h5, h2 h6, h2 p, h2 pre, h2 address,78,2533
+ h3 h1,79,2603
+ h3 h1, h3 h2,79,2603
+ h3 h1, h3 h2, h3 h3,79,2603
+ h3 h1, h3 h2, h3 h3, h3 h4,79,2603
+ h3 h1, h3 h2, h3 h3, h3 h4, h3 h5,79,2603
+ h3 h1, h3 h2, h3 h3, h3 h4, h3 h5, h3 h6,79,2603
+ h3 h1, h3 h2, h3 h3, h3 h4, h3 h5, h3 h6, h3 p,79,2603
+ h3 h1, h3 h2, h3 h3, h3 h4, h3 h5, h3 h6, h3 p, h3 pre,79,2603
+ h3 h1, h3 h2, h3 h3, h3 h4, h3 h5, h3 h6, h3 p, h3 pre, h3 address,79,2603
+ h4 h1,80,2673
+ h4 h1, h4 h2,80,2673
+ h4 h1, h4 h2, h4 h3,80,2673
+ h4 h1, h4 h2, h4 h3, h4 h4,80,2673
+ h4 h1, h4 h2, h4 h3, h4 h4, h4 h5,80,2673
+ h4 h1, h4 h2, h4 h3, h4 h4, h4 h5, h4 h6,80,2673
+ h4 h1, h4 h2, h4 h3, h4 h4, h4 h5, h4 h6, h4 p,80,2673
+ h4 h1, h4 h2, h4 h3, h4 h4, h4 h5, h4 h6, h4 p, h4 pre,80,2673
+ h4 h1, h4 h2, h4 h3, h4 h4, h4 h5, h4 h6, h4 p, h4 pre, h4 address,80,2673
+ h5 h1,81,2743
+ h5 h1, h5 h2,81,2743
+ h5 h1, h5 h2, h5 h3,81,2743
+ h5 h1, h5 h2, h5 h3, h5 h4,81,2743
+ h5 h1, h5 h2, h5 h3, h5 h4, h5 h5,81,2743
+ h5 h1, h5 h2, h5 h3, h5 h4, h5 h5, h5 h6,81,2743
+ h5 h1, h5 h2, h5 h3, h5 h4, h5 h5, h5 h6, h5 p,81,2743
+ h5 h1, h5 h2, h5 h3, h5 h4, h5 h5, h5 h6, h5 p, h5 pre,81,2743
+ h5 h1, h5 h2, h5 h3, h5 h4, h5 h5, h5 h6, h5 p, h5 pre, h5 address,81,2743
+ h6 h1,82,2813
+ h6 h1, h6 h2,82,2813
+ h6 h1, h6 h2, h6 h3,82,2813
+ h6 h1, h6 h2, h6 h3, h6 h4,82,2813
+ h6 h1, h6 h2, h6 h3, h6 h4, h6 h4,82,2813
+ h6 h1, h6 h2, h6 h3, h6 h4, h6 h4, h6 h6,82,2813
+ h6 h1, h6 h2, h6 h3, h6 h4, h6 h4, h6 h6, h6 p,82,2813
+ h6 h1, h6 h2, h6 h3, h6 h4, h6 h4, h6 h6, h6 p, h6 pre,82,2813
+ h6 h1, h6 h2, h6 h3, h6 h4, h6 h4, h6 h6, h6 p, h6 pre, h6 address,82,2813
+ p h1,83,2883
+ p h1, p h2,83,2883
+ p h1, p h2, p h3,83,2883
+ p h1, p h2, p h3, p h4,83,2883
+ p h1, p h2, p h3, p h4, p h5,83,2883
+ p h1, p h2, p h3, p h4, p h5, p h6,83,2883
+ p h1, p h2, p h3, p h4, p h5, p h6, p pre,83,2883
+ p h1, p h2, p h3, p h4, p h5, p h6, p pre, p address,83,2883
+ pre h1,84,2939
+ pre h1, pre h2,84,2939
+ pre h1, pre h2, pre h3,84,2939
+ pre h1, pre h2, pre h3, pre h4,84,2939
+ pre h1, pre h2, pre h3, pre h4, pre h5,84,2939
+ pre h1, pre h2, pre h3, pre h4, pre h5, pre h6,84,2939
+ pre h1, pre h2, pre h3, pre h4, pre h5, pre h6, pre p,84,2939
+ pre h1, pre h2, pre h3, pre h4, pre h5, pre h6, pre p, pre pre,84,2939
+ pre h1, pre h2, pre h3, pre h4, pre h5, pre h6, pre p, pre pre, pre address,84,2939
+ address h1,85,3018
+ address h1, address h2,85,3018
+ address h1, address h2, address h3,85,3018
+ address h1, address h2, address h3, address h4,85,3018
+ address h1, address h2, address h3, address h4, address h5,85,3018
+ address h1, address h2, address h3, address h4, address h5, address h6,85,3018
+ address p,86,3092
+ address p, address pre,86,3092
+
+static/objectapp/js/wymeditor/skins/twopanels/icons.png,45
+]11,2788
+]ÉBXóhžhò‰Üõ.Õ3ϲ±›RR12,3024
+
+static/objectapp/js/wymeditor/skins/twopanels/skin.css,393
+ .wym_skin_twopanels p,21,599
+ .wym_skin_twopanels p, .wym_skin_twopanels h2,21,599
+ .wym_skin_twopanels p, .wym_skin_twopanels h2, .wym_skin_twopanels h3,21,599
+ .wym_skin_twopanels ul,22,678
+ .wym_skin_twopanels .wym_section h2,67,2688
+ .wym_skin_twopanels .wym_dropdown:hover ul,86,3967
+ .wym_skin_twopanels .wym_buttons li a 93,4443
+
+static/objectapp/js/wymeditor/skins/silver/README,21
+<script type=17,564
+
+static/objectapp/js/wymeditor/skins/silver/skin.css,446
+ .wym_skin_silver p,21,603
+ .wym_skin_silver p, .wym_skin_silver h2,21,603
+ .wym_skin_silver p, .wym_skin_silver h2, .wym_skin_silver h3,21,603
+ .wym_skin_silver ul,22,673
+ .wym_skin_silver .wym_section h2,67,2630
+ .wym_skin_silver .wym_dropdown:hover ul,87,4007
+ .wym_skin_silver .wym_buttons li a 96,4728
+.wym_skin_silver .wym_panel a,277,12569
+div.wym_dropdown a:hover,hover292,12903
+
+static/objectapp/js/wymeditor/skins/silver/COPYING,1403
+ Version 2,47
+ Copyright 4,95
+your programs,20,926
+ When we speak of free software,22,947
+ When we speak of free software, we are referring to freedom,22,947
+11 of the WIPO copyright treaty adopted on 20 December 183,9244
+ When you convey a covered work,187,9381
+the covered work,190,9592
+modification of the work as a means of enforcing,191,9663
+users, your or third parties' legal rights to forbid circumvention oftion of192,9732
+receive it,198,9929
+receive it, in any medium,198,9929
+appropriately publish on each copy an appropriate copyright notice;199,9992
+non-permissive terms added in accord with section 7 apply to the code;201,10118
+keep intact all notices of the absence of any warranty;202,10189
+ Appropriate Legal Notices; however,231,11578
+ Notices displayed by works containing it;370,19236
+ c) Prohibiting misrepresentation of the origin of that material,372,19286
+ reasonable ways as different from the original version;374,19425
+ material by anyone who conveys the material 383,19780
+modify it is void,411,21191
+ However,415,21356
+ the Free Software Foundation,639,33294
+ the Free Software Foundation, either version 3 of the License,639,33294
+ the Free Software Foundation, either version 3 of the License, or639,33294
+parts of the General Public License. Of course,661,34332
+might be different;662,34405
+
+static/objectapp/js/wymeditor/skins/silver/images/bg.selector.silver.gif,72
+J´¨Ñ£H“*zcÔ‰>iö”JÀ… iö”JÀ…2,620
+Œ HÁVP‚¬ Ž2hÁF*$! @(6,1500
+
+static/objectapp/js/wymeditor/skins/minimal/skin.css,270
+.wym_skin_minimal div.wym_tools h2,32,718
+.wym_skin_minimal div.wym_containers h2,33,754
+.wym_skin_minimal div.wym_area_top,wym_area_top48,1026
+.wym_skin_minimal div.wym_area_right,wym_area_right49,1062
+.wym_skin_minimal div.wym_containers,wym_containers50,1100
+
+static/objectapp/js/wymeditor/skins/minimal/images/bg.selector.silver.gif,72
+J´¨Ñ£H“*zcÔ‰>iö”JÀ… iö”JÀ…2,620
+Œ HÁVP‚¬ Ž2hÁF*$! @(6,1500
+
+static/objectapp/js/wymeditor/skins/default/icons.png,45
+]11,2788
+]ÉBXóhžhò‰Üõ.Õ3ϲ±›RR12,3024
+
+static/objectapp/js/wymeditor/skins/default/skin.css,373
+ .wym_skin_default p,20,575
+ .wym_skin_default p, .wym_skin_default h2,20,575
+ .wym_skin_default p, .wym_skin_default h2, .wym_skin_default h3,20,575
+ .wym_skin_default ul,21,648
+ .wym_skin_default .wym_section h2,66,2613
+ .wym_skin_default .wym_dropdown:hover ul,85,3868
+ .wym_skin_default .wym_buttons li a 92,4336
+
+static/objectapp/js/wymeditor/skins/wymeditor_icon.png,13
+DËD5,704
+
+static/objectapp/js/wymeditor/skins/django/icons.png,855
+B„5lB„5l5,564
+g ˜ig ˜8,1412
+¦4T•s•ž÷=2#·—ù|ÝZ{«õ¤&ªä.ò{‰À'K Üøµ#G³æTã\¥†#9;ôx¦´ÉE& V ¿Ùàõ~ðÊšxP‹í‚´ë·Ei¹ÏE¤üƒKvfûFQµ¸Ìm¤sþ—ŠªRP¸ì< Ý •-q†F8R‚–:C#ÓlÚ’ghR‚–<C#ÓlÚ’ghR‚–<C“´ä˜:¹%ÏФ¡ž¡9½fjAŸ.1¬ûq„g"ŠÄŽê?9‚1G¿‡èÚ¬}®;%iÿ1iÿ111,2665
+v@vu¤ë¼ËJndŒ:*¯<pz ”k[?Ñ¸Ö š‹œ ›ÌœtB"p“Õ+™W‚ʚ墖޶°AÔxžãu1SSl[îÿ;#a¼þ|ºÅßñ9Šm H ½‘„²´Êñîœ]_‘^]+Ü‚c H¨9Á×oýÝþ}È»!v$`é~4‹r±edK @Ò }%ò¢6Á’@(§À–(r¢¼9qÐ…æȲnœä\_ H5‡¢çdƒªWPh®l¯ÈÙ³gk&ŽŽì×yŸ73gÜn«oR4ÐÏ_Ž5Š¦-2*J£¿fÿÃxžÊüi£ZhùùóçsÀ«¯¾:0ùÓš¿Î°I³éÁƒ¾¿¼ž;w®-8U½µl'í ´‚õû¦Ç>pÝæâ'@Ϭé·ãÕW_…ÞmVƹ&Ðüâ׿âYá*}1íHpîÜ9£~ûô3¼Œ,S]S15,4078
+Ýûö¶t§tYJ³Št§tYJ³16,4628
+î ¸Y„Do†P8uê_Í&`}Ã1ËÊÊŒØ<ººZê_B !KÞ‡—KÞ‡18,5109
+ÑU]U20,5704
+ÑU]PPÀPP20,5704
+‹«0€ýÀÆvB~Q/ÓI[P[YÁ]ÃÁk|àˆö© /ká@ldQ}-†v‹`O ÍöJ ´Ú*nµV[ÏnµV[22,6043
+
+static/objectapp/js/wymeditor/skins/django/skin.css,363
+ .wym_skin_django p,21,610
+ .wym_skin_django p, .wym_skin_django h2,21,610
+ .wym_skin_django p, .wym_skin_django h2, .wym_skin_django h3,21,610
+ .wym_skin_django ul,22,680
+ .wym_skin_django .wym_section h2,67,2667
+ .wym_skin_django .wym_dropdown:hover ul,86,3923
+ .wym_skin_django .wym_buttons li a 95,4488
+
+static/objectapp/js/wymeditor/skins/compact/icons.png,45
+]11,2788
+]ÉBXóhžhò‰Üõ.Õ3ϲ±›RR12,3024
+
+static/objectapp/js/wymeditor/skins/compact/skin.css,373
+ .wym_skin_compact p,21,633
+ .wym_skin_compact p, .wym_skin_compact h2,21,633
+ .wym_skin_compact p, .wym_skin_compact h2, .wym_skin_compact h3,21,633
+ .wym_skin_compact ul,22,706
+ .wym_skin_compact .wym_section h2,67,2657
+ .wym_skin_compact .wym_dropdown:hover ul,86,3912
+ .wym_skin_compact .wym_buttons li a 93,4380
+
+static/objectapp/js/wymeditor/plugins/resizable/readme.txt,145
+The ``resizable`` plugin takes exactly one parameter,61,1916
+ minHeight:minHeight67,2204
+ var jQueryPlugins 89,3049
+Changelog110,3941
+
+static/objectapp/js/wymeditor/plugins/tidy/README,89
+Copyright (c) 2005 - 2009 Jean-Francois Hovinne,2,62
+Dual licensed under the MIT 3,137
+
+static/objectapp/js/markitup/templates/preview.html,33
+markItUp! preview template5,243
+
+static/objectapp/js/markitup/skins/django/style.css,408
+.markItUp a:link,link9,302
+ font:12px 'Courier New', Courier,28,652
+ font:12px 'Courier New', Courier, monospace;28,652
+ padding:5px;px29,698
+ width:790px;px30,712
+ height:220px;px31,726
+ clear:both;both32,741
+ line-height:18px;px33,754
+ overflow:auto;auto34,773
+.markItUpHeader ul .markItUpDropMenu markItUpDropMenu68,1428
+.markItUpHeader ul ul .markItUpDropMenu markItUpDropMenu87,1841
+
+static/objectapp/js/markitup/skins/django/images/menu.png,270
+wAÁ©÷~¸á“ÐkQ)?Âö•r• ár• 6,1115
+pOšxfØpOšxf9,1905
+chg€Ð’ê¡*ƒ…òäKxZÎ×¹RiPÉz¯Å»ozÐ+ÁœdcnÈ:­0ÕªJ±RÓÛâÛ(V}svàÍ·äBà –ðbÛ`Ö”ÌúàFÛðë9F#Ù ¦ÞÜb ¼ Ѿr=r10,2288
+¹1 Ëúþ+Kú¨èdXŒ];JÄ£Žïcˆx$§DÑ×·Þž¢X`i× ì¨ëÑ@lÌûrn°m$¨Æ^×9΄ïzBGÏžzBGÏ53,17284
+
+static/objectapp/js/markitup/sets/textile/images/quotes.png,50
+µDMs6gRÎLDMs6gRÎ4,156
+µDMs6gRÎL:,Óp÷p4,156
+
+static/objectapp/js/markitup/sets/textile/images/h2.png,11
+${$4,125
+
+static/objectapp/js/markitup/sets/html/images/h2.png,11
+${$4,125
+
+static/objectapp/js/markitup/sets/html/images/image.png,101
+‰PNGPNG1,0
+º]ñ»Hà Bv„åÌ“µÚˆ_Sÿ%Ò;ô6×9Host wöEwöE4,391
+ó]*f"Å"ô½.\00CLâCL5,439
+
+static/objectapp/js/markitup/sets/markdown/images/quotes.png,50
+µDMs6gRÎLDMs6gRÎ4,156
+µDMs6gRÎL:,Óp÷p4,156
+
+static/objectapp/js/markitup/sets/markdown/images/h2.png,11
+${$4,125
+
+static/objectapp/js/markitup/sets/restructuredtext/images/quotes.png,50
+µDMs6gRÎLDMs6gRÎ4,156
+µDMs6gRÎL:,Óp÷p4,156
+
+static/objectapp/js/markitup/sets/restructuredtext/images/h2.png,11
+${$4,125
+
+ping.py,417
+class URLRessources(17,347
+ def __init__(20,433
+class DirectoryPinger(29,843
+ def __init__(32,921
+ def run(44,1321
+ def ping_gbobject(54,1740
+class ExternalUrlsPinger(78,2713
+ def __init__(81,2797
+ def run(93,3213
+ def is_external_url(108,3770
+ def find_external_urls(115,4029
+ def find_pingback_href(123,4382
+ def find_pingback_urls(132,4768
+ def pingback_url(160,5812
+
+plugins/cms_app.py,31
+class ObjectappApphook(10,230
+
+plugins/cms_plugins.py,318
+class CMSLatestGbobjectsPlugin(19,547
+ def formfield_for_manytomany(52,1395
+ def render(61,1807
+ def icon_src(85,2782
+class CMSSelectedGbobjectsPlugin(90,2923
+ def render(100,3297
+ def icon_src(107,3587
+class CMSRandomGbobjectsPlugin(112,3728
+ def render(121,4063
+ def icon_src(129,4408
+
+plugins/migrations/0004_auto__add_field_latestgbobjectsplugin_subobjecttypes.py,71
+class Migration(5,63
+ def forwards(7,98
+ def backwards(12,349
+
+plugins/migrations/0005_auto__add_randomgbobjectsplugin.py,71
+class Migration(5,63
+ def forwards(7,98
+ def backwards(17,668
+
+plugins/migrations/0002_auto__add_field_latestgbobjectsplugin_template_to_render__add_field_sele.py,71
+class Migration(5,63
+ def forwards(7,98
+ def backwards(15,700
+
+plugins/migrations/0001_initial.py,73
+class Migration(6,92
+ def forwards(8,127
+ def backwards(33,1752
+
+plugins/migrations/0003_auto__del_field_latestgbobjectsplugin_objecttype__del_field_latestgbobjectsp.py,73
+class Migration(6,92
+ def forwards(8,127
+ def backwards(40,2041
+
+plugins/admin.py,67
+class GbobjectPlaceholderAdmin(14,421
+ def save_model(33,1637
+
+plugins/models.py,364
+class LatestGbobjectsPlugin(21,698
+ def render_template(42,1508
+ def copy_relations(47,1666
+ def __unicode__(53,1923
+class SelectedGbobjectsPlugin(57,2012
+ def render_template(68,2391
+ def copy_relations(73,2549
+ def __unicode__(77,2708
+class RandomGbobjectsPlugin(81,2795
+ def __unicode__(91,3163
+def invalidate_menu_cache(95,3252
+
+plugins/placeholder.py,61
+class GbobjectPlaceholder(7,139
+ class Meta:Meta12,299
+
+plugins/menu.py,272
+class GbobjectMenu(17,506
+ def get_nodes(21,643
+class ObjecttypeMenu(63,2464
+ def get_nodes(67,2579
+class AuthorMenu(80,3136
+ def get_nodes(84,3239
+class TagMenu(98,3842
+ def get_nodes(102,3936
+class GbobjectModifier(115,4459
+ def modify(119,4593
+
+sitemaps.py,459
+class GbobjectSitemap(13,320
+ def items(18,430
+ def lastmod(22,533
+class ObjecttypeSitemap(27,648
+ def cache(31,744
+ def items(42,1195
+ def lastmod(48,1371
+ def priority(55,1593
+class AuthorSitemap(63,1817
+ def items(68,1924
+ def lastmod(72,2023
+ def location(79,2242
+class TagSitemap(84,2382
+ def cache(88,2464
+ def items(97,2856
+ def lastmod(103,2996
+ def priority(108,3162
+ def location(115,3381
+
+models.py,994
+class Author(52,1966
+ def gbobjects_published(58,2094
+ def get_absolute_url(63,2254
+ class Meta:Meta68,2430
+class Gbobject(74,2501
+ def get_relations(147,5681
+ def get_attributes(188,7648
+ def get_nbh(198,7976
+ def get_relations1(218,8649
+ def get_rendered_nbh(239,9396
+ def html_content(273,10506
+ def previous_gbobject(287,11007
+ def next_gbobject(295,11253
+ def word_count(303,11517
+ def is_actual(308,11661
+ def is_visible(314,11870
+ def related_published(319,12024
+ def discussions(324,12175
+ def comments(330,12356
+ def pingbacks(336,12550
+ def trackbacks(341,12694
+ def comments_are_open(346,12841
+ def short_url(354,13144
+ def __unicode__(358,13257
+ def memberof_sentence(362,13325
+ def get_absolute_url(372,13742
+ class Meta:Meta380,14059
+class Process(390,14350
+ def __unicode__(416,15811
+ class Meta:Meta419,15865
+class System(426,16090
+ def __unicode__(457,17561
+
+xmlrpc/metaweblog.py,475
+def authenticate(30,955
+def blog_structure(46,1597
+def user_structure(54,1839
+def author_structure(66,2235
+def Objecttype_structure(74,2449
+def post_structure(90,3127
+def get_users_blogs(119,4485
+def get_user_info(128,4798
+def get_authors(137,5118
+def delete_post(147,5507
+def get_post(157,5906
+def get_recent_posts(167,6287
+def get_objecttypes(177,6718
+def new_Objecttype(187,7127
+def new_post(204,7901
+def edit_post(248,9902
+def new_media_object(291,11783
+
+xmlrpc/pingback.py,113
+def generate_pingback_content(28,835
+def pingback_ping(54,1557
+def pingback_extensions_get_pingbacks(114,3742
+
+comparison.py,268
+def pearson_score(9,166
+class ClusteredModel(26,691
+ def __init__(30,795
+ def dataset(34,907
+class VectorBuilder(44,1233
+ def __init__(47,1315
+ def build_dataset(54,1525
+ def generate_key(82,2521
+ def flush(86,2655
+ def __call__(91,2786
+
+xmlrpc/__init__.py,0
+
+plugins/migrations/__init__.py,0
+
+plugins/__init__.py,0
+
+plugins/settings.py,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/h4.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/list-numeric.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/code.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/list-bullet.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/h5.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/picture.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/h3.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/link.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/preview.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/h1.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/italic.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/images/bold.png,0
+
+static/objectapp/js/markitup/sets/restructuredtext/style.css,0
+
+static/objectapp/js/markitup/sets/markdown/images/h4.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/list-numeric.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/code.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/list-bullet.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/h5.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/picture.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/h3.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/link.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/preview.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/h1.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/italic.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/h6.png,0
+
+static/objectapp/js/markitup/sets/markdown/images/bold.png,0
+
+static/objectapp/js/markitup/sets/markdown/style.css,0
+
+static/objectapp/js/markitup/sets/html/images/h4.png,0
+
+static/objectapp/js/markitup/sets/html/images/list-numeric.png,0
+
+static/objectapp/js/markitup/sets/html/images/clean.png,0
+
+static/objectapp/js/markitup/sets/html/images/list-item.png,0
+
+static/objectapp/js/markitup/sets/html/images/list-bullet.png,0
+
+static/objectapp/js/markitup/sets/html/images/h5.png,0
+
+static/objectapp/js/markitup/sets/html/images/picture.png,0
+
+static/objectapp/js/markitup/sets/html/images/h3.png,0
+
+static/objectapp/js/markitup/sets/html/images/link.png,0
+
+static/objectapp/js/markitup/sets/html/images/preview.png,0
+
+static/objectapp/js/markitup/sets/html/images/h1.png,0
+
+static/objectapp/js/markitup/sets/html/images/italic.png,0
+
+static/objectapp/js/markitup/sets/html/images/stroke.png,0
+
+static/objectapp/js/markitup/sets/html/images/paragraph.png,0
+
+static/objectapp/js/markitup/sets/html/images/h6.png,0
+
+static/objectapp/js/markitup/sets/html/images/bold.png,0
+
+static/objectapp/js/markitup/sets/html/style.css,0
+
+static/objectapp/js/markitup/sets/textile/images/h4.png,0
+
+static/objectapp/js/markitup/sets/textile/images/list-numeric.png,0
+
+static/objectapp/js/markitup/sets/textile/images/code.png,0
+
+static/objectapp/js/markitup/sets/textile/images/list-bullet.png,0
+
+static/objectapp/js/markitup/sets/textile/images/h5.png,0
+
+static/objectapp/js/markitup/sets/textile/images/picture.png,0
+
+static/objectapp/js/markitup/sets/textile/images/h3.png,0
+
+static/objectapp/js/markitup/sets/textile/images/link.png,0
+
+static/objectapp/js/markitup/sets/textile/images/preview.png,0
+
+static/objectapp/js/markitup/sets/textile/images/h1.png,0
+
+static/objectapp/js/markitup/sets/textile/images/italic.png,0
+
+static/objectapp/js/markitup/sets/textile/images/stroke.png,0
+
+static/objectapp/js/markitup/sets/textile/images/paragraph.png,0
+
+static/objectapp/js/markitup/sets/textile/images/h6.png,0
+
+static/objectapp/js/markitup/sets/textile/images/bold.png,0
+
+static/objectapp/js/markitup/sets/textile/style.css,0
+
+static/objectapp/js/markitup/skins/django/images/submenu.png,0
+
+static/objectapp/js/markitup/skins/django/images/handle.png,0
+
+static/objectapp/js/markitup/templates/preview.css,0
+
+static/objectapp/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif,0
+
+static/objectapp/js/wymeditor/plugins/tidy/wand.png,0
+
+static/objectapp/js/wymeditor/plugins/tidy/tidy.php,0
+
+static/objectapp/js/wymeditor/skins/minimal/images/bg.wymeditor.png,0
+
+static/objectapp/js/wymeditor/skins/minimal/images/icons.silver.gif,0
+
+static/objectapp/js/wymeditor/skins/minimal/images/bg.header.gif,0
+
+static/objectapp/js/wymeditor/skins/silver/images/bg.wymeditor.png,0
+
+static/objectapp/js/wymeditor/skins/silver/images/icons.silver.gif,0
+
+static/objectapp/js/wymeditor/skins/silver/images/bg.header.gif,0
+
+static/objectapp/js/wymeditor/iframe/default/lbl-h5.png,0
+
+static/objectapp/js/wymeditor/iframe/default/lbl-h3.png,0
+
+static/objectapp/js/wymeditor/iframe/default/lbl-pre.png,0
+
+static/objectapp/js/wymeditor/iframe/default/lbl-h6.png,0
+
+static/objectapp/js/wymeditor/iframe/default/lbl-blockquote.png,0
+
+static/objectapp/js/wymeditor/iframe/default/lbl-h1.png,0
+
+static/objectapp/js/wymeditor/iframe/default/lbl-h4.png,0
+
+static/objectapp/js/wymeditor/iframe/default/lbl-h2.png,0
+
+static/objectapp/css/slider.css,0
+
+static/objectapp/css/jquery.autocomplete.css,0
+
+static/objectapp/css/wymeditor_styles.css,0
+
+static/objectapp/css/src/slider.sass,0
+
+static/objectapp/css/src/_gbobject.sass,0
+
+static/objectapp/css/src/ie.sass,0
+
+static/objectapp/css/src/_paginator.sass,0
+
+static/objectapp/css/src/_calendar.sass,0
+
+static/objectapp/css/src/_tag-cloud.sass,0
+
+static/objectapp/img/background.gif,0
+
+static/objectapp/img/tags.png,0
+
+static/objectapp/img/rss.png,0
+
+static/objectapp/img/bullet.png,0
+
+static/objectapp/img/wlw/objectapp.png,0
+
+static/objectapp/img/favicon.ico,0
+
+static/objectapp/img/grid.png,0
+
+static/objectapp/img/preview.png,0
+
+static/objectapp/img/comments.png,0
+
+static/objectapp/img/trans.png,0
+
+static/objectapp/img/shorturl.png,0
+
+static/objectapp/img/manage.png,0
+
+static/objectapp/img/help.png,0
+
+spam_checker/backends/__init__.py,0
+
+templates/feeds/comment_title.html,0
+
+templates/feeds/gbobject_title.html,0
+
+templates/feeds/trackback_description.html,0
+
+templates/feeds/gbobject_description.html,0
+
+templates/feeds/comment_description.html,0
+
+templates/feeds/pingback_description.html,0
+
+templates/feeds/discussion_description.html,0
+
+templates/feeds/trackback_title.html,0
+
+templates/feeds/pingback_title.html,0
+
+templates/feeds/discussion_title.html,0
+
+templates/admin/objectapp/widgets/_content_stats.html,0
+
+templates/admin/objectapp/widgets/recent_comments.html,0
+
+templates/admin/objectapp/widgets/draft_gbobjects.html,0
+
+templates/admin/objectapp/widgets/_recent_linkbacks.html,0
+
+templates/admin/objectapp/widgets/_draft_gbobjects.html,0
+
+templates/admin/objectapp/widgets/recent_linkbacks.html,0
+
+templates/admin/objectapp/widgets/_recent_comments.html,0
+
+templates/admin/objectapp/widgets/content_stats.html,0
+
+templates/comments/comment_reply_email.txt,0
+
+templates/comments/comment_notification_email.txt,0
+
+templates/comments/objectapp/gbobject/form.html,0
+
+templates/comments/comment_authors_email.txt,0
+
+templates/objectapp/author/gbobject_list.html,0
+
+templates/objectapp/gbobject_archive.html,0
+
+templates/objectapp/tag/gbobject_list.html,0
+
+templates/objectapp/cms/gbobject_detail.html,0
+
+templates/objectapp/cms/random_gbobjects.html,0
+
+templates/objectapp/cms/gbobject_list.html,0
+
+templates/objectapp/objecttype/gbobject_list.html,0
+
+templates/objectapp/_subtype_detail.html,0
+
+templates/objectapp/tags/breadcrumbs.html,0
+
+templates/objectapp/tags/recent_gbobjects.html,0
+
+templates/objectapp/tags/tag_cloud.html,0
+
+templates/objectapp/tags/random_gbobjects.html,0
+
+templates/objectapp/tags/recent_comments.html,0
+
+templates/objectapp/tags/recent_linkbacks.html,0
+
+templates/objectapp/tags/popular_gbobjects.html,0
+
+templates/objectapp/tags/similar_gbobjects.html,0
+
+templates/objectapp/tags/archives_gbobjects.html,0
+
+templates/objectapp/tags/objecttypes.html,0
+
+templates/objectapp/tags/archives_gbobjects_tree.html,0
+
+templates/objectapp/tags/calendar.html,0
+
+templates/objectapp/tags/gbobjects.html,0
+
+templates/objectapp/tags/featured_gbobjects.html,0
+
+templates/objectapp/tags/dummy.html,0
+
+templates/objectapp/tags/archives_gbobjects_link.html,0
+
+templates/objectapp/tags/pagination.html,0
+
+templates/objectapp/tags/authors.html,0
+
+migrations/0001_initial.pyc,0
+
+migrations/__init__.pyc,0
+
+migrations/__init__.py,0
+
+locale/de/LC_MESSAGES/django.po,0
+
+locale/pt_BR/LC_MESSAGES/django.po,0
+
+locale/zh_CN/LC_MESSAGES/django.po,0
+
+locale/ru/LC_MESSAGES/django.po,0
+
+locale/es/LC_MESSAGES/django.po,0
+
+locale/hu/LC_MESSAGES/django.po,0
+
+locale/it/LC_MESSAGES/django.po,0
+
+locale/fr/LC_MESSAGES/django.po,0
+
+locale/nl/LC_MESSAGES/django.po,0
+
+locale/pl/LC_MESSAGES/django.po,0
+
+templatetags/__init__.py,0
+
+url_shortener/backends/__init__.py,0
+
+admin/__init__.py,0
+
+views/__init__.py,0
+
+__init__.py,0
+
+tests/templates/objectapp/gbobject_detail.html,0
+
+tests/templates/objectapp/gbobject_search.html,0
+
+tests/templates/objectapp/gbobject_list.html,0
+
+tests/urls.py,0
+
+urls/capabilities.py,0
+
+urls/discussions.py,0
+
+urls/sitemap.py,0
+
+urls/objecttypes.py,0
+
+urls/tags.py,0
+
+urls/gbobjects.py,0
+
+urls/search.py,0
+
+urls/trackback.py,0
+
+urls/__init__.py,0
+
+urls/authors.py,0
+
+urls/quick_gbobject.py,0
+
+urls/feeds.py,0
+
+management/commands/__init__.py,0
+
+management/__init__.py,0
+
+settings.py,0
+
+testsettings.py,0
diff --git a/objectapp/__init__.py b/objectapp/__init__.py
new file mode 100644
index 0000000..2a57f36
--- /dev/null
+++ b/objectapp/__init__.py
@@ -0,0 +1,55 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Objectapp"""
+__version__ = '0.3.dev'
+__license__ = 'BSD License'
+
+__author__ = 'Nagarjuna G.'
+__email__ = 'nagarjun@gnowledge.org'
+
+__url__ = 'http://github.com/gnowgi/django-gstudio'
diff --git a/objectapp/admin/__init__.py b/objectapp/admin/__init__.py
new file mode 100644
index 0000000..01c0450
--- /dev/null
+++ b/objectapp/admin/__init__.py
@@ -0,0 +1,79 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Admin of Objectapp"""
+from django.contrib import admin
+
+from objectapp.models import Gbobject
+from objectapp.models import Process
+from objectapp.models import System
+
+from objectapp.admin.gbobject import GbobjectAdmin
+from objectapp.admin.process import ProcessAdmin
+from objectapp.admin.system import SystemAdmin
+
+admin.site.register(Gbobject, GbobjectAdmin)
+admin.site.register(Process, ProcessAdmin)
+admin.site.register(System, SystemAdmin)
+
diff --git a/objectapp/admin/forms.py b/objectapp/admin/forms.py
new file mode 100644
index 0000000..c21e4e5
--- /dev/null
+++ b/objectapp/admin/forms.py
@@ -0,0 +1,153 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Forms for Objectapp admin"""
+from django import forms
+from django.db.models import ManyToOneRel
+from django.db.models import ManyToManyRel
+from django.contrib.sites.models import Site
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
+
+import reversion
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.models import System
+from objectapp.models import Process
+from objectapp.models import Systemtype
+from gstudio.models import Edge
+from gstudio.models import Node
+from gstudio.models import Relation
+from gstudio.models import Attribute
+from gstudio.models import Processtype
+from gstudio.models import Nodetype
+from gstudio.models import Metatype
+
+from objectapp.admin.widgets import TreeNodeChoiceField
+from objectapp.admin.widgets import MPTTFilteredSelectMultiple
+from objectapp.admin.widgets import MPTTModelMultipleChoiceField
+
+
+class ProcessAdminForm(forms.ModelForm):
+
+ class Meta:
+ """SystemAdminForm's Meta"""
+ model = Process
+
+class SystemAdminForm(forms.ModelForm):
+
+ class Meta:
+ """SystemAdminForm's Meta"""
+ model = System
+
+
+
+class ObjecttypeAdminForm(forms.ModelForm):
+ """Form for Objecttype's Admin"""
+ parent = TreeNodeChoiceField(
+ label=_('parent Objecttype').capitalize(),
+ required=False, empty_label=_('No parent Objecttype'),
+ queryset=Objecttype.tree.all())
+
+ def __init__(self, *args, **kwargs):
+ super(ObjecttypeAdminForm, self).__init__(*args, **kwargs)
+ rel = ManyToOneRel(Objecttype, 'id')
+ self.fields['parent'].widget = RelatedFieldWidgetWrapper(
+ self.fields['parent'].widget, rel, self.admin_site)
+
+ def clean_parent(self):
+ """Check if Objecttype parent is not selfish"""
+ data = self.cleaned_data['parent']
+ if data == self.instance:
+ raise forms.ValidationError(
+ _('A Objecttype cannot be parent of itself.'))
+ return data
+
+ class Meta:
+ """ObjecttypeAdminForm's Meta"""
+ model = Objecttype
+
+
+class GbobjectAdminForm(forms.ModelForm):
+ """Form for Gbobject's Admin"""
+ objecttypes = MPTTModelMultipleChoiceField(
+ label=_('Objecttypes'), required=False,
+ queryset=Objecttype.objects.all(),
+ widget=MPTTFilteredSelectMultiple(_('objecttypes'), False,
+ attrs={'rows': '10'}))
+
+
+ def __init__(self, *args, **kwargs):
+ super(GbobjectAdminForm, self).__init__(*args, **kwargs)
+ rel = ManyToManyRel(Objecttype, 'id')
+
+ self.fields['objecttypes'].widget = RelatedFieldWidgetWrapper(
+ self.fields['objecttypes'].widget, rel, self.admin_site)
+
+
+ self.fields['sites'].initial = [Site.objects.get_current()]
+
+ class Meta:
+ """GbobjectAdminForm's Meta"""
+ model = Gbobject
diff --git a/objectapp/admin/gbobject.py b/objectapp/admin/gbobject.py
new file mode 100644
index 0000000..5b2ce3d
--- /dev/null
+++ b/objectapp/admin/gbobject.py
@@ -0,0 +1,378 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""GbobjectAdmin for Objectapp"""
+from datetime import datetime
+
+from django.forms import Media
+from django.contrib import admin
+from django.contrib.auth.models import User
+from django.utils.html import strip_tags
+from django.utils.text import truncate_words
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+from django.conf import settings as project_settings
+from django.utils.translation import ugettext_lazy as _
+from django.core.urlresolvers import reverse, NoReverseMatch
+import reversion
+from tagging.models import Tag
+
+from objectapp import settings
+from objectapp.managers import HIDDEN
+from objectapp.managers import PUBLISHED
+from objectapp.ping import DirectoryPinger
+from objectapp.admin.forms import GbobjectAdminForm
+
+
+class GbobjectAdmin(reversion.VersionAdmin):
+ """Admin for Gbobject model"""
+ form = GbobjectAdminForm
+ date_hierarchy = 'creation_date'
+ fieldsets = ((_('Content'), {'fields': ('title', 'altnames', 'objecttypes','content',
+ 'image', 'status')}),
+ (_('Dependency'), {'fields': ('prior_nodes', 'posterior_nodes',),
+ 'classes': ('collapse', 'collapse-closed')}),
+
+ (_('Options'), {'fields': ('featured', 'excerpt', 'template',
+ 'authors',
+ 'creation_date',
+ 'start_publication',
+ 'end_publication'),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Privacy'), {'fields': ('password', 'login_required',),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Discussion'), {'fields': ('comment_enabled',
+ 'pingback_enabled')}),
+ (_('Publication'), {'fields': ( 'tags',
+ 'sites', 'slug')}))
+ list_filter = ('objecttypes', 'authors', 'status', 'featured',
+ 'login_required', 'comment_enabled', 'pingback_enabled',
+ 'creation_date', 'start_publication',
+ 'end_publication', 'sites')
+ list_display = ('get_title', 'get_authors', 'get_objecttypes',
+ 'get_tags', 'get_sites',
+ 'get_comments_are_open', 'pingback_enabled',
+ 'get_is_actual', 'get_is_visible', 'get_link',
+ 'get_short_url', 'creation_date')
+ radio_fields = {'template': admin.VERTICAL}
+ filter_horizontal = ('objecttypes', 'authors')
+ prepopulated_fields = {'slug': ('title', )}
+ search_fields = ('title', 'excerpt', 'content', 'tags')
+ actions = ['make_mine', 'make_published', 'make_hidden',
+ 'close_comments', 'close_pingbacks',
+ 'ping_directories', 'make_tweet', 'put_on_top']
+ actions_on_top = True
+ actions_on_bottom = True
+
+ def __init__(self, model, admin_site):
+ self.form.admin_site = admin_site
+ super(GbobjectAdmin, self).__init__(model, admin_site)
+
+ # Custom Display
+ def get_title(self, gbobject):
+ """Return the title with word count and number of comments"""
+ title = _('%(title)s (%(word_count)i words)') % \
+ {'title': gbobject.title, 'word_count': gbobject.word_count}
+ comments = gbobject.comments.count()
+ if comments:
+ return _('%(title)s (%(comments)i comments)') % \
+ {'title': title, 'comments': comments}
+ return title
+ get_title.short_description = _('title')
+
+ def get_authors(self, gbobject):
+ """Return the authors in HTML"""
+ try:
+ authors = ['<a href="%s" target="blank">%s</a>' %
+ (reverse('objectapp_author_detail',
+ args=[author.username]),
+ author.username) for author in gbobject.authors.all()]
+ except NoReverseMatch:
+ authors = [author.username for author in gbobject.authors.all()]
+ return ', '.join(authors)
+ get_authors.allow_tags = True
+ get_authors.short_description = _('author(s)')
+
+ def get_objecttypes(self, gbobject):
+ """Return the objecttypes linked in HTML"""
+ try:
+ objecttypes = ['<a href="%s" target="blank">%s</a>' %
+ (Objecttype.get_absolute_url(), Objecttype.title)
+ for Objecttype in gbobject.objecttypes.all()]
+ except NoReverseMatch:
+ objecttypes = [Objecttype.title for Objecttype in
+ gbobject.objecttypes.all()]
+ return ', '.join(objecttypes)
+ get_objecttypes.allow_tags = True
+ get_objecttypes.short_description = _('Objecttype(s)')
+
+ def get_tags(self, gbobject):
+ """Return the tags linked in HTML"""
+ try:
+ return ', '.join(['<a href="%s" target="blank">%s</a>' %
+ (reverse('objectapp_tag_detail',
+ args=[tag.name]), tag.name)
+ for tag in Tag.objects.get_for_object(gbobject)])
+ except NoReverseMatch:
+ return gbobject.tags
+ get_tags.allow_tags = True
+ get_tags.short_description = _('tag(s)')
+
+ def get_sites(self, gbobject):
+ """Return the sites linked in HTML"""
+ return ', '.join(
+ ['<a href="http://%(domain)s" target="blank">%(name)s</a>' %
+ site.__dict__ for site in gbobject.sites.all()])
+ get_sites.allow_tags = True
+ get_sites.short_description = _('site(s)')
+
+ def get_comments_are_open(self, gbobject):
+ """Admin wrapper for gbobject.comments_are_open"""
+ return gbobject.comments_are_open
+ get_comments_are_open.boolean = True
+ get_comments_are_open.short_description = _('comment enabled')
+
+ def get_is_actual(self, gbobject):
+ """Admin wrapper for gbobject.is_actual"""
+ return gbobject.is_actual
+ get_is_actual.boolean = True
+ get_is_actual.short_description = _('is actual')
+
+ def get_is_visible(self, gbobject):
+ """Admin wrapper for gbobject.is_visible"""
+ return gbobject.is_visible
+ get_is_visible.boolean = True
+ get_is_visible.short_description = _('is visible')
+
+ def get_link(self, gbobject):
+ """Return a formated link to the gbobject"""
+ return u'<a href="%s" target="blank">%s</a>' % (
+ gbobject.get_absolute_url(), _('View'))
+ get_link.allow_tags = True
+ get_link.short_description = _('View on site')
+
+ def get_short_url(self, gbobject):
+ """Return the short url in HTML"""
+ short_url = gbobject.short_url
+ if not short_url:
+ return _('Unavailable')
+ return '<a href="%(url)s" target="blank">%(url)s</a>' % \
+ {'url': short_url}
+ get_short_url.allow_tags = True
+ get_short_url.short_description = _('short url')
+
+ # Custom Methods
+ def save_model(self, request, gbobject, form, change):
+ """Save the authors, update time, make an excerpt"""
+ if not form.cleaned_data.get('excerpt') and gbobject.status == PUBLISHED:
+ gbobject.excerpt = truncate_words(strip_tags(gbobject.content), 50)
+
+ if gbobject.pk and not request.user.has_perm('objectapp.can_change_author'):
+ form.cleaned_data['authors'] = gbobject.authors.all()
+
+ if not form.cleaned_data.get('authors'):
+ form.cleaned_data['authors'].append(request.user)
+
+ gbobject.last_update = datetime.now()
+ gbobject.save()
+
+ def queryset(self, request):
+ """Make special filtering by user permissions"""
+ queryset = super(GbobjectAdmin, self).queryset(request)
+ if request.user.has_perm('objectapp.can_view_all'):
+ return queryset
+ return request.user.gbobjects.all()
+
+ def formfield_for_manytomany(self, db_field, request, **kwargs):
+ """Filters the disposable authors"""
+ if db_field.name == 'authors':
+ if request.user.has_perm('objectapp.can_change_author'):
+ kwargs['queryset'] = User.objects.filter(is_staff=True)
+ else:
+ kwargs['queryset'] = User.objects.filter(pk=request.user.pk)
+
+ return super(GbobjectAdmin, self).formfield_for_manytomany(
+ db_field, request, **kwargs)
+
+ def get_actions(self, request):
+ """Define user actions by permissions"""
+ actions = super(GbobjectAdmin, self).get_actions(request)
+ if not request.user.has_perm('objectapp.can_change_author') \
+ or not request.user.has_perm('objectapp.can_view_all'):
+ del actions['make_mine']
+ if not settings.PING_DIRECTORIES:
+ del actions['ping_directories']
+ if not settings.USE_TWITTER:
+ del actions['make_tweet']
+
+ return actions
+
+ # Custom Actions
+ def make_mine(self, request, queryset):
+ """Set the gbobjects to the user"""
+ for gbobject in queryset:
+ if request.user not in gbobject.authors.all():
+ gbobject.authors.add(request.user)
+ self.message_user(
+ request, _('The selected gbobjects now belong to you.'))
+ make_mine.short_description = _('Set the gbobjects to the user')
+
+ def make_published(self, request, queryset):
+ """Set gbobjects selected as published"""
+ queryset.update(status=PUBLISHED)
+ self.ping_directories(request, queryset, messages=False)
+ self.message_user(
+ request, _('The selected gbobjects are now marked as published.'))
+ make_published.short_description = _('Set gbobjects selected as published')
+
+ def make_hidden(self, request, queryset):
+ """Set gbobjects selected as hidden"""
+ queryset.update(status=HIDDEN)
+ self.message_user(
+ request, _('The selected gbobjects are now marked as hidden.'))
+ make_hidden.short_description = _('Set gbobjects selected as hidden')
+
+ def make_tweet(self, request, queryset):
+ """Post an update on Twitter"""
+ import tweepy
+ auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY,
+ settings.TWITTER_CONSUMER_SECRET)
+ auth.set_access_token(settings.TWITTER_ACCESS_KEY,
+ settings.TWITTER_ACCESS_SECRET)
+ api = tweepy.API(auth)
+ for gbobject in queryset:
+ short_url = gbobject.short_url
+ message = '%s %s' % (gbobject.title[:139 - len(short_url)], short_url)
+ api.update_status(message)
+ self.message_user(
+ request, _('The selected gbobjects have been tweeted.'))
+ make_tweet.short_description = _('Tweet gbobjects selected')
+
+ def close_comments(self, request, queryset):
+ """Close the comments for selected gbobjects"""
+ queryset.update(comment_enabled=False)
+ self.message_user(
+ request, _('Comments are now closed for selected gbobjects.'))
+ close_comments.short_description = _('Close the comments for '\
+ 'selected gbobjects')
+
+ def close_pingbacks(self, request, queryset):
+ """Close the pingbacks for selected gbobjects"""
+ queryset.update(pingback_enabled=False)
+ self.message_user(
+ request, _('Linkbacks are now closed for selected gbobjects.'))
+ close_pingbacks.short_description = _(
+ 'Close the linkbacks for selected gbobjects')
+
+ def put_on_top(self, request, queryset):
+ """Put the selected gbobjects on top at the current date"""
+ queryset.update(creation_date=datetime.now())
+ self.ping_directories(request, queryset, messages=False)
+ self.message_user(request, _(
+ 'The selected gbobjects are now set at the current date.'))
+ put_on_top.short_description = _(
+ 'Put the selected gbobjects on top at the current date')
+
+ def ping_directories(self, request, queryset, messages=True):
+ """Ping Directories for selected gbobjects"""
+ for directory in settings.PING_DIRECTORIES:
+ pinger = DirectoryPinger(directory, queryset)
+ pinger.join()
+ if messages:
+ success = 0
+ for result in pinger.results:
+ if not result.get('flerror', True):
+ success += 1
+ else:
+ self.message_user(request,
+ '%s : %s' % (directory,
+ result['message']))
+ if success:
+ self.message_user(
+ request,
+ _('%(directory)s directory succesfully ' \
+ 'pinged %(success)d gbobjects.') %
+ {'directory': directory, 'success': success})
+ ping_directories.short_description = _(
+ 'Ping Directories for selected gbobjects')
+
+ def get_urls(self):
+ gbobject_admin_urls = super(GbobjectAdmin, self).get_urls()
+ urls = patterns(
+ 'django.views.generic.simple',
+ url(r'^autocomplete_tags/$', 'direct_to_template',
+ {'template': 'admin/objectapp/gbobject/autocomplete_tags.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_gbobject_autocomplete_tags'),
+ url(r'^wymeditor/$', 'direct_to_template',
+ {'template': 'admin/objectapp/gbobject/wymeditor.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_gbobject_wymeditor'),
+ url(r'^markitup/$', 'direct_to_template',
+ {'template': 'admin/objectapp/gbobject/markitup.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_gbobject_markitup'),)
+ return urls + gbobject_admin_urls
+
+ def _media(self):
+ STATIC_URL = '%sobjectapp/' % project_settings.STATIC_URL
+ media = super(GbobjectAdmin, self).media + Media(
+ css={'all': ('%scss/jquery.autocomplete.css' % STATIC_URL,)},
+ js=('%sjs/jquery.js' % STATIC_URL,
+ '%sjs/jquery.bgiframe.js' % STATIC_URL,
+ '%sjs/jquery.autocomplete.js' % STATIC_URL,
+ reverse('admin:objectapp_gbobject_autocomplete_tags'),))
+
+ if settings.WYSIWYG == 'wymeditor':
+ media += Media(
+ js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % STATIC_URL,
+ '%sjs/wymeditor/plugins/hovertools/'
+ 'jquery.wymeditor.hovertools.js' % STATIC_URL,
+ reverse('admin:objectapp_gbobject_wymeditor')))
+ elif settings.WYSIWYG == 'tinymce':
+ from tinymce.widgets import TinyMCE
+ media += TinyMCE().media + Media(
+ js=(reverse('tinymce-js', args=('admin/objectapp/gbobject',)),))
+ elif settings.WYSIWYG == 'markitup':
+ media += Media(
+ js=('%sjs/markitup/jquery.markitup.js' % STATIC_URL,
+ '%sjs/markitup/sets/%s/set.js' % (
+ STATIC_URL, settings.MARKUP_LANGUAGE),
+ reverse('admin:objectapp_gbobject_markitup')),
+ css={'all': (
+ '%sjs/markitup/skins/django/style.css' % STATIC_URL,
+ '%sjs/markitup/sets/%s/style.css' % (
+ STATIC_URL, settings.MARKUP_LANGUAGE))})
+ return media
+ media = property(_media)
diff --git a/objectapp/admin/process.py b/objectapp/admin/process.py
new file mode 100644
index 0000000..21f9e07
--- /dev/null
+++ b/objectapp/admin/process.py
@@ -0,0 +1,373 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""GbobjectAdmin for Objectapp"""
+from datetime import datetime
+
+from django.forms import Media
+from django.contrib import admin
+from django.contrib.auth.models import User
+from django.utils.html import strip_tags
+from django.utils.text import truncate_words
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+from django.conf import settings as project_settings
+from django.utils.translation import ugettext_lazy as _
+from django.core.urlresolvers import reverse, NoReverseMatch
+import reversion
+from tagging.models import Tag
+
+from objectapp import settings
+from objectapp.managers import HIDDEN
+from objectapp.managers import PUBLISHED
+from objectapp.ping import DirectoryPinger
+from objectapp.admin.forms import ProcessAdminForm
+
+
+class ProcessAdmin(reversion.VersionAdmin):
+ """Admin for Process model"""
+ form = ProcessAdminForm
+ date_hierarchy = 'creation_date'
+ fieldsets = (
+ (_('Neighbourhood definition'), {'fields': ('title',
+ 'processtypes',
+ 'priorstate_attribute_set',
+ 'priorstate_relation_set',
+ 'poststate_attribute_set',
+ 'poststate_relation_set',
+ 'altnames' ,
+ 'slug',
+
+ 'status')}),
+
+ (_('Content'), {'fields': ('content', 'image',),
+ 'classes': ('collapse', 'collapse-closed')}),
+
+
+
+ (_('Dependency'), {'fields': ('prior_nodes', 'posterior_nodes',),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Options'), {'fields': ('featured', 'excerpt', 'template',
+ 'authors',
+ 'creation_date',
+ 'start_publication',
+ 'end_publication'),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Privacy'), {'fields': ('password', 'login_required',),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Publication'), {'fields': ('tags',
+ 'sites')}))
+ list_filter = ('objecttypes', 'authors', 'status', 'featured',
+ 'login_required', 'comment_enabled', 'pingback_enabled',
+ 'creation_date', 'start_publication',
+ 'end_publication', 'sites')
+ list_display = ('get_title', 'get_authors', 'get_objecttypes',
+ 'get_tags', 'get_sites',
+ 'get_comments_are_open', 'pingback_enabled',
+ 'get_is_actual', 'get_is_visible', 'get_link',
+ 'get_short_url', 'creation_date')
+ radio_fields = {'template': admin.VERTICAL}
+ filter_horizontal = ('objecttypes', 'authors')
+ prepopulated_fields = {'slug': ('title', )}
+ search_fields = ('title', 'excerpt', 'content', 'tags')
+ actions = ['make_mine', 'make_published', 'make_hidden',
+ 'close_comments', 'close_pingbacks',
+ 'ping_directories', 'make_tweet', 'put_on_top']
+ actions_on_top = True
+ actions_on_bottom = True
+
+ def __init__(self, model, admin_site):
+ self.form.admin_site = admin_site
+ super(ProcessAdmin, self).__init__(model, admin_site)
+
+ # Custom Display
+ def get_title(self, process):
+ """Return the title with word count and number of comments"""
+ title = _('%(title)s (%(word_count)i words)') % \
+ {'title': process.title, 'word_count': process.word_count}
+ comments = process.comments.count()
+ if comments:
+ return _('%(title)s (%(comments)i comments)') % \
+ {'title': title, 'comments': comments}
+ return title
+ get_title.short_description = _('title')
+
+ def get_authors(self, process):
+ """Return the authors in HTML"""
+ try:
+ authors = ['<a href="%s" target="blank">%s</a>' %
+ (reverse('objectapp_author_detail',
+ args=[author.username]),
+ author.username) for author in process.authors.all()]
+ except NoReverseMatch:
+ authors = [author.username for author in process.authors.all()]
+ return ', '.join(authors)
+ get_authors.allow_tags = True
+ get_authors.short_description = _('author(s)')
+
+ def get_objecttypes(self, process):
+ """Return the objecttypes linked in HTML"""
+ try:
+ objecttypes = ['<a href="%s" target="blank">%s</a>' %
+ (Objecttype.get_absolute_url(), Objecttype.title)
+ for Objecttype in process.objecttypes.all()]
+ except NoReverseMatch:
+ objecttypes = [Objecttype.title for Objecttype in
+ process.objecttypes.all()]
+ return ', '.join(objecttypes)
+ get_objecttypes.allow_tags = True
+ get_objecttypes.short_description = _('Objecttype(s)')
+
+ def get_tags(self, process):
+ """Return the tags linked in HTML"""
+ try:
+ return ', '.join(['<a href="%s" target="blank">%s</a>' %
+ (reverse('objectapp_tag_detail',
+ args=[tag.name]), tag.name)
+ for tag in Tag.objects.get_for_object(process)])
+ except NoReverseMatch:
+ return process.tags
+ get_tags.allow_tags = True
+ get_tags.short_description = _('tag(s)')
+
+ def get_sites(self, process):
+ """Return the sites linked in HTML"""
+ return ', '.join(
+ ['<a href="http://%(domain)s" target="blank">%(name)s</a>' %
+ site.__dict__ for site in process.sites.all()])
+ get_sites.allow_tags = True
+ get_sites.short_description = _('site(s)')
+
+ def get_comments_are_open(self, process):
+ """Admin wrapper for process.comments_are_open"""
+ return process.comments_are_open
+ get_comments_are_open.boolean = True
+ get_comments_are_open.short_description = _('comment enabled')
+
+ def get_is_actual(self, process):
+ """Admin wrapper for process.is_actual"""
+ return process.is_actual
+ get_is_actual.boolean = True
+ get_is_actual.short_description = _('is actual')
+
+ def get_is_visible(self, process):
+ """Admin wrapper for process.is_visible"""
+ return process.is_visible
+ get_is_visible.boolean = True
+ get_is_visible.short_description = _('is visible')
+
+ def get_link(self, process):
+ """Return a formated link to the process"""
+ return u'<a href="%s" target="blank">%s</a>' % (
+ process.get_absolute_url(), _('View'))
+ get_link.allow_tags = True
+ get_link.short_description = _('View on site')
+
+ def get_short_url(self, process):
+ """Return the short url in HTML"""
+ short_url = process.short_url
+ if not short_url:
+ return _('Unavailable')
+ return '<a href="%(url)s" target="blank">%(url)s</a>' % \
+ {'url': short_url}
+ get_short_url.allow_tags = True
+ get_short_url.short_description = _('short url')
+
+ # Custom Methods
+ def save_model(self, request, process, form, change):
+ """Save the authors, update time, make an excerpt"""
+ if not form.cleaned_data.get('excerpt') and process.status == PUBLISHED:
+ process.excerpt = truncate_words(strip_tags(process.content), 50)
+
+ if process.pk and not request.user.has_perm('objectapp.can_change_author'):
+ form.cleaned_data['authors'] = process.authors.all()
+
+ if not form.cleaned_data.get('authors'):
+ form.cleaned_data['authors'].append(request.user)
+
+ process.last_update = datetime.now()
+ process.save()
+
+ def queryset(self, request):
+ """Make special filtering by user permissions"""
+ queryset = super(ProcessAdmin, self).queryset(request)
+ if request.user.has_perm('objectapp.can_view_all'):
+ return queryset
+ return request.user.process.all()
+
+ def formfield_for_manytomany(self, db_field, request, **kwargs):
+ """Filters the disposable authors"""
+ if db_field.name == 'authors':
+ if request.user.has_perm('objectapp.can_change_author'):
+ kwargs['queryset'] = User.objects.filter(is_staff=True)
+ else:
+ kwargs['queryset'] = User.objects.filter(pk=request.user.pk)
+
+ return super(ProcessAdmin, self).formfield_for_manytomany(
+ db_field, request, **kwargs)
+
+ def get_actions(self, request):
+ """Define user actions by permissions"""
+ actions = super(ProcessAdmin, self).get_actions(request)
+ if not request.user.has_perm('objectapp.can_change_author') \
+ or not request.user.has_perm('objectapp.can_view_all'):
+ del actions['make_mine']
+ if not settings.PING_DIRECTORIES:
+ del actions['ping_directories']
+ if not settings.USE_TWITTER:
+ del actions['make_tweet']
+
+ return actions
+
+ # Custom Actions
+ def make_mine(self, request, queryset):
+ """Set the processesto the user"""
+ for process in queryset:
+ if request.user not in process.authors.all():
+ process.authors.add(request.user)
+ self.message_user(
+ request, _('The selected processes now belong to you.'))
+ make_mine.short_description = _('Set the processes to the user')
+
+ def make_published(self, request, queryset):
+ """Set processes selected as published"""
+ queryset.update(status=PUBLISHED)
+ self.ping_directories(request, queryset, messages=False)
+ self.message_user(
+ request, _('The selected processes are now marked as published.'))
+ make_published.short_description = _('Set processes selected as published')
+
+ def make_hidden(self, request, queryset):
+ """Set systems selected as hidden"""
+ queryset.update(status=HIDDEN)
+ self.message_user(
+ request, _('The selected systems are now marked as hidden.'))
+ make_hidden.short_description = _('Set systems selected as hidden')
+
+ def make_tweet(self, request, queryset):
+ """Post an update on Twitter"""
+ import tweepy
+ auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY,
+ settings.TWITTER_CONSUMER_SECRET)
+ auth.set_access_token(settings.TWITTER_ACCESS_KEY,
+ settings.TWITTER_ACCESS_SECRET)
+ api = tweepy.API(auth)
+ for process in queryset:
+ short_url = process.short_url
+ message = '%s %s' % (process.title[:139 - len(short_url)], short_url)
+ api.update_status(message)
+ self.message_user(
+ request, _('The selected systems have been tweeted.'))
+ make_tweet.short_description = _('Tweet processes selected')
+
+ def close_comments(self, request, queryset):
+ """Close the comments for selected processes """
+ queryset.update(comment_enabled=False)
+ self.message_user(
+ request, _('Comments are now closed for selected processes.'))
+ close_comments.short_description = _('Close the comments for '\
+ 'selected processes')
+
+ def close_pingbacks(self, request, queryset):
+ """Close the pingbacks for selected processes """
+ queryset.update(pingback_enabled=False)
+ self.message_user(
+ request, _('Linkbacks are now closed for selected processes.'))
+ close_pingbacks.short_description = _(
+ 'Close the linkbacks for selected processes ')
+
+ def put_on_top(self, request, queryset):
+ """Put the selected processes on top at the current date"""
+ queryset.update(creation_date=datetime.now())
+ self.ping_directories(request, queryset, messages=False)
+ self.message_user(request, _(
+ 'The selected processes are now set at the current date.'))
+ put_on_top.short_description = _(
+ 'Put the selected processes on top at the current date')
+
+ def ping_directories(self, request, queryset, messages=True):
+ """Ping Directories for selected processes """
+ for directory in settings.PING_DIRECTORIES:
+ pinger = DirectoryPinger(directory, queryset)
+ pinger.join()
+ if messages:
+ success = 0
+ for result in pinger.results:
+ if not result.get('flerror', True):
+ success += 1
+ else:
+ self.message_user(request,
+ '%s : %s' % (directory,
+ result['message']))
+ if success:
+ self.message_user(
+ request,
+ _('%(directory)s directory succesfully ' \
+ 'pinged %(success)d processes.') %
+ {'directory': directory, 'success': success})
+ ping_directories.short_description = _(
+ 'Ping Directories for selected processes')
+
+ def get_urls(self):
+ process_admin_urls = super(ProcessAdmin, self).get_urls()
+ urls = patterns(
+ 'django.views.generic.simple',
+ url(r'^autocomplete_tags/$', 'direct_to_template',
+ {'template': 'admin/objectapp/process/autocomplete_tags.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_process_autocomplete_tags'),
+ url(r'^wymeditor/$', 'direct_to_template',
+ {'template': 'admin/objectapp/process/wymeditor.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_process_wymeditor'),
+ url(r'^markitup/$', 'direct_to_template',
+ {'template': 'admin/objectapp/process/markitup.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_process_markitup'),)
+ return urls + process_admin_urls
+
+ def _media(self):
+ STATIC_URL = '%sobjectapp/' % project_settings.STATIC_URL
+ media = super(ProcessAdmin, self).media + Media(
+ css={'all': ('%scss/jquery.autocomplete.css' % STATIC_URL,)},
+ js=('%sjs/jquery.js' % STATIC_URL,
+ '%sjs/jquery.bgiframe.js' % STATIC_URL,
+ '%sjs/jquery.autocomplete.js' % STATIC_URL,
+ reverse('admin:objectapp_process_autocomplete_tags'),))
+
+ if settings.WYSIWYG == 'wymeditor':
+ media += Media(
+ js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % STATIC_URL,
+ '%sjs/wymeditor/plugins/hovertools/'
+ 'jquery.wymeditor.hovertools.js' % STATIC_URL,
+ reverse('admin:objectapp_process_wymeditor')))
+ elif settings.WYSIWYG == 'tinymce':
+ from tinymce.widgets import TinyMCE
+ media += TinyMCE().media + Media(
+ js=(reverse('tinymce-js', args=('admin/objectapp/process',)),))
+ elif settings.WYSIWYG == 'markitup':
+ media += Media(
+ js=('%sjs/markitup/jquery.markitup.js' % STATIC_URL,
+ '%sjs/markitup/sets/%s/set.js' % (
+ STATIC_URL, settings.MARKUP_LANGUAGE),
+ reverse('admin:objectapp_process_markitup')),
+ css={'all': (
+ '%sjs/markitup/skins/django/style.css' % STATIC_URL,
+ '%sjs/markitup/sets/%s/style.css' % (
+ STATIC_URL, settings.MARKUP_LANGUAGE))})
+ return media
+ media = property(_media)
diff --git a/objectapp/admin/system.py b/objectapp/admin/system.py
new file mode 100644
index 0000000..387a26c
--- /dev/null
+++ b/objectapp/admin/system.py
@@ -0,0 +1,356 @@
+"""GbobjectAdmin for Objectapp"""
+from datetime import datetime
+
+from django.forms import Media
+from django.contrib import admin
+from django.contrib.auth.models import User
+from django.utils.html import strip_tags
+from django.utils.text import truncate_words
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+from django.conf import settings as project_settings
+from django.utils.translation import ugettext_lazy as _
+from django.core.urlresolvers import reverse, NoReverseMatch
+import reversion
+from tagging.models import Tag
+
+from objectapp import settings
+from objectapp.managers import HIDDEN
+from objectapp.managers import PUBLISHED
+from objectapp.ping import DirectoryPinger
+from objectapp.admin.forms import SystemAdminForm
+
+
+class SystemAdmin(reversion.VersionAdmin):
+ """Admin for System model"""
+ form = SystemAdminForm
+ date_hierarchy = 'creation_date'
+ fieldsets = (
+ (_('Neighbourhood definition'), {'fields': ('title',
+ 'systemtypes',
+ 'gbobject_set',
+ 'relation_set',
+ 'attribute_set',
+ 'process_set',
+ 'system_set',
+ 'altnames' ,
+ 'slug',
+ 'status')}),
+
+ (_('Content'), {'fields': ('content', 'image',),
+ 'classes': ('collapse', 'collapse-closed')}),
+
+
+
+ (_('Dependency'), {'fields': ('prior_nodes', 'posterior_nodes',),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Options'), {'fields': ('featured', 'excerpt', 'template',
+ 'authors',
+ 'creation_date',
+ 'start_publication',
+ 'end_publication'),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Privacy'), {'fields': ('password', 'login_required',),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Publication'), {'fields': ('tags',
+ 'sites')}))
+ list_filter = ('objecttypes', 'authors', 'status', 'featured',
+ 'login_required', 'comment_enabled', 'pingback_enabled',
+ 'creation_date', 'start_publication',
+ 'end_publication', 'sites')
+ list_display = ('get_title', 'get_authors', 'get_objecttypes',
+ 'get_tags', 'get_sites',
+ 'get_comments_are_open', 'pingback_enabled',
+ 'get_is_actual', 'get_is_visible', 'get_link',
+ 'get_short_url', 'creation_date')
+ radio_fields = {'template': admin.VERTICAL}
+ filter_horizontal = ('objecttypes', 'authors')
+ prepopulated_fields = {'slug': ('title', )}
+ search_fields = ('title', 'excerpt', 'content', 'tags')
+ actions = ['make_mine', 'make_published', 'make_hidden',
+ 'close_comments', 'close_pingbacks',
+ 'ping_directories', 'make_tweet', 'put_on_top']
+ actions_on_top = True
+ actions_on_bottom = True
+
+ def __init__(self, model, admin_site):
+ self.form.admin_site = admin_site
+ super(SystemAdmin, self).__init__(model, admin_site)
+
+ # Custom Display
+ def get_title(self, system):
+ """Return the title with word count and number of comments"""
+ title = _('%(title)s (%(word_count)i words)') % \
+ {'title': system.title, 'word_count': system.word_count}
+ comments = system.comments.count()
+ if comments:
+ return _('%(title)s (%(comments)i comments)') % \
+ {'title': title, 'comments': comments}
+ return title
+ get_title.short_description = _('title')
+
+ def get_authors(self, system):
+ """Return the authors in HTML"""
+ try:
+ authors = ['<a href="%s" target="blank">%s</a>' %
+ (reverse('objectapp_author_detail',
+ args=[author.username]),
+ author.username) for author in system.authors.all()]
+ except NoReverseMatch:
+ authors = [author.username for author in system.authors.all()]
+ return ', '.join(authors)
+ get_authors.allow_tags = True
+ get_authors.short_description = _('author(s)')
+
+ def get_objecttypes(self, system):
+ """Return the objecttypes linked in HTML"""
+ try:
+ objecttypes = ['<a href="%s" target="blank">%s</a>' %
+ (Objecttype.get_absolute_url(), Objecttype.title)
+ for Objecttype in system.objecttypes.all()]
+ except NoReverseMatch:
+ objecttypes = [Objecttype.title for Objecttype in
+ system.objecttypes.all()]
+ return ', '.join(objecttypes)
+ get_objecttypes.allow_tags = True
+ get_objecttypes.short_description = _('Objecttype(s)')
+
+ def get_tags(self, system):
+ """Return the tags linked in HTML"""
+ try:
+ return ', '.join(['<a href="%s" target="blank">%s</a>' %
+ (reverse('objectapp_tag_detail',
+ args=[tag.name]), tag.name)
+ for tag in Tag.objects.get_for_object(system)])
+ except NoReverseMatch:
+ return system.tags
+ get_tags.allow_tags = True
+ get_tags.short_description = _('tag(s)')
+
+ def get_sites(self, system):
+ """Return the sites linked in HTML"""
+ return ', '.join(
+ ['<a href="http://%(domain)s" target="blank">%(name)s</a>' %
+ site.__dict__ for site in system.sites.all()])
+ get_sites.allow_tags = True
+ get_sites.short_description = _('site(s)')
+
+ def get_comments_are_open(self, system):
+ """Admin wrapper for system.comments_are_open"""
+ return system.comments_are_open
+ get_comments_are_open.boolean = True
+ get_comments_are_open.short_description = _('comment enabled')
+
+ def get_is_actual(self, system):
+ """Admin wrapper for system.is_actual"""
+ return system.is_actual
+ get_is_actual.boolean = True
+ get_is_actual.short_description = _('is actual')
+
+ def get_is_visible(self, system):
+ """Admin wrapper for system.is_visible"""
+ return system.is_visible
+ get_is_visible.boolean = True
+ get_is_visible.short_description = _('is visible')
+
+ def get_link(self, system):
+ """Return a formated link to the system"""
+ return u'<a href="%s" target="blank">%s</a>' % (
+ system.get_absolute_url(), _('View'))
+ get_link.allow_tags = True
+ get_link.short_description = _('View on site')
+
+ def get_short_url(self, system):
+ """Return the short url in HTML"""
+ short_url = system.short_url
+ if not short_url:
+ return _('Unavailable')
+ return '<a href="%(url)s" target="blank">%(url)s</a>' % \
+ {'url': short_url}
+ get_short_url.allow_tags = True
+ get_short_url.short_description = _('short url')
+
+ # Custom Methods
+ def save_model(self, request, system, form, change):
+ """Save the authors, update time, make an excerpt"""
+ if not form.cleaned_data.get('excerpt') and system.status == PUBLISHED:
+ system.excerpt = truncate_words(strip_tags(system.content), 50)
+
+ if system.pk and not request.user.has_perm('objectapp.can_change_author'):
+ form.cleaned_data['authors'] = system.authors.all()
+
+ if not form.cleaned_data.get('authors'):
+ form.cleaned_data['authors'].append(request.user)
+
+ system.last_update = datetime.now()
+ system.save()
+
+ def queryset(self, request):
+ """Make special filtering by user permissions"""
+ queryset = super(SystemAdmin, self).queryset(request)
+ if request.user.has_perm('objectapp.can_view_all'):
+ return queryset
+ return request.user.systems.all()
+
+ def formfield_for_manytomany(self, db_field, request, **kwargs):
+ """Filters the disposable authors"""
+ if db_field.name == 'authors':
+ if request.user.has_perm('objectapp.can_change_author'):
+ kwargs['queryset'] = User.objects.filter(is_staff=True)
+ else:
+ kwargs['queryset'] = User.objects.filter(pk=request.user.pk)
+
+ return super(SystemAdmin, self).formfield_for_manytomany(
+ db_field, request, **kwargs)
+
+ def get_actions(self, request):
+ """Define user actions by permissions"""
+ actions = super(SystemAdmin, self).get_actions(request)
+ if not request.user.has_perm('objectapp.can_change_author') \
+ or not request.user.has_perm('objectapp.can_view_all'):
+ del actions['make_mine']
+ if not settings.PING_DIRECTORIES:
+ del actions['ping_directories']
+ if not settings.USE_TWITTER:
+ del actions['make_tweet']
+
+ return actions
+
+ # Custom Actions
+ def make_mine(self, request, queryset):
+ """Set the systems to the user"""
+ for system in queryset:
+ if request.user not in system.authors.all():
+ system.authors.add(request.user)
+ self.message_user(
+ request, _('The selected systems now belong to you.'))
+ make_mine.short_description = _('Set the systems to the user')
+
+ def make_published(self, request, queryset):
+ """Set systems selected as published"""
+ queryset.update(status=PUBLISHED)
+ self.ping_directories(request, queryset, messages=False)
+ self.message_user(
+ request, _('The selected systems are now marked as published.'))
+ make_published.short_description = _('Set systems selected as published')
+
+ def make_hidden(self, request, queryset):
+ """Set systems selected as hidden"""
+ queryset.update(status=HIDDEN)
+ self.message_user(
+ request, _('The selected systems are now marked as hidden.'))
+ make_hidden.short_description = _('Set systems selected as hidden')
+
+ def make_tweet(self, request, queryset):
+ """Post an update on Twitter"""
+ import tweepy
+ auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY,
+ settings.TWITTER_CONSUMER_SECRET)
+ auth.set_access_token(settings.TWITTER_ACCESS_KEY,
+ settings.TWITTER_ACCESS_SECRET)
+ api = tweepy.API(auth)
+ for system in queryset:
+ short_url = system.short_url
+ message = '%s %s' % (system.title[:139 - len(short_url)], short_url)
+ api.update_status(message)
+ self.message_user(
+ request, _('The selected systems have been tweeted.'))
+ make_tweet.short_description = _('Tweet systems selected')
+
+ def close_comments(self, request, queryset):
+ """Close the comments for selected systems"""
+ queryset.update(comment_enabled=False)
+ self.message_user(
+ request, _('Comments are now closed for selected systems.'))
+ close_comments.short_description = _('Close the comments for '\
+ 'selected systems')
+
+ def close_pingbacks(self, request, queryset):
+ """Close the pingbacks for selected systems"""
+ queryset.update(pingback_enabled=False)
+ self.message_user(
+ request, _('Linkbacks are now closed for selected systems.'))
+ close_pingbacks.short_description = _(
+ 'Close the linkbacks for selected systems')
+
+ def put_on_top(self, request, queryset):
+ """Put the selected systems on top at the current date"""
+ queryset.update(creation_date=datetime.now())
+ self.ping_directories(request, queryset, messages=False)
+ self.message_user(request, _(
+ 'The selected systems are now set at the current date.'))
+ put_on_top.short_description = _(
+ 'Put the selected systems on top at the current date')
+
+ def ping_directories(self, request, queryset, messages=True):
+ """Ping Directories for selected systems"""
+ for directory in settings.PING_DIRECTORIES:
+ pinger = DirectoryPinger(directory, queryset)
+ pinger.join()
+ if messages:
+ success = 0
+ for result in pinger.results:
+ if not result.get('flerror', True):
+ success += 1
+ else:
+ self.message_user(request,
+ '%s : %s' % (directory,
+ result['message']))
+ if success:
+ self.message_user(
+ request,
+ _('%(directory)s directory succesfully ' \
+ 'pinged %(success)d systems.') %
+ {'directory': directory, 'success': success})
+ ping_directories.short_description = _(
+ 'Ping Directories for selected systems')
+
+ def get_urls(self):
+ system_admin_urls = super(SystemAdmin, self).get_urls()
+ urls = patterns(
+ 'django.views.generic.simple',
+ url(r'^autocomplete_tags/$', 'direct_to_template',
+ {'template': 'admin/objectapp/system/autocomplete_tags.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_system_autocomplete_tags'),
+ url(r'^wymeditor/$', 'direct_to_template',
+ {'template': 'admin/objectapp/system/wymeditor.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_system_wymeditor'),
+ url(r'^markitup/$', 'direct_to_template',
+ {'template': 'admin/objectapp/system/markitup.js',
+ 'mimetype': 'application/javascript'},
+ name='objectapp_system_markitup'),)
+ return urls + system_admin_urls
+
+ def _media(self):
+ STATIC_URL = '%sobjectapp/' % project_settings.STATIC_URL
+ media = super(SystemAdmin, self).media + Media(
+ css={'all': ('%scss/jquery.autocomplete.css' % STATIC_URL,)},
+ js=('%sjs/jquery.js' % STATIC_URL,
+ '%sjs/jquery.bgiframe.js' % STATIC_URL,
+ '%sjs/jquery.autocomplete.js' % STATIC_URL,
+ reverse('admin:objectapp_system_autocomplete_tags'),))
+
+ if settings.WYSIWYG == 'wymeditor':
+ media += Media(
+ js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % STATIC_URL,
+ '%sjs/wymeditor/plugins/hovertools/'
+ 'jquery.wymeditor.hovertools.js' % STATIC_URL,
+ reverse('admin:objectapp_system_wymeditor')))
+ elif settings.WYSIWYG == 'tinymce':
+ from tinymce.widgets import TinyMCE
+ media += TinyMCE().media + Media(
+ js=(reverse('tinymce-js', args=('admin/objectapp/system',)),))
+ elif settings.WYSIWYG == 'markitup':
+ media += Media(
+ js=('%sjs/markitup/jquery.markitup.js' % STATIC_URL,
+ '%sjs/markitup/sets/%s/set.js' % (
+ STATIC_URL, settings.MARKUP_LANGUAGE),
+ reverse('admin:objectapp_system_markitup')),
+ css={'all': (
+ '%sjs/markitup/skins/django/style.css' % STATIC_URL,
+ '%sjs/markitup/sets/%s/style.css' % (
+ STATIC_URL, settings.MARKUP_LANGUAGE))})
+ return media
+ media = property(_media)
diff --git a/objectapp/admin/widgets.py b/objectapp/admin/widgets.py
new file mode 100644
index 0000000..68b98d1
--- /dev/null
+++ b/objectapp/admin/widgets.py
@@ -0,0 +1,171 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Widgets for Objectapp admin"""
+from itertools import chain
+
+from django import forms
+from django.conf import settings
+from django.contrib.admin import widgets
+from django.utils.html import escape
+from django.utils.html import conditional_escape
+from django.utils.encoding import smart_unicode
+from django.utils.encoding import force_unicode
+
+
+class TreeNodeChoiceField(forms.ModelChoiceField):
+ """Duplicating the TreeNodeChoiceField bundled in django-mptt
+ to avoid conflict with the TreeNodeChoiceField bundled in django-cms..."""
+ def __init__(self, level_indicator=u'|--', *args, **kwargs):
+ self.level_indicator = level_indicator
+ if kwargs.get('required', True) and not 'empty_label' in kwargs:
+ kwargs['empty_label'] = None
+ super(TreeNodeChoiceField, self).__init__(*args, **kwargs)
+
+ def label_from_instance(self, obj):
+ """Creates labels which represent the tree level of each node
+ when generating option labels."""
+ return u'%s %s' % (self.level_indicator * getattr(
+ obj, obj._mptt_meta.level_attr), smart_unicode(obj))
+
+
+class MPTTModelChoiceIterator(forms.models.ModelChoiceIterator):
+ """MPTT version of ModelChoiceIterator"""
+ def choice(self, obj):
+ """Overriding choice method"""
+ tree_id = getattr(obj, self.queryset.model._mptt_meta.tree_id_attr, 0)
+ left = getattr(obj, self.queryset.model._mptt_meta.left_attr, 0)
+ return super(MPTTModelChoiceIterator,
+ self).choice(obj) + ((tree_id, left),)
+
+
+class MPTTModelMultipleChoiceField(forms.ModelMultipleChoiceField):
+ """MPTT version of ModelMultipleChoiceField"""
+ def __init__(self, level_indicator=u'|--', *args, **kwargs):
+ self.level_indicator = level_indicator
+ super(MPTTModelMultipleChoiceField, self).__init__(*args, **kwargs)
+
+ def label_from_instance(self, obj):
+ """Creates labels which represent the tree level of each node
+ when generating option labels."""
+ return u'%s %s' % (self.level_indicator * getattr(
+ obj, obj._mptt_meta.level_attr), smart_unicode(obj))
+
+ def _get_choices(self):
+ """Overriding _get_choices"""
+ if hasattr(self, '_choices'):
+ return self._choices
+ return MPTTModelChoiceIterator(self)
+
+ choices = property(_get_choices, forms.ChoiceField._set_choices)
+
+
+class MPTTFilteredSelectMultiple(widgets.FilteredSelectMultiple):
+ """MPTT version of FilteredSelectMultiple"""
+ def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
+ super(MPTTFilteredSelectMultiple, self).__init__(
+ verbose_name, is_stacked, attrs, choices)
+
+ def render_options(self, choices, selected_choices):
+ """
+ This is copy'n'pasted from django.forms.widgets Select(Widget)
+ change to the for loop and render_option so they will unpack
+ and use our extra tuple of mptt sort fields (if you pass in
+ some default choices for this field, make sure they have the
+ extra tuple too!)
+ """
+ def render_option(option_value, option_label, sort_fields):
+ """Inner scope render_option"""
+ option_value = force_unicode(option_value)
+ selected_html = (option_value in selected_choices) \
+ and u' selected="selected"' or ''
+ return u'<option value="%s" data-tree-id="%s" ' \
+ 'data-left-value="%s"%s>%s</option>' % (
+ escape(option_value),
+ sort_fields[0],
+ sort_fields[1],
+ selected_html,
+ conditional_escape(force_unicode(option_label)),
+ )
+ # Normalize to strings.
+ selected_choices = set([force_unicode(v) for v in selected_choices])
+ output = []
+ for option_value, option_label, sort_fields in chain(
+ self.choices, choices):
+ if isinstance(option_label, (list, tuple)):
+ output.append(u'<optgroup label="%s">' % escape(
+ force_unicode(option_value)))
+ for option in option_label:
+ output.append(render_option(*option))
+ output.append(u'</optgroup>')
+ else:
+ output.append(render_option(option_value, option_label,
+ sort_fields))
+ return u'\n'.join(output)
+
+ class Media:
+ """MPTTFilteredSelectMultiple's Media"""
+ js = (settings.ADMIN_MEDIA_PREFIX + 'js/core.js',
+ settings.STATIC_URL + 'objectapp/js/mptt_m2m_selectbox.js',
+ settings.ADMIN_MEDIA_PREFIX + 'js/SelectFilter2.js',)
diff --git a/objectapp/comparison.py b/objectapp/comparison.py
new file mode 100644
index 0000000..d79d78d
--- /dev/null
+++ b/objectapp/comparison.py
@@ -0,0 +1,93 @@
+"""Comparison tools for Objectapp
+Based on clustered_models app"""
+from math import sqrt
+
+from objectapp.settings import F_MIN
+from objectapp.settings import F_MAX
+
+
+def pearson_score(list1, list2):
+ """Compute the pearson score between 2 lists of vectors"""
+ sum1 = sum(list1)
+ sum2 = sum(list2)
+ sum_sq1 = sum([pow(l, 2) for l in list1])
+ sum_sq2 = sum([pow(l, 2) for l in list2])
+
+ prod_sum = sum([list1[i] * list2[i] for i in range(len(list1))])
+
+ num = prod_sum - (sum1 * sum2 / len(list1))
+ den = sqrt((sum_sq1 - pow(sum1, 2) / len(list1)) *
+ (sum_sq2 - pow(sum2, 2) / len(list2)))
+ if den == 0:
+ return 0.0
+ return 1.0 - num / den
+
+
+class ClusteredModel(object):
+ """Wrapper around Model class
+ building a dataset of instances"""
+
+ def __init__(self, queryset, fields=['id']):
+ self.fields = fields
+ self.queryset = queryset
+
+ def dataset(self):
+ """Generate a dataset with the queryset
+ and specified fields"""
+ dataset = {}
+ for item in self.queryset.filter():
+ dataset[item] = ' '.join([unicode(item.__dict__[field])
+ for field in self.fields])
+ return dataset
+
+
+class VectorBuilder(object):
+ """Build a list of vectors based on datasets"""
+
+ def __init__(self, queryset, fields):
+ self.key = ''
+ self.columns = []
+ self.dataset = {}
+ self.clustered_model = ClusteredModel(queryset, fields)
+ self.build_dataset()
+
+ def build_dataset(self):
+ """Generate whole dataset"""
+ data = {}
+ words_total = {}
+
+ model_data = self.clustered_model.dataset()
+ for instance, words in model_data.items():
+ words_item_total = {}
+ for word in words.split():
+ words_total.setdefault(word, 0)
+ words_item_total.setdefault(word, 0)
+ words_total[word] += 1
+ words_item_total[word] += 1
+ data[instance] = words_item_total
+
+ top_words = []
+ for word, count in words_total.items():
+ frequency = float(count) / len(data)
+ if frequency > F_MIN and frequency < F_MAX:
+ top_words.append(word)
+
+ self.dataset = {}
+ self.columns = top_words
+ for instance in data.keys():
+ self.dataset[instance] = [data[instance].get(word, 0)
+ for word in top_words]
+ self.key = self.generate_key()
+
+ def generate_key(self):
+ """Generate key for this list of vectors"""
+ return self.clustered_model.queryset.count()
+
+ def flush(self):
+ """Flush the dataset"""
+ if self.key != self.generate_key():
+ self.build_dataset()
+
+ def __call__(self):
+ self.flush()
+ return self.columns, self.dataset
diff --git a/objectapp/context_processors.py b/objectapp/context_processors.py
new file mode 100644
index 0000000..a764c78
--- /dev/null
+++ b/objectapp/context_processors.py
@@ -0,0 +1,7 @@
+"""Context Processors for Objectapp"""
+from objectapp import __version__
+
+
+def version(request):
+ """Adds version of Objectapp to the context"""
+ return {'OBJECTAPP_VERSION': __version__}
diff --git a/objectapp/feeds.py b/objectapp/feeds.py
new file mode 100644
index 0000000..6c75a34
--- /dev/null
+++ b/objectapp/feeds.py
@@ -0,0 +1,398 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Feeds for Objectapp"""
+from urlparse import urljoin
+from BeautifulSoup import BeautifulSoup
+
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import reverse
+from django.shortcuts import get_object_or_404
+from django.utils.feedgenerator import Atom1Feed
+from django.utils.translation import ugettext as _
+from django.contrib.syndication.views import Feed
+from django.core.urlresolvers import NoReverseMatch
+from django.core.exceptions import ObjectDoesNotExist
+
+from tagging.models import Tag
+from tagging.models import TaggedItem
+
+from objectapp.models import Gbobject
+from objectapp.settings import COPYRIGHT
+from objectapp.settings import PROTOCOL
+from objectapp.settings import FEEDS_FORMAT
+from objectapp.settings import FEEDS_MAX_ITEMS
+from objectapp.managers import gbobjects_published
+from objectapp.views.objecttypes import get_Objecttype_or_404
+from objectapp.templatetags.objectapp_tags import get_gravatar
+
+
+class ObjectappFeed(Feed):
+ """Base Feed for Objectapp"""
+ feed_copyright = COPYRIGHT
+
+ def __init__(self):
+ self.site = Site.objects.get_current()
+ self.site_url = '%s://%s' % (PROTOCOL, self.site.domain)
+ if FEEDS_FORMAT == 'atom':
+ self.feed_type = Atom1Feed
+ self.subtitle = self.description
+
+
+class GbobjectFeed(ObjectappFeed):
+ """Base Gbobject Feed"""
+ title_template = 'feeds/gbobject_title.html'
+ description_template = 'feeds/gbobject_description.html'
+
+ def item_pubdate(self, item):
+ """Publication date of an gbobject"""
+ return item.creation_date
+
+ def item_objecttypes(self, item):
+ """Gbobject's objecttypes"""
+ return [Objecttype.title for Objecttype in item.objecttypes.all()]
+
+ def item_author_name(self, item):
+ """Returns the first author of an gbobject"""
+ if item.authors.count():
+ self.item_author = item.authors.all()[0]
+ return self.item_author.username
+
+ def item_author_email(self, item):
+ """Returns the first author's email"""
+ return self.item_author.email
+
+ def item_author_link(self, item):
+ """Returns the author's URL"""
+ try:
+ author_url = reverse('objectapp_author_detail',
+ args=[self.item_author.username])
+ return self.site_url + author_url
+ except NoReverseMatch:
+ return self.site_url
+
+ def item_enclosure_url(self, item):
+ """Returns an image for enclosure"""
+ if item.image:
+ return item.image.url
+
+ img = BeautifulSoup(item.html_content).find('img')
+ if img:
+ return urljoin(self.site_url, img['src'])
+
+ def item_enclosure_length(self, item):
+ """Hardcoded enclosure length"""
+ return '100000'
+
+ def item_enclosure_mime_type(self, item):
+ """Hardcoded enclosure mimetype"""
+ return 'image/jpeg'
+
+
+class LatestGbobjects(GbobjectFeed):
+ """Feed for the latest gbobjects"""
+
+ def link(self):
+ """URL of latest gbobjects"""
+ return reverse('objectapp_gbobject_archive_index')
+
+ def items(self):
+ """Items are published gbobjects"""
+ return Gbobject.published.all()[:FEEDS_MAX_ITEMS]
+
+ def title(self):
+ """Title of the feed"""
+ return '%s - %s' % (self.site.name, _('Latest gbobjects'))
+
+ def description(self):
+ """Description of the feed"""
+ return _('The latest gbobjects for the site %s') % self.site.name
+
+
+class ObjecttypeGbobjects(GbobjectFeed):
+ """Feed filtered by a Objecttype"""
+
+ def get_object(self, request, path):
+ """Retrieve the Objecttype by his path"""
+ return get_Objecttype_or_404(path)
+
+ def items(self, obj):
+ """Items are the published gbobjects of the Objecttype"""
+ return obj.gbobjects_published()[:FEEDS_MAX_ITEMS]
+
+ def link(self, obj):
+ """URL of the Objecttype"""
+ return obj.get_absolute_url()
+
+ def title(self, obj):
+ """Title of the feed"""
+ return _('Gbobjects for the Objecttype %s') % obj.title
+
+ def description(self, obj):
+ """Description of the feed"""
+ return _('The latest gbobjects for the Objecttype %s') % obj.title
+
+
+class AuthorGbobjects(GbobjectFeed):
+ """Feed filtered by an author"""
+
+ def get_object(self, request, username):
+ """Retrieve the author by his username"""
+ return get_object_or_404(User, username=username)
+
+ def items(self, obj):
+ """Items are the published gbobjects of the author"""
+ return gbobjects_published(obj.gbobjects)[:FEEDS_MAX_ITEMS]
+
+ def link(self, obj):
+ """URL of the author"""
+ return reverse('objectapp_author_detail', args=[obj.username])
+
+ def title(self, obj):
+ """Title of the feed"""
+ return _('Gbobjects for author %s') % obj.username
+
+ def description(self, obj):
+ """Description of the feed"""
+ return _('The latest gbobjects by %s') % obj.username
+
+
+class TagGbobjects(GbobjectFeed):
+ """Feed filtered by a tag"""
+
+ def get_object(self, request, slug):
+ """Retrieve the tag by his name"""
+ return get_object_or_404(Tag, name=slug)
+
+ def items(self, obj):
+ """Items are the published gbobjects of the tag"""
+ return TaggedItem.objects.get_by_model(
+ Gbobject.published.all(), obj)[:FEEDS_MAX_ITEMS]
+
+ def link(self, obj):
+ """URL of the tag"""
+ return reverse('objectapp_tag_detail', args=[obj.name])
+
+ def title(self, obj):
+ """Title of the feed"""
+ return _('Gbobjects for the tag %s') % obj.name
+
+ def description(self, obj):
+ """Description of the feed"""
+ return _('The latest gbobjects for the tag %s') % obj.name
+
+
+class SearchGbobjects(GbobjectFeed):
+ """Feed filtered by a search pattern"""
+
+ def get_object(self, request):
+ """The GET parameter 'pattern' is the object"""
+ pattern = request.GET.get('pattern', '')
+ if len(pattern) < 3:
+ raise ObjectDoesNotExist
+ return pattern
+
+ def items(self, obj):
+ """Items are the published gbobjects founds"""
+ return Gbobject.published.search(obj)[:FEEDS_MAX_ITEMS]
+
+ def link(self, obj):
+ """URL of the search request"""
+ return '%s?pattern=%s' % (reverse('objectapp_gbobject_search'), obj)
+
+ def title(self, obj):
+ """Title of the feed"""
+ return _("Results of the search for '%s'") % obj
+
+ def description(self, obj):
+ """Description of the feed"""
+ return _("The gbobjects containing the pattern '%s'") % obj
+
+
+class GbobjectDiscussions(ObjectappFeed):
+ """Feed for discussions in an gbobject"""
+ title_template = 'feeds/discussion_title.html'
+ description_template = 'feeds/discussion_description.html'
+
+ def get_object(self, request, year, month, day, slug):
+ """Retrieve the discussions by gbobject's slug"""
+ return get_object_or_404(Gbobject.published, slug=slug,
+ creation_date__year=year,
+ creation_date__month=month,
+ creation_date__day=day)
+
+ def items(self, obj):
+ """Items are the discussions on the gbobject"""
+ return obj.discussions[:FEEDS_MAX_ITEMS]
+
+ def item_pubdate(self, item):
+ """Publication date of a discussion"""
+ return item.submit_date
+
+ def item_link(self, item):
+ """URL of the discussion"""
+ return item.get_absolute_url()
+
+ def link(self, obj):
+ """URL of the gbobject"""
+ return obj.get_absolute_url()
+
+ def item_author_name(self, item):
+ """Author of the discussion"""
+ return item.userinfo['name']
+
+ def item_author_email(self, item):
+ """Author's email of the discussion"""
+ return item.userinfo['email']
+
+ def item_author_link(self, item):
+ """Author's URL of the discussion"""
+ return item.userinfo['url']
+
+ def title(self, obj):
+ """Title of the feed"""
+ return _('Discussions on %s') % obj.title
+
+ def description(self, obj):
+ """Description of the feed"""
+ return _('The latest discussions for the gbobject %s') % obj.title
+
+
+class GbobjectComments(GbobjectDiscussions):
+ """Feed for comments in an gbobject"""
+ title_template = 'feeds/comment_title.html'
+ description_template = 'feeds/comment_description.html'
+
+ def items(self, obj):
+ """Items are the comments on the gbobject"""
+ return obj.comments[:FEEDS_MAX_ITEMS]
+
+ def item_link(self, item):
+ """URL of the comment"""
+ return item.get_absolute_url('#comment_%(id)s')
+
+ def title(self, obj):
+ """Title of the feed"""
+ return _('Comments on %s') % obj.title
+
+ def description(self, obj):
+ """Description of the feed"""
+ return _('The latest comments for the gbobject %s') % obj.title
+
+ def item_enclosure_url(self, item):
+ """Returns a gravatar image for enclosure"""
+ return get_gravatar(item.userinfo['email'])
+
+ def item_enclosure_length(self, item):
+ """Hardcoded enclosure length"""
+ return '100000'
+
+ def item_enclosure_mime_type(self, item):
+ """Hardcoded enclosure mimetype"""
+ return 'image/jpeg'
+
+
+class GbobjectPingbacks(GbobjectDiscussions):
+ """Feed for pingbacks in an gbobject"""
+ title_template = 'feeds/pingback_title.html'
+ description_template = 'feeds/pingback_description.html'
+
+ def items(self, obj):
+ """Items are the pingbacks on the gbobject"""
+ return obj.pingbacks[:FEEDS_MAX_ITEMS]
+
+ def item_link(self, item):
+ """URL of the pingback"""
+ return item.get_absolute_url('#pingback_%(id)s')
+
+ def title(self, obj):
+ """Title of the feed"""
+ return _('Pingbacks on %s') % obj.title
+
+ def description(self, obj):
+ """Description of the feed"""
+ return _('The latest pingbacks for the gbobject %s') % obj.title
+
+
+class GbobjectTrackbacks(GbobjectDiscussions):
+ """Feed for trackbacks in an gbobject"""
+ title_template = 'feeds/trackback_title.html'
+ description_template = 'feeds/trackback_description.html'
+
+ def items(self, obj):
+ """Items are the trackbacks on the gbobject"""
+ return obj.trackbacks[:FEEDS_MAX_ITEMS]
+
+ def item_link(self, item):
+ """URL of the trackback"""
+ return item.get_absolute_url('#trackback_%(id)s')
+
+ def title(self, obj):
+ """Title of the feed"""
+ return _('Trackbacks on %s') % obj.title
+
+ def description(self, obj):
+ """Description of the feed"""
+ return _('The latest trackbacks for the gbobject %s') % obj.title
diff --git a/objectapp/fixtures/helloworld.json b/objectapp/fixtures/helloworld.json
new file mode 100644
index 0000000..5ac204e
--- /dev/null
+++ b/objectapp/fixtures/helloworld.json
@@ -0,0 +1,129 @@
+[
+ {
+ "pk": 2,
+ "model": "objectapp.Objecttype",
+ "fields": {
+ "rght": 2,
+ "description": "All gbobjects containing tests.",
+ "parent": null,
+ "slug": "tests",
+ "title": "Tests",
+ "level": 0,
+ "lft": 1,
+ "tree_id": 1
+ }
+ },
+ {
+ "pk": 1,
+ "model": "objectapp.Objecttype",
+ "fields": {
+ "rght": 2,
+ "description": "All uncategorized gbobjects.",
+ "parent": null,
+ "slug": "uncategorized",
+ "title": "Uncategorized",
+ "level": 0,
+ "lft": 1,
+ "tree_id": 2
+ }
+ },
+ {
+ "pk": 1,
+ "model": "objectapp.gbobject",
+ "fields": {
+ "status": 2,
+ "login_required": false,
+ "password": "",
+ "end_publication": "2042-12-31 00:00:00",
+ "comment_enabled": true,
+ "tags": "welcome, blog, test",
+ "image": "",
+ "title": "Hello World !",
+ "excerpt": "Welcome to the Objectapp blogging application written in Django.This is your first gbobject, don't hesite to archive or remove it.Or you can simply add a new gbobject.",
+ "sites": [
+ 1
+ ],
+ "related": [
+ 3
+ ],
+ "creation_date": "2010-01-01 00:00:00",
+ "content": "<p>Welcome to the Objectapp blogging application written in Django.</p><p>This is your first gbobject, don't hesite to <a title=\"Edit this gbobject.\" href=\"/admin/objectapp/gbobject/1/\">archive or remove it</a>.</p><p>Or you can simply <a title=\"Add a new gbobject.\" href=\"/admin/objectapp/gbobject/add/\">add a new gbobject</a>.</p>",
+ "start_publication": "2010-01-01 00:00:00",
+ "authors": [
+ 1
+ ],
+ "last_update": "2010-04-20 15:12:49",
+ "slug": "hello-world",
+ "objecttypes": [
+ 1
+ ]
+ }
+ },
+ {
+ "pk": 3,
+ "model": "objectapp.gbobject",
+ "fields": {
+ "status": 2,
+ "login_required": false,
+ "password": "",
+ "end_publication": "2042-12-31 00:00:00",
+ "comment_enabled": true,
+ "tags": "blog, test",
+ "image": "",
+ "title": "Lorem ipsum",
+ "excerpt": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut commodo mi at massa iaculis at facilisis sem viverra. Aliquam eget odio turpis. Sed luctus, diam sed vehicula auctor, orci tortor auctor turpis, quis bibendum enim odio sed neque. Sed eu justo vitae orci placerat suscipit eu tempus ligula. Ut condimentum, ...",
+ "sites": [
+ 1
+ ],
+ "related": [
+ 1,
+ 2
+ ],
+ "creation_date": "2009-03-15 00:00:00",
+ "content": "<div id=\"lipsum\"><p>\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Ut commodo mi\r\nat massa iaculis at facilisis sem viverra. Aliquam eget odio turpis.\r\nSed luctus, diam sed vehicula auctor, orci tortor auctor turpis, quis\r\nbibendum enim odio sed neque. Sed eu justo vitae orci placerat suscipit\r\neu tempus ligula. Ut condimentum, odio at dignissim egestas, est tortor\r\nlobortis dolor, vel feugiat massa magna non magna. Sed sit amet quam a\r\nturpis tristique auctor. Vestibulum nec sapien sed enim congue varius.\r\nUt vulputate adipiscing neque, a egestas purus euismod nec. Donec\r\nauctor nunc ac massa mattis quis commodo tortor luctus. Integer id sem\r\nnec nisl iaculis imperdiet. Vestibulum ante ipsum primis in faucibus\r\norci luctus et ultrices posuere cubilia Curae; Mauris bibendum augue\r\nvitae nisi volutpat a tempor purus sodales. Nunc quis justo nunc, at\r\ntristique quam. Fusce vitae nibh tellus, quis consequat nisl. Fusce mi\r\nnulla, sollicitudin a auctor et, posuere nec leo. Curabitur vehicula\r\nvelit vel lacus condimentum eget ullamcorper eros faucibus. Curabitur\r\nposuere, magna non rhoncus aliquet, ante sem cursus nibh, a mattis est\r\nlorem et lorem.\r\n</p><p>Nullam metus ligula, sagittis eu consectetur in, pharetra non purus.\r\nInteger non porta tortor. Fusce fringilla mi eget nibh sodales viverra.\r\nVivamus erat urna, pretium eu ornare non, sollicitudin eget urna.\r\nPraesent justo enim, hendrerit at porttitor in, adipiscing vel nulla.\r\nVivamus nunc nisl, fermentum eget tempor nec, convallis sed justo.\r\nPellentesque vel imperdiet leo. Class aptent taciti sociosqu ad litora\r\ntorquent per conubia nostra, per inceptos himenaeos. Etiam lacinia erat\r\nnon nisi sagittis eget mattis dui dictum. Donec id lorem eu quam\r\nfacilisis ultricies. Ut et dolor porta lectus adipiscing aliquam vel at\r\nodio. In hac habitasse platea dictumst. Sed mollis lacus tincidunt\r\nmassa placerat sed tincidunt tortor interdum. Nunc vulputate placerat\r\nnunc eget venenatis. In aliquam rutrum nulla, sed aliquet dolor ornare\r\ninterdum. Etiam fermentum mollis cursus.\r\n</p></div>",
+ "start_publication": "2010-01-01 00:00:00",
+ "authors": [
+ 1
+ ],
+ "last_update": "2010-04-20 15:26:02",
+ "slug": "lorem-ipsum",
+ "objecttypes": [
+ 2,
+ 1
+ ]
+ }
+ },
+ {
+ "pk": 2,
+ "model": "objectapp.gbobject",
+ "fields": {
+ "status": 2,
+ "login_required": false,
+ "password": "",
+ "end_publication": "2042-12-31 00:00:00",
+ "comment_enabled": true,
+ "tags": "html, test, ",
+ "image": "",
+ "title": "Testing HTML elements",
+ "excerpt": "The purpose of this HTML is to help determine what default settings are with CSS and to make sure that all possible HTML Elements are included in this HTML so as to not miss any possible Elements when designing a site.Heading 1Heading 2Heading 3Heading 4Heading 5Heading 6ParagraphLorem ipsum dolor sit ...",
+ "sites": [
+ 1
+ ],
+ "related": [
+ 3
+ ],
+ "creation_date": "2009-01-01 00:00:00",
+ "content": "<p>The\r\npurpose of this HTML is to help determine what default settings are\r\nwith CSS and to make sure that all possible HTML Elements are included\r\nin this HTML so as to not miss any possible Elements when designing a\r\nsite.</p><hr /><h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3><h4>Heading 4</h4><h5>Heading 5</h5><h6>Heading 6</h6><hr /><h2 id=\"paragraph\">Paragraph</h2><p>Lorem ipsum dolor sit amet, <a href=\"http://wp-themes.com/?p=36#\" title=\"test link\">test link</a>\r\nadipiscing elit. Nullam dignissim convallis est. Quisque aliquam. Donec\r\nfaucibus. Nunc iaculis suscipit dui. Nam sit amet sem. Aliquam libero\r\nnisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. Praesent\r\nmattis, massa quis luctus fermentum, turpis mi volutpat justo, eu\r\nvolutpat enim diam eget metus. Maecenas ornare tortor. Donec sed tellus\r\neget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem,\r\nconsequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue\r\nquis tellus.</p><p>Lorem ipsum dolor sit amet, <em>emphasis</em> consectetuer\r\nadipiscing elit. Nullam dignissim convallis est. Quisque aliquam. Donec\r\nfaucibus. Nunc iaculis suscipit dui. Nam sit amet sem. Aliquam libero\r\nnisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. Praesent\r\nmattis, massa quis luctus fermentum, turpis mi volutpat justo, eu\r\nvolutpat enim diam eget metus. Maecenas ornare tortor. Donec sed tellus\r\neget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem,\r\nconsequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue\r\nquis tellus.</p><hr /><h2 id=\"list_types\">List Types</h2><h3>Definition List</h3><dl><dt>Definition List Title</dt><dd>This is a definition list division.</dd></dl><h3>Ordered List</h3><ol><li>List Item 1</li><li>List Item 2</li><li>List Item 3</li></ol><h3>Unordered List</h3><ul><li>List Item 1</li><li>List Item 2</li><li>List Item 3</li></ul><hr /><h2 id=\"form_elements\">Forms</h2><fieldset><legend>Legend</legend><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam\r\ndignissim convallis est. Quisque aliquam. Donec faucibus. Nunc iaculis\r\nsuscipit dui. Nam sit amet sem. Aliquam libero nisi, imperdiet at,\r\ntincidunt nec, gravida vehicula, nisl. Praesent mattis, massa quis\r\nluctus fermentum, turpis mi volutpat justo, eu volutpat enim diam eget\r\nmetus.</p><form><h2>Form Element</h2><p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam\r\ndignissim convallis est. Quisque aliquam. Donec faucibus. Nunc iaculis\r\nsuscipit dui.</p><p><label for=\"text_field\">Text Field:</label></p><input id=\"text_field\" type=\"text\" /><p><label for=\"text_area\">Text Area:</label><br /><textarea id=\"text_area\"></textarea></p><p><label for=\"select_element\">Select Element:</label></p><select name=\"select_element\"><optgroup label=\"Option Group 1\"></optgroup><option value=\"1\">Option 1</option><option value=\"2\">Option 2</option><option value=\"3\">Option 3</option><optgroup label=\"Option Group 2\"></optgroup><option value=\"1\">Option 1</option><option value=\"2\">Option 2</option><option value=\"3\">Option 3</option></select><p><label for=\"radio_buttons\">Radio Buttons:</label></p><input class=\"radio\" name=\"radio_button\" value=\"radio_1\" type=\"radio\" /> Radio 1<br /><input class=\"radio\" name=\"radio_button\" value=\"radio_2\" type=\"radio\" /> Radio 2<br /><input class=\"radio\" name=\"radio_button\" value=\"radio_3\" type=\"radio\" /> Radio 3<br /><p><label for=\"checkboxes\">Checkboxes:</label></p><input class=\"checkbox\" name=\"checkboxes\" value=\"check_1\" type=\"checkbox\" /> Radio 1<br /><input class=\"checkbox\" name=\"checkboxes\" value=\"check_2\" type=\"checkbox\" /> Radio 2<br /><input class=\"checkbox\" name=\"checkboxes\" value=\"check_3\" type=\"checkbox\" /> Radio 3<br /><p><label for=\"password\">Password:</label></p><input class=\"password\" name=\"password\" type=\"password\" /><p><label for=\"file\">File Input:</label></p><input class=\"file\" name=\"file\" type=\"file\" /><p><input class=\"button\" value=\"Clear\" type=\"reset\" /><input class=\"button\" value=\"Submit\" type=\"submit\" /></p></form></fieldset><hr /><h2 id=\"tables\">Tables</h2><table><tbody><tr><th>Table Header 1</th><th>Table Header 2</th><th>Table Header 3</th></tr><tr><td>Division 1</td><td>Division 2</td><td>Division 3</td></tr><tr class=\"even\"><td>Division 1</td><td>Division 2</td><td>Division 3</td></tr><tr><td>Division 1</td><td>Division 2</td><td>Division 3</td></tr></tbody></table><hr /><h2 id=\"misc\">Misc Stuff \u2013 abbr, acronym, pre, code, sub, sup, etc.</h2><p>Lorem <sup>superscript</sup> dolor <sub>subscript</sub> amet, consectetuer adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. <cite>cite</cite>.\r\nNunc iaculis suscipit dui. Nam sit amet sem. Aliquam libero nisi,\r\nimperdiet at, tincidunt nec, gravida vehicula, nisl. Praesent mattis,\r\nmassa quis luctus fermentum, turpis mi volutpat justo, eu volutpat enim\r\ndiam eget metus. Maecenas ornare tortor. Donec sed tellus eget sapien\r\nfringilla nonummy. <acronym title=\"National Basketball Association\">NBA</acronym> Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. <abbr title=\"Avenue\">AVE</abbr></p><pre>Lorem ipsum dolor sit amet,<br /> consectetuer adipiscing elit.<br /> Nullam dignissim convallis est.<br /> Quisque aliquam. Donec faucibus.<br />Nunc iaculis suscipit dui.<br />Nam sit amet sem.<br />Aliquam libero nisi, imperdiet at,<br /> tincidunt nec, gravida vehicula,<br /> nisl.<br />Praesent mattis, massa quis<br />luctus fermentum, turpis mi<br />volutpat justo, eu volutpat<br />enim diam eget metus.<br />Maecenas ornare tortor.<br />Donec sed tellus eget sapien<br /> fringilla nonummy.<br /><acronym title=\"National Basketball Association\">NBA</acronym>\r\nMauris a ante. Suspendisse\r\n quam sem, consequat at,\r\ncommodo vitae, feugiat in,\r\nnunc. Morbi imperdiet augue\r\n quis tellus.\r\n<abbr title=\"Avenue\">AVE</abbr></pre><blockquote><p>\r\n\t\u201cThis stylesheet is going to help so freaking much.\u201d <br />-Blockquote\r\n</p></blockquote>\r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n ",
+ "start_publication": "2010-01-01 00:00:00",
+ "authors": [
+ 1
+ ],
+ "last_update": "2010-04-20 15:26:38",
+ "slug": "testing-html-elements",
+ "objecttypes": [
+ 2,
+ 1
+ ]
+ }
+ }
+]
diff --git a/objectapp/forms.py b/objectapp/forms.py
new file mode 100644
index 0000000..6f9df1a
--- /dev/null
+++ b/objectapp/forms.py
@@ -0,0 +1,35 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from objectapp.models import *
+from django.forms import ModelForm
+
+class GbobjectForm(ModelForm):
+
+ class Meta:
+ model = Gbobject
+
+class ProcessForm(ModelForm):
+
+ class Meta:
+ model = Process
+
+class SystemForm(ModelForm):
+
+ class Meta:
+ model = System
+
diff --git a/objectapp/locale/de/LC_MESSAGES/django.mo b/objectapp/locale/de/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..559f9ca
--- /dev/null
+++ b/objectapp/locale/de/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/de/LC_MESSAGES/django.po b/objectapp/locale/de/LC_MESSAGES/django.po
new file mode 100644
index 0000000..1a1c281
--- /dev/null
+++ b/objectapp/locale/de/LC_MESSAGES/django.po
@@ -0,0 +1,1200 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Jannis Leidel <jannis@leidel.info>, 2011.
+# Julien Fache <fantomas42@gmail.com>, 2011.
+# madcad <mail@mad-cad.net>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-12 18:21+0000\n"
+"Last-Translator: Fantomas42 <fantomas42@gmail.com>\n"
+"Language-Team: German (http://www.transifex.net/projects/p/django-blog-objectapp/team/de/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: de\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "Neuste Einträge"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "Die neusten Einträge der Seite %s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "Einträge der Kategorie %s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr "Die neusten Einträge in Kategorie %s"
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "Einträge für Autor %s"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr "Die neuesten Einträge von %s"
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "Einträge für den Tag %s"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr "Die neuen Einträge für den Tag %s"
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr ""
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr ""
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr "Kommentare auf %s"
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr "Die neusten Kommentare für die Einträge %s"
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Kommentar für %s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr "Die neusten Kommentare für die Einträge %s"
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr "Pingbacks für %s"
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr "Die neusten Pingbacks für Eintrag %s"
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr "Trackbacks für %s"
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr "Die neusten Trackbacks für Eintrag %s"
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "Titel"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "Für die Veröffentlichung benutzt"
+
+#: models.py:67
+msgid "description"
+msgstr "Beschreibung"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "Ãœbergeordnete Kategorie"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "Kategorie"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "Kategorien"
+
+#: models.py:101
+msgid "draft"
+msgstr "Entwurf"
+
+#: models.py:102
+msgid "hidden"
+msgstr "Versteckt"
+
+#: models.py:103
+msgid "published"
+msgstr "Veröffentlicht"
+
+#: models.py:107
+msgid "image"
+msgstr "Bild"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "Als Abbildung benutzt"
+
+#: models.py:109
+msgid "content"
+msgstr "Inhalt"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "Auszug"
+
+#: models.py:111
+msgid "optional element"
+msgstr "Optionales Element"
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "Tags"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "Verwandte Einträge"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "Autoren"
+
+#: models.py:126
+msgid "featured"
+msgstr "empfohlenen"
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "Kommentare aktiviert"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr "Linkback aktiviert"
+
+#: models.py:130
+msgid "creation date"
+msgstr "Erstellungsdatum"
+
+#: models.py:131
+msgid "last update"
+msgstr "Letzte Änderung"
+
+#: models.py:132
+msgid "start publication"
+msgstr "Veröffentlichungsbeginn"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "Datum der ersten Veröffentlichung"
+
+#: models.py:135
+msgid "end publication"
+msgstr "Veröffentlichungsende"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "Datum des Veröffentlichungsende"
+
+#: models.py:139
+msgid "sites publication"
+msgstr "Veröffentlich auf Seiten"
+
+#: models.py:141
+msgid "login required"
+msgstr "Anmeldung erfolderlich"
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr "Nur authentifizierte Benutzer können den Eintrag lesen "
+
+#: models.py:143
+msgid "password"
+msgstr "Passwort"
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr "Eintrag mit Passwort schützen"
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr "Template"
+
+#: models.py:149
+msgid "Default template"
+msgstr "Standard-Template"
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr "Template, mit dem dieser Eintrag dargestellt wird"
+
+#: models.py:285
+msgid "gbobject"
+msgstr "Eintrag"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "Einträge"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr "[%(site)s] Neuer Kommentar zu \"%(title)s\""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr "Pfad"
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "Inhalt"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "Optionen"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr "Privatsphäre"
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr "Diskussion"
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr "Veröffentlichung"
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr "%(title)s (%(word_count)i Wörter)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr "%(title)s (%(comments)i Kommentare)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "Autor(en)"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "Kategorie(n)"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "Tag(s)"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "Seite(n)"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "Ist aktuell"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "Ist sichtbar"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr ""
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "Im Browser öffnen"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "Nicht verfügbar"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "Kurze URL"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr ""
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "Ausgewählte Einträge dem aktuellem Benutzer zuordnen"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr ""
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "Ausgewählte Einträge als öffentlich markieren"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr ""
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "Ausgewählte Einträge als unsichtbar markieren"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr ""
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "Ausgewählte Einträge twittern"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "Kommentare für ausgewählte Einträge deaktivieren"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr "Schließen der Linkbacks für die ausgewählte Einträge"
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr ""
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr "Setzen Sie die ausgewählten Einträge auf das aktuelle Datum"
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr ""
+"%(directory)s Verzeichnis erfolgreich angepingt %(success)d Einträge."
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "Verzeichnisdienste mit ausgewählten Einträgen anpingen"
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr "Keine Hauptkategorie"
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr "Eine Kategorie kann auf selbst sein referenzieren."
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "Kategorien"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr "Objectapp App Hook"
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "Sortierung"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr "Fortgeschritten"
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "Ausgewählte Einträge"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "Zufällige Einträge"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr "Objectapp Eintragsmenü"
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr "Objectapp Kategorie Menü"
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr "Objectapp Autor Menü"
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "Autoren"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr "Objectapp Tag Menu"
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "Tags"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr "Liste der Einträge (Standard)"
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr "Eintragsdetail"
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr "Unterkategorien mit einbeziehen"
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "Anzahl der Einträge"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr "Template zur Anzeige des Plugins"
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s Einträge"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr "Fehler 404"
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr "Seite nicht gefunden"
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr ""
+"Entschuldigung, aber die angeforderte Seite konnte nicht gefunden werden."
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr "Nützliche Links"
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr "Blog Index"
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr "Sitemap"
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "Neuste Einträge"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "Suche"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "Schlüsselwörter..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr "Fehler 500"
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr "Server-Fehler"
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+"Es ist ein Fehler aufgetreten. Der Server-Admin wurde umgehend benachrichtig"
+" und wird sich in Kürze um den Fehler kümmern. Wir danken für Ihr "
+"Verständnis."
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr "Inhalt"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr "Diskussionen"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr " %(gbobjects)s Einträge"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr "%(comments)s Kommentare"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr " %(objecttypes)s Kategorien"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr " %(pingbacks)s Pingbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr " %(tags)s Tags"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr " %(trackbacks)s Trackbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr " %(authors)s Autoren"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr " %(rejects)s abgelehnt"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr "Eintrag bearbeiten"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "am"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "Vorschau"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr "Kein Entwurfs Einträge."
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr "Alle Eintragsentwürfe anziegen"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "in"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr "Ihre Meinung zu"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr "Kommentar bearbeiten"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr "Bearbeiten"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr "Keine Kommentare vorhanden."
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr "Verwalten Sie die Kommentare"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr "mache eine LinkBack auf"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr "Bearbeiten Sie den LinkBack"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr "Keine Linkbacks."
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr "Heute"
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr "Eintragsentwürfe"
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr "Schnelles Veröffentlichen"
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr "Titel"
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr "Als Entwurf speichern"
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr "Zurücksetzen"
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr "Veröffentlichen"
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr "Neueste Kommentare"
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr "Aktuelle Linkbacks"
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Autor"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr ""
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "Kommentarvorschau"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] "Bitte korrigieren Sie folgenden Fehler."
+msgstr[1] "Bitte korrigieren Sie folgende Fehler."
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "Kommentarvorschau einblenden"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "Kommentar schreiben"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "Abschicken"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "Danke für Ihren Kommentar"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "Zur Liste der Einträge zurückkehren"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "Geschrieben von"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "Zeige Einträge von %(author)s"
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "Keine Tags"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "Kurze URL"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] " %(comment_count)s Kommentar"
+msgstr[1] " %(comment_count)s Kommentare"
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr "Schreiben den ersten Kommentar!"
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "Kommentare deaktiviert."
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] " %(pingback_count)s Pingback"
+msgstr[1] " %(pingback_count)s Pingbacks"
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] " %(trackback_count)s Trackback"
+msgstr[1] " %(trackback_count)s Trackbacks"
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "Autorenliste"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] " %(gbobject_count)s Eintrag"
+msgstr[1] " %(gbobject_count)s Einträge"
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+"Sie können, um Worte oder Sätze, \"Anführungszeichen\" für genaue Sätze "
+"und/oder die UND / ODER boolschen Operatoren mit Klammern für komplexe Suche"
+" kombiniert verwenden."
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr "Kalender"
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "Beliebte Einträge"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "Archiv"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr "Werkzeuge"
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr "Dashboard"
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr "Schreibe einen Eintrag"
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr "Abmelden"
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "Kategorieliste"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr "Tagesarchiv"
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr "Monatliche Archive"
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr "RSS-Feed der Diskussionen auf"
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "RSS-Feed mit Kommentaren zu"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr "RSS-Feed auf den Pingbacks"
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr "RSS-Feed auf Trackbacks ist an."
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Nächster Eintrag"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "Vorheriger Eintrag"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "Verwandte Einträge"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Ähnliche Einträge"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Kommentare"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr "Pingbacks"
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr "Pingbacks sind aktiviert."
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr "Pingbacks sind deaktiviert."
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr "Trackbacks"
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr "Trackback-URL"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "RSS-Feed"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "Kategorie"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "Tag"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Seite"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "Einträge von %(author)s"
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr "Keine Einträge vorhanden."
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr "Seite %(current_page)s von %(total_page)s "
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "Mehr neuste Einträge"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr "Einträge der Seite"
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "Mehr alte Einträge"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr "Kategorie bearbeiten"
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr "Tag bearbeiten"
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr "Autor bearbeiten"
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "RSS-Feed mit Suchergebnissen zu"
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] "%(gbobject_count)s Eintrag gefunden"
+msgstr[1] "%(gbobject_count)s Einträge gefunden"
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "Nichts gefunden."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr "Anmeldung erforderlich"
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr ""
+"Ihr Benutzername und Passwort stimmen nicht überein. Bitte versuchen Sie es "
+"erneut."
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr "Sie müssen eingeloggt sein um diesen Eintrag zu sehen."
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr "Login"
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr "Passwort erforderlich"
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr "Das Kennwort ist ungültig. Bitte versuchen Sie es erneut."
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr "Sie benötigen ein Passwort um diesen Eintrag lesen zu können."
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr "Passwort"
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr "Gültig"
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "Einträge pro Kategorien"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "Kommentar"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Alle Einträge"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "Keine Kategorien vorhanden."
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "RSS-Feed der neuesten Einträge"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "Tag-Liste"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr ""
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr "Kein Archiv vorhanden."
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr "Keine Autoren vorhanden."
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr "Linkback auf"
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "Keine ähnlichen Einträge."
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "Der Suchbegriff ist zu kurz"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "Kein Suchbegriff gegeben"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "Benutzername ist falsch."
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "Kennwort ist ungültig."
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "Benutzerkonto nicht verfügbar."
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr "Benutzer kann nicht %s."
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr "Kein Titel"
+
+
diff --git a/objectapp/locale/es/LC_MESSAGES/django.mo b/objectapp/locale/es/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..50a02a9
--- /dev/null
+++ b/objectapp/locale/es/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/es/LC_MESSAGES/django.po b/objectapp/locale/es/LC_MESSAGES/django.po
new file mode 100644
index 0000000..cd66c90
--- /dev/null
+++ b/objectapp/locale/es/LC_MESSAGES/django.po
@@ -0,0 +1,1199 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# igalarzab <igalarzab@gmail.com>, 2011.
+# Julien Fache <fantomas42@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-23 12:47+0000\n"
+"Last-Translator: igalarzab <igalarzab@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "Últimas entradas"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "Las últimas entradas del sitio %s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "Entradas de la categoría %s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr "Las últimas entradas de la categoría %s"
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "Entradas del autor %s"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr "Las últimas entradas de %s"
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "Entradas del tag %s"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr "Las últimas entradas del tag %s"
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr "Resultados de la búsqueda '%s'"
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr "Entradas que contienen el patron '%s'"
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr "Debate en %s"
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr "Los últimos debates de la entrada %s"
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Comentarios en %s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr "Los últimos comentarios de la entrada %s"
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr "Pingbacks en %s"
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr "Los últimos pingbacks de la entrada %s"
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr "Trackbacks en %s"
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr "Los últimos trackbacks de la entrada %s"
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "título"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "usado para la publicación"
+
+#: models.py:67
+msgid "description"
+msgstr "descripción"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "categoría padre"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "categoría"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "categorías"
+
+#: models.py:101
+msgid "draft"
+msgstr "borrador"
+
+#: models.py:102
+msgid "hidden"
+msgstr "oculto"
+
+#: models.py:103
+msgid "published"
+msgstr "publicado"
+
+#: models.py:107
+msgid "image"
+msgstr "imagen"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "utilizado como ilustración"
+
+#: models.py:109
+msgid "content"
+msgstr "contenido"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "extracto"
+
+#: models.py:111
+msgid "optional element"
+msgstr "elemento opcional"
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "tags"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "entradas relacionadas"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "autores"
+
+#: models.py:126
+msgid "featured"
+msgstr "destacada"
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "comentarios habilitados"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr "linkbacks habilitados"
+
+#: models.py:130
+msgid "creation date"
+msgstr "fecha de creación"
+
+#: models.py:131
+msgid "last update"
+msgstr "última actualización"
+
+#: models.py:132
+msgid "start publication"
+msgstr "comienzo de la publicación"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "fecha de comienzo de la publicación"
+
+#: models.py:135
+msgid "end publication"
+msgstr "fin de la publicación"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "Fecha de cierre de la publicación"
+
+#: models.py:139
+msgid "sites publication"
+msgstr "publicaciones del sitio"
+
+#: models.py:141
+msgid "login required"
+msgstr "identificación requerida"
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr "sólo los usuarios identificados pueden ver la entrada"
+
+#: models.py:143
+msgid "password"
+msgstr "contraseña"
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr "protege la entrada con una contraseña"
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr "plantilla"
+
+#: models.py:149
+msgid "Default template"
+msgstr "Plantilla predeterminada"
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr "plantilla usada para la entrada"
+
+#: models.py:285
+msgid "gbobject"
+msgstr "entrada"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "entradas"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr "[%(site)s] Nuevo comentario en \"%(title)s\""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr "árbol de categorías"
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "Contenido"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "Opciones"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr "Privacidad"
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr "Debates"
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr "Publicación"
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr "%(title)s (%(word_count)i palabras)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr "%(title)s (%(comments)i comentarios)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "autor(es)"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "categoría(s)"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "tag(s)"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "sitio(s)"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "es actual?"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "es visible?"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr "Ver"
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "Ver en el sitio"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "No disponible"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "url corta"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr "Las entradas seleccionadas ahora son tuyas."
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "Asignar las entradas al usuario"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr "Las entradas seleccionadas han sido marcadas como publicadas."
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "Publicar las entradas seleccionadas"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr "Las entradas seleccionadas han sido marcadas como ocultas."
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "Ocultar las entradas seleccionadas"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr "Las entradas marcadas han sido twitteadas."
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "Twittear las entradas seleccionadas"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr "Los comentarios en las entradas marcadas han sido cerrados."
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "Cerrar los comentarios de las entradas seleccionadas"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr "Los linkbacks en las entradas marcadas han sido cerrados."
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr "Cerrar los linkbacks de las entradas seleccionadas"
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr ""
+"Las fechas de las entradas seleccionadas han sido puestas al momento actual."
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr "Poner las fechas de las entradas seleccionadas al momento actual"
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr ""
+"El directorio %(directory)s ha realizado un ping correctamente a %(success)d"
+" entradas."
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "Ping directorios para las entradas seleccionadas"
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr "No hay categoría padre"
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr "Una categoría no puede ser padre de si misma."
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "Categorías"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr "Aplicación Objectapp"
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "Ordenaci´n"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr "Avanzado"
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "Entradas seleccionadas"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "Entradas al azar"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr "Menú de entradas Objectapp "
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr "Menú Categoría Objectapp"
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr "Menú Autor Objectapp"
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "Autores"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr "Menú Tag Objectapp"
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "Tags"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr "Lista de entradas (por defecto)"
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr "Detalle de la entrada"
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr "incluir subcategorías"
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "Número de entradas"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr "Plantilla utilizada para mostrar el plugin "
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s entradas"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr "Error 404"
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr "Página no encontrada"
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr "La página solicitada no pudo ser encontrada."
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr "Enlaces útiles"
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr "Inicio del blog"
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr "Sitemap"
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "Entradas recientes"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "Buscar"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "Palabras clave..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr "Error 500"
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr "Error del Servidor"
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+"Ha ocurrido un error. Ha sido reportado al administrador de la web por "
+"email. Intentaremos solucionarlo con la mayor brevedad posible. Gracias por "
+"tu paciencia."
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr "Contenido"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr "Debates"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr "%(gbobjects)s entradas"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr "%(comments)s comentarios"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr "%(objecttypes)s categorías"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr "%(pingbacks)s pingbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr "%(tags)s tags"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr "%(trackbacks)s trackbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr "%(authors)s autores"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr "%(rejects)s rechazadas"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr "Editar la entrada"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "en"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "Previsualizar"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr "No hay borradores."
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr "Ver todos los borradores"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "en"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr "Comentarios en"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr "Editar el comentario"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr "Editar"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr "No hay comentarios."
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr "Administra los comentarios"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr "Crea un linkback en"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr "Edita el linkback"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr "No hay linkbacks."
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr "Hoy"
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr "Borradores"
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr "Publicación rápida"
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr "Título"
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr "Guardado como borrador"
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr "Resetear"
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr "Publicar"
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr "Comentarios recientes"
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr "Linkbacks recientes"
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Autor"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr "Email"
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr "IP"
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr "Comentario"
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr "Ver este comentario"
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr "Marcar este comentario"
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr "Borrar este comentario"
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr "Aprobar este comentario"
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "Previsualizar el comentario"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] "Por favor corrije el siguiente error."
+msgstr[1] "Por favor corrige los siguientes errores."
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "Previsualización del comentario"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "Escribe un comentario"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "Post"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "Gracias por el comentario"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "Volver a la lista de entradas"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "Escrito por"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "Mostrar las entradas de %(author)s"
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr "Escrito en"
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "Sin tags"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "Url corta"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] "%(comment_count)s comentario"
+msgstr[1] "%(comment_count)s commentarios"
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr "¡Se el primero en escribir un comentario!"
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "Los comentarios están deshabilitados."
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] "%(pingback_count)s pingback"
+msgstr[1] "%(pingback_count)s pingbacks"
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] "%(trackback_count)s trackback"
+msgstr[1] "%(trackback_count)s trackbacks"
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "Lista de autores"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] "%(gbobject_count)s entrada"
+msgstr[1] "%(gbobject_count)s entradas"
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+"Puedes usar - para excluir palabras o frases, &quot;comillas dobles&quot; "
+"para buscar frases exactas, y las operaciones booleanas AND/OR combinada con"
+" paréntesis para realizar búsquedas complejas."
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr "Calendario"
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "Entradas populares"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "Archivos"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr "Herramientas"
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr "Panel de control"
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr "Escribe una entrada"
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr "Desconectar"
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "Lista de categorías"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr "Archivos diarios"
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr "Archivos Mensuales"
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr "Feed RSS de los debates en"
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "Feed RSS de comentarios en"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr "Feed RSS de pingbacks en"
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr "Feed RSS de trackbacks en"
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Siguiente entrada"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "Entrada anterior"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "Entradas relacionadas"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Entradas similares"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Comentarios"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr "Pingbacks"
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr "Los pingbacks están abiertos."
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr "Los pingbacks están cerrados."
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr "Trackbacks"
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr "URL del trackback"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr "Últimas entradas de"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr "la categoría"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr "el tag"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr "el autor"
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr "página"
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "Feed RSS"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "Categoría"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "Tag"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Página"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "Entradas de %(author)s"
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr "No hay entradas."
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr "Página %(current_page)s de %(total_page)s"
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "Entradas más recientes"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr "Página de entradas"
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "Entradas más antiguas"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr "Editar la categoría"
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr "Editar el tag"
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr "Editar el autor"
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr "Resultados de la búsqueda para"
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "Feed RSS de la búsqueda de"
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] "%(gbobject_count)s entrada encontrada"
+msgstr[1] "%(gbobject_count)s entradas encontradas"
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "No hubo resultados."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr "Identificación requerida"
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr ""
+"Tu nombre de usuario y contraseña no son correctos. Vuelve a intentarlo."
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr "Necesitas estar identificado para ver esta entrada."
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr "Login"
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr "Contraseña requerida"
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr "La contraseña proporcionada no es válida. Vuelve a intentarlo."
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr "Necesitas una contraseña para ver esta entrada."
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr "Contraseña"
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr "Válido"
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "Entradas de la categoría"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "comentario"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Todas las entradas"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "No hay categorías."
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "Feed RSS de las últimas entradas"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "Lista de tags"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr "Ver la web"
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr "Panel de administración"
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr "Administrar comentarios"
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr "No hay archivos."
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr "No hay autores."
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr "Linkbacks en"
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "No hay entradas similares"
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "La cadena es muy corta"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "No se encuentra cadena"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "El nombre de usuario es incorrecto."
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "La contraseña es incorrecta."
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "El nombre de usuario no está disponible."
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr "El usuario no puede ser %s."
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr "No hay título"
+
+
diff --git a/objectapp/locale/fr/LC_MESSAGES/django.mo b/objectapp/locale/fr/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..ea0a795
--- /dev/null
+++ b/objectapp/locale/fr/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/fr/LC_MESSAGES/django.po b/objectapp/locale/fr/LC_MESSAGES/django.po
new file mode 100644
index 0000000..1e3e9fb
--- /dev/null
+++ b/objectapp/locale/fr/LC_MESSAGES/django.po
@@ -0,0 +1,1199 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Julien Fache <fantomas42@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-12 18:22+0000\n"
+"Last-Translator: Fantomas42 <fantomas42@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "Derniers articles"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "Les dernières actualités du site %s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "Actualités pour la catégorie : %s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr "Les dernières actualités pour la catégorie : %s"
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "L'actualité par %s"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr "Les dernières actualités par %s"
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "Actualités pour le tag : %s"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr "Les dernières actualités pour le tag : %s"
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr "Résultats de recherche pour '%s'"
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr "Les dernières actualités contenant le motif '%s'"
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr "Discussions sur %s"
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr "Les dernières discussions sur l'actualité : %s"
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Commentaires sur : %s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr "Les derniers commentaires sur l'actualité : %s"
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr "Pingbacks sur %s"
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr "Les derniers pingbacks sur l'actualité : %s"
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr "Trackbacks sur %s"
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr "Les derniers trackbacks sur l'actualité : %s"
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "titre"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "Utilisé pour la publication."
+
+#: models.py:67
+msgid "description"
+msgstr "description"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "catégorie parente"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "catégorie"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "catégories"
+
+#: models.py:101
+msgid "draft"
+msgstr "Brouillon"
+
+#: models.py:102
+msgid "hidden"
+msgstr "Non publié"
+
+#: models.py:103
+msgid "published"
+msgstr "Publié"
+
+#: models.py:107
+msgid "image"
+msgstr "image"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "Utilisée comme illustration."
+
+#: models.py:109
+msgid "content"
+msgstr "contenu"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "extrait"
+
+#: models.py:111
+msgid "optional element"
+msgstr "Elément optionnel."
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "tags"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "articles associés"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "auteurs"
+
+#: models.py:126
+msgid "featured"
+msgstr "favori ?"
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "commentaires ?"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr "liens entrants ?"
+
+#: models.py:130
+msgid "creation date"
+msgstr "date de création"
+
+#: models.py:131
+msgid "last update"
+msgstr "mise à jour"
+
+#: models.py:132
+msgid "start publication"
+msgstr "démarrage"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "Date utilisé pour démarrer la publication."
+
+#: models.py:135
+msgid "end publication"
+msgstr "arrêt"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "Date utilisé pour arrêter la publication."
+
+#: models.py:139
+msgid "sites publication"
+msgstr "sites publiés"
+
+#: models.py:141
+msgid "login required"
+msgstr "identification requise"
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr "Seuls les visiteurs authentifiés peuvent voir l'article."
+
+#: models.py:143
+msgid "password"
+msgstr "mot de passe"
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr "Protège l'article avec un mot de passe."
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr "modèle"
+
+#: models.py:149
+msgid "Default template"
+msgstr "Modèle par défaut"
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr "Modèle utilisé pour afficher l'article."
+
+#: models.py:285
+msgid "gbobject"
+msgstr "article"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "articles"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr "[%(site)s] Nouveau commentaire posté sur \"%(title)s\""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr "arborescense"
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "Contenu"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "Options"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr "Confidentialité"
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr "Discussion"
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr "Publication"
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr "%(title)s (%(word_count)i mots)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr "%(title)s (%(comments)i commentaires)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "auteur(s)"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "Catégorie(s)"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "Tag(s)"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "Site(s)"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "En cours ?"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "Visible ?"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr "Consulter"
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "Voir sur le site"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "Indisponible"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "url raccourcie"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr "Les articles sélectionnés vous appartiennent désormais."
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "Acquérir les articles"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr "Les articles sélectionnés sont désormais marqués comme publiés."
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "Publier les articles sélectionnés"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr "Les articles sélectionnés sont désormais marqués comme cachés."
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "Masquer les articles sélectionnés"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr "Les articles sélectionnés ont été twittés."
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "Tweeter les articles sélectionnés"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr ""
+"Les commentaires sont désormais fermés pour les articles sélectionnés."
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "Fermer les commentaires pour les articles sélectionnés"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr ""
+"Les liens entrants sont désormais fermés pour les articles sélectionnés."
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr "Fermer les liens entrants pour les articles sélectionnés"
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr "Les articles sélectionnés sont désormais à la date du jour."
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr "Remonter les articles sélectionnés à la date du jour"
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr "%(directory)s a été pingé avec succès pour %(success)d articles."
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "Pinger les articles dans les annuaires."
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr "Aucune catégorie parente"
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr "Une catégorie ne peut être parente d'elle même."
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "Catégories"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr "Application Objectapp"
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "Répartition"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr "Avancé"
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "Selection d'articles"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "Articles au hasard"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr "Menu Article Objectapp"
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr "Menu Catégorie Objectapp"
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr "Menu Auteur Objectapp"
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "Auteurs"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr "Menu Tag Objectapp"
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "Tags"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr "Article en liste (par défaut)"
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr "Article détaillé"
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr "inclure les sous-catégories ?"
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "nombre d'articles"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr "Modèle utilisé pour afficher le plugin."
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s articles"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr "Erreur 404"
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr "Cette page n'a pas été trouvée"
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr "Désolé, mais la page demandée est introuvable."
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr "Liens utiles"
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr "Accueil du blog"
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr "Carte du site"
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "Articles récents"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "Recherche"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "Mots-clefs..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr "Erreur 500"
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr "Erreur du serveur"
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+"Une erreur est survenue. Elle a été transmise par courriel aux "
+"administrateurs du site et sera corrigée dans les meilleurs délais. Merci "
+"pour votre patience."
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr "Contenus"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr "Discussions"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr "%(gbobjects)s articles"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr "%(comments)s commentaires"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr "%(objecttypes)s catégories"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr "%(pingbacks)s pingbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr "%(tags)s tags"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr "%(trackbacks)s trackbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr "%(authors)s auteurs"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr "%(rejects)s rejetées"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr "Editer l'article"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "le"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "Prévisualiser"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr "Aucun brouillon actuellement"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr "Voir tous les articles en brouillon"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "sous"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr "Commentaire sur"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr "Editer le commentaire"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr "Editer"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr "Pas de commentaires."
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr "Gérer les commentaires"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr "a fait un lien sur"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr "Editer le lien entrant"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr "Aucun lien entrant actuellement"
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr "Aujourd'hui"
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr "Brouillons en cours"
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr "Publication rapide"
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr "Titre"
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr "Enregistrer en brouillon"
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr "Réinitialiser"
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr "Publier"
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr "Derniers commentaires"
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr "Derniers liens entrants"
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Auteur"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr "Email"
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr "IP"
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr "Commentaire"
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr "Voir ce commentaire"
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr "Signaler ce commentaire"
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr "Supprimer ce commentaire"
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr "Approuver ce commentaire"
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "Prévisualiser le commentaire"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] "Veuillez corriger l'erreur suivante."
+msgstr[1] "Veuillez corriger les erreurs suivantes. "
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "Prévisualisation du commentaire"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "Poster un commentaire"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "Poster"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "Merci pour votre commentaire"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "Retourner à la liste des articles"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "Ecrit par"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "Voir les articles de %(author)s"
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr "Ecrit le"
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "Pas de tags"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "Url raccourcie"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] "%(comment_count)s commentaire "
+msgstr[1] "%(comment_count)s commentaires"
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr "Soyez le premier à commenter !"
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "Les commentaires sont fermés."
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] "%(pingback_count)s pingback"
+msgstr[1] "%(pingback_count)s pingbacks"
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] "%(trackback_count)s trackback"
+msgstr[1] "%(trackback_count)s trackbacks"
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "Liste des auteurs"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] "%(gbobject_count)s article"
+msgstr[1] "%(gbobject_count)s articles"
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+"Vous pouvez utiliser le - pour exclure des mots ou des expressions, les "
+"&quot;double guillemets&quot; pour les expressions exactes et les opérateurs"
+" booléens AND/OR combinés avec des parenthèses pour les recherches "
+"complexes."
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr "Calendrier"
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "Articles populaires"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "Archives"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr "Outils"
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr "Tableau de bord"
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr "Poster un article"
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr "Déconnexion"
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "Liste des catégories"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr "Archives journalières"
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr "Archives mensuelles"
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr "Flux RSS des discussions sur"
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "Flux RSS des commentaires sur"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr "Flux RSS des pingbacks sur"
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr "Flux RSS des trackbacks sur"
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Article suivant"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "Article précédent"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "Articles associés"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Articles similaires"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Commentaires"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr "Pingbacks"
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr "Les pingbacks sont ouverts."
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr "Les pingbacks sont fermés."
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr "Trackbacks"
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr "URL de Trackback"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr "Derniers articles pour"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr "la catégorie"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr "le tag"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr "l'auteur"
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr "page"
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "Flux RSS"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "Catégorie"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "Tag"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Page"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "L'actualité par %(author)s"
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr "Pas encore d'articles."
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr "Page %(current_page)s sur %(total_page)s"
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "Articles plus récents"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr "Articles page"
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "Articles plus anciens"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr "Editer la catégorie"
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr "Editer le tag"
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr "Editer l'auteur"
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr "Résultats de recherche pour"
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "Flux RSS des résultats de recherche pour"
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] "%(gbobject_count)s article trouvé."
+msgstr[1] "%(gbobject_count)s articles trouvés."
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "Aucun article trouvé."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr "Identification requise"
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr ""
+"Votre nom d'utilisateur et mot de passe ne correspondent pas. Veuillez "
+"réessayer."
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr "Vous devez être identifé pour voir cet article."
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr "Connexion"
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr "Mot de passe requis"
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr "Le mot de passe fournis est non valide. Veuillez réessayer."
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr "Vous devez fournir un mot de passe pour voir cet article."
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr "Mot de passe"
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr "Valider"
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "Articles par catégories"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "commentaire"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Tous les articles"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "Pas encore de catégories."
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "Flux RSS des derniers articles"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "Liste des tags"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr "Voir le site"
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr "Admin. site"
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr "Gérer les commentaires"
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr "Pas encore d'archives."
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr "Pas encore d'auteurs."
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr "Pingback sur"
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "Pas d'articles similaires."
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "Le motif de recherche est trop court !"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "Aucun motif de recherche !"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "Le nom d'utilisateur est incorrect."
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "Le mot de passe est invalide."
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "Compte utilisateur non valable."
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr "L'utilisateur ne peut %s."
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr "Aucun titre"
+
+
diff --git a/objectapp/locale/hu/LC_MESSAGES/django.mo b/objectapp/locale/hu/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..931a52f
--- /dev/null
+++ b/objectapp/locale/hu/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/hu/LC_MESSAGES/django.po b/objectapp/locale/hu/LC_MESSAGES/django.po
new file mode 100644
index 0000000..0d13597
--- /dev/null
+++ b/objectapp/locale/hu/LC_MESSAGES/django.po
@@ -0,0 +1,1194 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Frank Kruchio <fkruchio@gmail.com>, 2011.
+# judit <judit.nz@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-12 18:21+0000\n"
+"Last-Translator: Fantomas42 <fantomas42@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: hu\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "Legújabb bejegyzések"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "A legfrissebb bejegyzések az oldalon:%s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "Bejegyzés a kategóriában:%s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr "A legfrissebb bejegyzések a kategóriában:%s"
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "Bejegyzés szerzője:%s"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr "A legfrissebb bejegyzések:%s"
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "Bejegyzés ezzel a címkével:%s"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr "A legfrissebb bejegyzések ezzel a címkével:%s"
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr ""
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr ""
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr "Beszélgetések:%s"
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr "A bejegyzés legutóbbi beszélgetései :%s"
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Megjegyzések:%s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr "A bejegyzés legfrissebb hozzászólásai:%s"
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr "Pingbacks:%s"
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr "Legújabb Pingbacks ebben a bejegyzésben :%s"
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr "Trackbacks:%s"
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr "A legújabb trackbacks ebben a bejegyzésben:%s"
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "cím"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "közzétételhez használva"
+
+#: models.py:67
+msgid "description"
+msgstr "leírás"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "főkategória"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "kategória"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "kategóriák"
+
+#: models.py:101
+msgid "draft"
+msgstr "tervezet"
+
+#: models.py:102
+msgid "hidden"
+msgstr "rejtett"
+
+#: models.py:103
+msgid "published"
+msgstr "közzétett"
+
+#: models.py:107
+msgid "image"
+msgstr "kép"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "illusztrációhoz használva"
+
+#: models.py:109
+msgid "content"
+msgstr "tartalom"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "idézet"
+
+#: models.py:111
+msgid "optional element"
+msgstr "választható elem"
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "címkék"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "kapcsolatos bejegyzések"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "szerzők"
+
+#: models.py:126
+msgid "featured"
+msgstr "Kiemelt"
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "megjegyzés engedélyezve"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr "linkback engedélyezve"
+
+#: models.py:130
+msgid "creation date"
+msgstr "létrehozás dátuma"
+
+#: models.py:131
+msgid "last update"
+msgstr "utolsó frissítés"
+
+#: models.py:132
+msgid "start publication"
+msgstr "közzététel indítása"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "közzététel dátuma"
+
+#: models.py:135
+msgid "end publication"
+msgstr "közzététel vége"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "közzététel befejezésének dátuma"
+
+#: models.py:139
+msgid "sites publication"
+msgstr "oldalak közzététele"
+
+#: models.py:141
+msgid "login required"
+msgstr "bejelentkezés szükséges"
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr "a bejegyzést csak hitelesített felhasználók tekinthetik meg"
+
+#: models.py:143
+msgid "password"
+msgstr "jelszó"
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr "cikk levédése jelszóval"
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr "sablon"
+
+#: models.py:149
+msgid "Default template"
+msgstr "Alapértelmezett sablon"
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr "bejegyzéshez használt sablon"
+
+#: models.py:285
+msgid "gbobject"
+msgstr "bejegyzés"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "bejegyzések"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr "[ %(site)s ] Új hozzászólás érkezett \" %(title)s \""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr "fa útvonal"
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "Tartalom"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "Opciók"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr "Adatvédelem"
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr "Hozzászólás"
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr "Közzététel"
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr " %(title)s (%(word_count)i szavak)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr " %(title)s (%(comments)i hozzászólások)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "szerző(k)"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "kategória(ák)"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "tag(ek)"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "helyszín(ek)"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "a tényleges"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "látható"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr ""
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "Nézd meg a helyszínen"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "Nem érhető el"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "Rövid url"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr ""
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "Ãllítsa be a bejegyzéseket a felhasználónak"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr ""
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "Ãllítsa be a kiválasztott bejegyzéseket közzétettnek"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr ""
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "Ãllítsa be a kiválasztott bejegyzéseket rejtettnek"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr ""
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "Tweet a kiválasztott bejegyzéseket"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "Zárja be a megjegyzéseket a kiválasztott bejegyzéseken"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr "Zárja be a hivatkozásokat a kiválasztott bejegyzéseken"
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr ""
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr "Tegye a kiválasztott bejegyzéseket az aktuális dátum tetejére"
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr "%(directory)s könyvtár sikeresen elért %(success)d bejegyzést."
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "Ping Könyvtárak a kiválasztott bejegyzésekhez"
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr "Nem főkategória"
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr "A kategória nem lehet önmaga szülője."
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "Kategóriák"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr "Objectapp Program Horog"
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "Besorolás"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr "Részletes"
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "Kiválasztott bejegyzések"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "Véletlen bejegyzések"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr "Objectapp Belépés Menü"
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr "Objectapp Kategória Menü"
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr "Objectapp Szerző Menü"
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "Szerzők"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr "Objectapp Címke Menü"
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "Címkék"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr "Nevezési lista (alapértelmezett)"
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr "Bejegyzés részletezve"
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr "Alkategóriák"
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "bejegyzések száma"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr "Segédprogram megjelenítéséhez használt sablon"
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s bejegyzések"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr "404-es hiba"
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr "Az oldal nem található"
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr "Sajnáljuk, de a kért oldal nem található."
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr "Hasznos linkek"
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr "Blog-index"
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr "Oldaltérkép"
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "Legutóbbi bejegyzések"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "Keresés"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "Kulcsszavak ..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr "500-as hiba"
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr "Szerver hiba"
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+"Hiba történt. A hibát a rendszeradminisztrátorok e-mailben jelezték, és "
+"hamarosan elháritják. Köszönjük a türelmét."
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr "Tartalma"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr "Fórum"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr " %(gbobjects)s bejegyzések"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr "%(comments)s észrevételek"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr " %(objecttypes)s kategóriák"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr " %(pingbacks)s pingbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr " %(tags)s címkék"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr " %(trackbacks)s trackbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr " %(authors)s szerzők"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr " %(rejects)s elutasítva"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr "Módosítsa a bejegyzést"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "-án / -én"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "Előnézet"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr "Nincsenek piszkozatok."
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr "Az összes piszkozat megtekintése"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "ban"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr "Hozzászólva"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr "Megjegyzés szerkesztése"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr "Szerkesztés"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr "Nincs még hozzászólás."
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr "Hozzászólás kezelése"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr "LinkBack készült"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr "LinkBack szerkesztése"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr "Nincs még LinkBack"
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr "Ma"
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr "Bejegyzés tervezetek"
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr "Gyors közzététel"
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr "Cím"
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr "Mentés piszkozatként"
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr "Visszaállítás"
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr "Közzététel"
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr "Friss hozzászólások"
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr "Friss LinkBacks"
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Szerző"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr ""
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "Hozzászólás előnézete"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] "Kérjük, javítsa ki a következő hibát."
+msgstr[1] "Kérjük, javítsa ki a következő hibákat."
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "A megjegyzés előnézete"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "Küldd el a hozzászólást!"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "Küldés"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "Köszönöm a megjegyzést"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "Vissza a bejegyzési listára"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "Ãrta:"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "Mutasd %(author)s bejegyzéseit"
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "Nincs címke"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "Rövid url"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] "%(comment_count)s hozzászólása"
+msgstr[1] "%(comment_count)s hozzászólásai"
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr "Legyen Ön az első hozzászóló!"
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "Hozzászólások lezárva."
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] "%(pingback_count)s pingback"
+msgstr[1] "%(pingback_count)s pingbacks"
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] "%(trackback_count)s trackback"
+msgstr[1] "%(trackback_count)s trackbacks"
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "Szerző lista"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] "%(gbobject_count)s bejegyzés"
+msgstr[1] "%(gbobject_count)s bejegyzések"
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+"Használhatja a - jelet, hogy kizárjon szavakat vagy kifejezéseket, "
+"&quot;kettős idézőjelet&quot; pontos kifejezések kereséséhez, és az AND/OR "
+"logikai operátorokat zárójelekkel kombinálva az összetett kereséseknél."
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr "Naptár"
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "Népszerű bejegyzések"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "Arhívum"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr "Eszközök"
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr "Műszerfal"
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr "Adj hozzá egy bejegyzést"
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr "Kijelentkezés"
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "Kategória lista"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr ""
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr "Havi arhívum"
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr "RSS Feed-megbeszélések a témáról"
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "RSS Feed észrevételek a témáról"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr "RSS Feed pingbacks a témáról"
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr "RSS Feed trackback a témáról"
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Következő bejegyzés"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "Előző bejegyzés"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "Kapcsolódó bejegyzések"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Hasonló bejegyzések"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Hozzászólások"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr "Pingbacks"
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr "Pingbacks nyitva."
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr "Pingbacks zárva."
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr "Trackbacks"
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr "Trackback URL"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "RSS Feed"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "Kategória"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "Címke"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Oldal"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "Bejegyzés szerzője %(author)s "
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr "Nincs még bejegyzés."
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr "%(current_page)s . oldal a %(total_page)s -ból/-ből"
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "Mégtöbb új bejegyzés"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr "Bejegyzések oldal"
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "Mégtöbb régi bejegyzés"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr "Kategória szerkesztése"
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr "Címke szerkesztése"
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr "Szerző kijavítása"
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "A találatok RSS Feed-je"
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] "%(gbobject_count)s bejegyzést találtam"
+msgstr[1] " %(gbobject_count)s bejegyzést találtam"
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "Nincs találat."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr "Bejelentkezés szükséges"
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr "Felhasználóneve és jelszava nem egyezik. Kérjük, próbálja újra."
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr "A bejegyzés megtekintéséhez be kell jelentkeznie."
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr "Bejelentkezés"
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr "Jelszó szükséges"
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr "A megadott jelszó nem érvényes. Kérjük, próbálja újra."
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr "Meg kell adnia egy jelszót, hogy megtekinthesse ezt a bejegyzést."
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr "Jelszó"
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr "Érvényes"
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "Bejegyzések egyes kategóriákban"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "megjegyzés"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Az összes bejegyzés"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "Nincs még kategória."
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "RSS link a legutóbbi bejegyzésekhez"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "Címke lista"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr ""
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr "Nincsenek még arhívumok."
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr "Nincsenek még szerzők."
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr "Linkback létrehozása"
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "Nincsenek hasonló bejegyzések."
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "A megadott kifejezés túl rövid"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "Nincs találat a megadott kifejezésre"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "Felhasználónév helytelen."
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "Jelszó érvénytelen."
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "Ez a felhasználói név már foglalt."
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr "A felhasználó nem %s."
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr "Nincs cím"
+
+
diff --git a/objectapp/locale/it/LC_MESSAGES/django.mo b/objectapp/locale/it/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..0cec5fb
--- /dev/null
+++ b/objectapp/locale/it/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/it/LC_MESSAGES/django.po b/objectapp/locale/it/LC_MESSAGES/django.po
new file mode 100644
index 0000000..8c37e7c
--- /dev/null
+++ b/objectapp/locale/it/LC_MESSAGES/django.po
@@ -0,0 +1,1196 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# babu <marco.badan@gmail.com>, 2011.
+# Julien Fache <fantomas42@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-12 18:21+0000\n"
+"Last-Translator: Fantomas42 <fantomas42@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: it\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "Ultimi articoli"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "Gli ultimi articoli del sito %s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "Articoli per la categoria %s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr "Gli ultimi articoli per la categoria %s"
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "Articoli di %s"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr "Gli ultimi articoli di %s"
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "Articoli taggati come %s"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr "Ultimi articoli taggati come %s"
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr ""
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr ""
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr "Discussioni su %s"
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr "Ultime discussioni sull'articolo %s"
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Commenti su : %s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr "Ultimi commenti sull'articolo %s"
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr "Pingback su %s"
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr "Ultimi pingback sull'articolo %s"
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr "Trackback su %s"
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr "Ultimi trackback sull'articolo %s"
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "titolo"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "usata per la pubblicazione"
+
+#: models.py:67
+msgid "description"
+msgstr "descrizione"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "categoria padre"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "categoria"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "categorie"
+
+#: models.py:101
+msgid "draft"
+msgstr "bozza"
+
+#: models.py:102
+msgid "hidden"
+msgstr "non pubblicato"
+
+#: models.py:103
+msgid "published"
+msgstr "pubblicato"
+
+#: models.py:107
+msgid "image"
+msgstr "immagine"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "usata come illustrazione"
+
+#: models.py:109
+msgid "content"
+msgstr "contenuto"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "estratto"
+
+#: models.py:111
+msgid "optional element"
+msgstr "elemento opzionale"
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "tag"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "articoli correlati"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "autori"
+
+#: models.py:126
+msgid "featured"
+msgstr ""
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "commenti abilitati"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr "linkback abilitati"
+
+#: models.py:130
+msgid "creation date"
+msgstr "data di creazione"
+
+#: models.py:131
+msgid "last update"
+msgstr "ultimo aggiornamento"
+
+#: models.py:132
+msgid "start publication"
+msgstr "inizio pubblicazione"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "data inizio pubblicazione"
+
+#: models.py:135
+msgid "end publication"
+msgstr "fine pubblicazione"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "data fine pubblicazione"
+
+#: models.py:139
+msgid "sites publication"
+msgstr "pubblicato sui siti"
+
+#: models.py:141
+msgid "login required"
+msgstr "richiesta identificazione"
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr "solo i visitatori autenticati possono vedere l'articolo"
+
+#: models.py:143
+msgid "password"
+msgstr "password"
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr "proteggi l'articolo con una password"
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr "template"
+
+#: models.py:149
+msgid "Default template"
+msgstr "Template di default"
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr "Template utilizzato per visualizzare l'articolo"
+
+#: models.py:285
+msgid "gbobject"
+msgstr "articolo"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "articoli"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr "[%(site)s] Nuovo commento inviato su \"%(title)s\""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr "alberatura"
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "Contenuto"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "Opzioni"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr "Privacy"
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr "Discussione"
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr "Pubblicazione"
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr "%(title)s (%(word_count)i parole)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr "%(title)s (%(comments)i commenti)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "autore(i)"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "Categoria(e)"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "Tag"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "Site(s)"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "in corso"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "visibile ?"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr ""
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "Guarda sul sito"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "Non disponibile"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "url breve"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr ""
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "Attribuisci gli articoli all'utente"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr ""
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "Pubblica gli articoli selezionati"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr ""
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "Nascondi gli articoli selezionati"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr ""
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "Manda un tweet per gli articoli selezionati"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "Chiudi i commenti per gli articoli selezionati"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr "Chiudi i linkback per gli articoli selezionati"
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr ""
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr ""
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr ""
+"La directory %(directory)s è stata pingata con successo per %(success)d "
+"articoli."
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "Pinga le directory per gli articoli selezionati"
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr "Nessuna categoria padre"
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr "Una categoria non può essere categoria padre di se stessa."
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "Catégories"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr "Applicazione Objectapp"
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "Ordinamento"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr "Avanzato"
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "Articoli selezionati"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "Articoli random"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr "Menu Articoli Objectapp"
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr "Menu Categorie Objectapp"
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr "Menu Auteur Objectapp"
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "Autori"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr "Menu Tag Objectapp"
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "Tag"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr "Elenco di articoli (default)"
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr "Dettaglio articolo"
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr "sottocategorie incluse"
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "numero di articoli"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr "Modello utilizzato per visualizzare il plugin"
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s articoli"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr "Errore 404"
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr "Pagina non trovata"
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr "Spiacente, ma la pagina richiesta è introvabile."
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr "Link utili"
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr "Indice del blog"
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr "Mappa del site"
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "Articoli recenti"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "Ricerca"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "Parole chiave..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr "Errore 500"
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr "Errore del server"
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+"C'è stato un errore. E' stato riportato agli amministratori del sito per "
+"email e dovrebbe venire risolto a breve. Grazie per la tua pazienza."
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr "Contenuti"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr "Discussioni"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr ""
+"%(gbobjects)s articoli\n"
+" "
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr "%(comments)s commenti"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr "%(objecttypes)s categorie"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr "%(pingbacks)s pingback"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr "%(tags)s tag"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr "%(trackbacks)s trackback"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr "%(authors)s autori"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr "%(rejects)s respinti"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr "Modifica l'articolo"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "il"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "Anteprima"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr "Nessun articolo in draft."
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr "articoli correlati"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "su"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr "Commenti su"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr "Modifica il commento"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr "Modifica"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr "Nessun commento."
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr "Gestisci i commenti"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr "Modifica il LinkBack"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr "Ancora nessun linkback."
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr "Oggi"
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr "Articoli in draft"
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr "Pubblicazione veloce"
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr "Titolo"
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr "Salva come bozza"
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr "Pubblica"
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr "Commenti recenti"
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr "Linkback recenti"
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Autore"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr ""
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "Anteprima commento"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] "Prego correggi l'errore seguente."
+msgstr[1] "Prego correggi gli errori seguenti."
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "Anteprima del commento"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "Invia un commento"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "Invia"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "Grazie per il tuo commento"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "Torna all'elenco degli articoli"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "Scritto da"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "Mostra gli articoli di %(author)s"
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "Nessun tag"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "Url breve"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] "%(comment_count)s commento"
+msgstr[1] "%(comment_count)s commenti"
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr "Sarai il primo a commentare!"
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "I commenti sono terminati."
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] "%(pingback_count)s pingback"
+msgstr[1] "%(pingback_count)s pingback"
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] "%(trackback_count)s trackback"
+msgstr[1] "%(trackback_count)s trackback"
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "Elenco degli autori"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] "%(gbobject_count)s articolo"
+msgstr[1] "%(gbobject_count)s articoli"
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr "Calendario"
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "Articoli popolari"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "Archivi"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr "Strumenti"
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr "Dashboard"
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr "Posta un articolo"
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr "Esci"
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "Elenco delle categorie"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr "Archivi giornalieri"
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr "Archivio mensile"
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr "Feed RSS delle discussioni su"
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "Feed RSS dei commenti su"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr "Feed RSS dei pingback su"
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr "Feed RSS dei trackback su"
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Articolo successivo"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "Articolo precedente"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "Articles associés"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Articoli simili"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Commenti"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr "Pingback"
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr "I pingback sono aperti."
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr "I pingback sono chiusi."
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr "Trackback"
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr "URL del Trackback"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "Feed RSS"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "Categorie"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "Tag"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Pagina"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "Articoli di %(author)s"
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr "Non ci sono ancora articoli."
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr "Pagina %(current_page)s di %(total_page)s"
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "Articoli più recenti"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr "Pagina degli articoli"
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "Articoli vecchi"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr "Modifica la categoria"
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr "Modifica il tag"
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr "Modifica l'autore"
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "Feed RSS dei risultati della ricerca per"
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] "%(gbobject_count)s articolo trouvato"
+msgstr[1] "%(gbobject_count)s articoli trovati"
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "Nessun articolo trovato."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr "Login richiesto"
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr ""
+"La tua username e la password non corrispondono. Per cortesia riprova."
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr "Devi essere collegato per vedere questo articolo."
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr "Login"
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr "Password richiesta"
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr "La password inserita non è valida. Per cortesia riprova."
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr "Devi inserire una password per vedere questo articolo."
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr "Password"
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr "Valida"
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "Articoli nella categoria"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "commento"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Tutti gli articoli"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "Nessuna categoria, al momento."
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "Feed RSS delgi ultimi articoli"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "Elenco dei tag"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr ""
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr "Nessun archivio, al momento."
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr "Nessun autore, al momento."
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr "Linkback su"
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "Nono ci sono articoli correlati."
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "La sequenza di ricerca è troppo corta"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "Nessuna sequenza di ricerca trovata"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "Username non corretta."
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "Password non valida."
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "Utenza non disponibile."
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr "L'utente non può %s."
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr "Nessun titolo"
+
+
diff --git a/objectapp/locale/nl/LC_MESSAGES/django.mo b/objectapp/locale/nl/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..992d4ed
--- /dev/null
+++ b/objectapp/locale/nl/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/nl/LC_MESSAGES/django.po b/objectapp/locale/nl/LC_MESSAGES/django.po
new file mode 100644
index 0000000..8bbdce5
--- /dev/null
+++ b/objectapp/locale/nl/LC_MESSAGES/django.po
@@ -0,0 +1,1195 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Dries Desmet <dries@urga.be>, 2011.
+# go2people <admin@go2people.nl>, 2011.
+# Julien Fache <fantomas42@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-09-01 06:46+0000\n"
+"Last-Translator: TrioTorus <dries@urga.be>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: nl\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "Nieuwste berichten"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "Het nieuwste bericht op de site %s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "Berichten in de categorie %s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr "De nieuwste berichten in de Objecttype %s"
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "Berichten van auteur %s"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr "De nieuwste berichten van %s"
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "Berichten voor het label %s"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr "De nieuwste berichten voor het label %s"
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr "zoekresultaten voor '%s'"
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr "Berichten die het patroon '%s' bevatten"
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr "Discussies over %s"
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr "De nieuwste discussies voor het bericht %s"
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Commentaren op %s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr "De nieuwste commentaren op het bericht %s"
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr "Pingbacks op %s"
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr "De nieuwste pingbacks voor het bericht %s"
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr "Trackbacks voor %s"
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr "De nieuwste trackbacks voor het bericht %s"
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "Titel"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "gebruikt voor publicatie"
+
+#: models.py:67
+msgid "description"
+msgstr "omschrijving"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "bovenliggende categorie"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "categorie"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "categorieën"
+
+#: models.py:101
+msgid "draft"
+msgstr "concept"
+
+#: models.py:102
+msgid "hidden"
+msgstr "verborgen"
+
+#: models.py:103
+msgid "published"
+msgstr "gepubliceerd"
+
+#: models.py:107
+msgid "image"
+msgstr "afbeelding"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "gebruikt als illustratie"
+
+#: models.py:109
+msgid "content"
+msgstr "inhoud"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "uittreksel"
+
+#: models.py:111
+msgid "optional element"
+msgstr "optioneel element"
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "labels"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "gerelateerde berichten"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "auteurs"
+
+#: models.py:126
+msgid "featured"
+msgstr "in de kijker"
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "commentaar geactiveerd"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr "linkback geactiveerd"
+
+#: models.py:130
+msgid "creation date"
+msgstr "aanmaakdatum"
+
+#: models.py:131
+msgid "last update"
+msgstr "Letzte Änderung"
+
+#: models.py:132
+msgid "start publication"
+msgstr "begin publicatie"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "publicatiedatum"
+
+#: models.py:135
+msgid "end publication"
+msgstr "eind publicatie"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "einddatum publicatie"
+
+#: models.py:139
+msgid "sites publication"
+msgstr "publicatie op site"
+
+#: models.py:141
+msgid "login required"
+msgstr "inloggen vereist"
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr "alleen geauthenticeerde gebruikers kunnen dit bericht zien"
+
+#: models.py:143
+msgid "password"
+msgstr "wachtwoord"
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr "beveilig het bericht met een wachtwoord"
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr "template"
+
+#: models.py:149
+msgid "Default template"
+msgstr "Stardaard template"
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr "gebruikte template om het bericht te tonen"
+
+#: models.py:285
+msgid "gbobject"
+msgstr "bericht"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "berichten"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr "[%(site)s] Nieuwe reactie op \"%(title)s\""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr "boomstructuur"
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "Inhoud"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "Opties"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr "Privacy"
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr "Discussie"
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr "Publicatie"
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr "%(title)s (%(word_count)i woorden)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr "%(title)s (%(comments)i reacties)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "auteur(s)"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "Categorie(ën)"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "label(s)"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "site(s)"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "is actueel"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "is zichtbaar"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr "Bekijk"
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "Op site bekijken"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "Niet beschikbaar"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "korte url"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr "De geselecteerde berichten zijn aan u toegewezen."
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "Wijs de berichten toe aan de gebruiker"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr "De geselecteerde berichten zijn gepubliceerd."
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "Geselecteerde berichten markeren als gepubliceerd"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr "De geselecteerde berichten zijn verborgen."
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "Geselecteerde berichten markeren als verborgen"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr "De geselecteerde berichten zijn getweet."
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "Geselecteerde berichten tweeten"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr "Reacties zijn niet meer mogelijk voor de geselecteerde berichten."
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "Commentaren sluiten voor geselecteerde berichten"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr "Linkbacks zijn nu afgesloten voor de geselecteerde berichten."
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr "Linkbacks sluiten voor geselecteerde berichten"
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr "De geselecteerde berichten zijn op de huidige datum ingesteld."
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr "Plaats de geselecteerde berichten bovenaan voor de huidige datum"
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr "%(directory)s map heeft succesvol %(success)d berichten gepingd."
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "Mappen pingen voor de geselecteerde berichten"
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr "Geen bovenliggende categorie"
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr "Een categorie kan zichzelf niet bevatten"
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "Categorieën"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr "Objectapp App Hook"
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "Sortering"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr "Geavanceerd"
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "Geselecteerde berichten"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "Willekeurige berichten"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr "Objectapp berigchtenmenu"
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr "Objectapp categoriemenu"
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr "Objectapp auteursmenu"
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "auteurs"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr "Objectapp labelmenu"
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "Labels"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr "Berichtenlijst (standaard)"
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr "Bericht detail"
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr "met subcategorieën"
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "aantal berichten"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr "Gebruikt sjabloon voor de plugin"
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s berichten"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr "Fout 404"
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr "Pagina niet gevonden"
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr "Sorry, maar de opgevraagde pagina is niet gevonden"
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr "Handige links"
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr "Blog-index"
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr "Sitemap"
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "Nieuwste berichten"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "Zoek"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "Zoektermen..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr "Fout 500"
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr "Server fout"
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+"Er is een fout opgetreden. De site administrators zijn via e-mail op de "
+"hoogte gesteld, en het zou binnenkort opgelost moeten zijn. Bedankt voor uw "
+"geduld"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr "Inhoud"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr "Discussies"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr "%(gbobjects)s berichten"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr "%(comments)s reacties"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr "%(objecttypes)s categorieën"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr "%(pingbacks)s pingbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr "%(tags)s labels"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr "%(trackbacks)s trackbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr "%(authors)s auteurs"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr "%(rejects)s afgewezen"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr "Wijzig bericht"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "op"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "Voorbeeld"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr "Geen kladberichten"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr "Bekijk alle kladberichten"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "in"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr "Commentaar op"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr "Wijzig de reactie"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr "Wijzig"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr "Nog geen commentaren"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr "Beheer commentaren"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr "maak een linkback op"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr "wijzig linkback"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr "Nog geen linkbacks"
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr "Vandaag"
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr "Kladberichten"
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr "Snel publiceren"
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr "Titel"
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr "Bewaar als klad"
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr "Reset"
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr "Publiceer"
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr "Nieuwste commentaren"
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr "Recente linkbacks"
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Auteur"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr "Email"
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr "IP"
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr "Reactie"
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr "Bekijk deze reactie"
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr "Markeer deze reactie"
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr "Verwijder deze reactie"
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr "Keur deze reactie goed"
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "Commentaarvoorbeeld"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] "Verbeter de volgende fout."
+msgstr[1] "Verbeter de volgende fouten."
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "Commentaarvoorbeeld"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "Verzend uw bericht"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "Verzend"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "Bedankt voor uw reactie"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "Terug naar de berichtenlijst"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "Geschreven door"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "Toon berichten van %(author)s"
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr "Gepost op"
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "Geen labels"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "Korte URL"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] "%(comment_count)s reactie"
+msgstr[1] "%(comment_count)s reacties"
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr "Schrijf als eerste een reactie!"
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "Reageren niet mogelijk."
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] "%(pingback_count)s pingback"
+msgstr[1] "%(pingback_count)s pingbacks"
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] "%(trackback_count)s trackback"
+msgstr[1] "%(trackback_count)s trackbacks"
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "Auteurslijst"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] "%(gbobject_count)s bericht"
+msgstr[1] "%(gbobject_count)s berichten"
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+"Gebruik - om woorden of zinnen uit te sluiten, ' of \" voor exacte woorden "
+"en AND of OR gecombineerd met () voor complexe zoekopdrachten."
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr "Kalender"
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "Populaire berichten"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "Archief"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr "Hulpmiddelen"
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr "Dashboard"
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr "Plaats een bericht"
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr "Log uit"
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "Categorieënlijst"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr "dagarchieven"
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr "Maandelijkse archieven"
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr "RSS-Feed van discussies voor"
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "RSS-Feed van commentaren voor"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr "RSS-Feed van pingbacks voor"
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr "RSS-Feed van trackbacks voor"
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Volgend bericht"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "Vorig bericht"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "Gerelateerde berichten"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Vergelijkbare berichten"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Reacties"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr "Pingbacks"
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr "Pingbacks beschikbaar."
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr "Pingbacks niet beschikbaar."
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr "Trackbacks"
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr "Trackback URL"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr "Laatste berichten van"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr "de categorie"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr "het label"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr "de auteur"
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr "pagina"
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "RSS-Feed"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "Categorie"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "Label"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Pagina"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "Berichten van %(author)s"
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr "Nog geen berichten."
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr "Pagina %(current_page)s van %(total_page)s"
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "Meer nieuwste berichten"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr "Berichtenpagina"
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "Meer oude berichten"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr "Wijzig de categorie"
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr "Wijzig het label"
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr "Wijzig de auteur"
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr "zoekresultaten voor"
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "RSS-Feed met zoekresultaten van"
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] "%(gbobject_count)s bericht gevonden"
+msgstr[1] "%(gbobject_count)s berichten gevonden"
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "Niks gevonden."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr "Login verplicht"
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr "Uw gebruikersnaam of wachtwoord is niet correct. Probeer opnieuw."
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr "U moet verbonden zijn om dit bericht te kunnen zien"
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr "Inloggen"
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr "Wachtwoord vereist"
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr "Het opgegeven wachtwoord is niet geldig. Probeer opnieuw."
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr "U moet een wachtwoord opgeven om dit bericht te kunnen zien"
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr "Wachtwoord"
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr "Geldig"
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "Berichten per categorieën"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "reactie"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Alle berichten"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "Nog geen categorieën"
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "RSS-Feed van de nieuwste berichten"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "labellijst"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr "Bekijk site"
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr "Beheer site"
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr "Reacties beheren"
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr "Nog geen archieven"
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr "Nog geen autheurs"
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr "Linkback voor"
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "Geen vergelijkbare berichten."
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "De zoekterm is te kort"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "Geen zoekterm gevonden"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "Gebruikersnaam is incorrect"
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "Wachtwoord is ongeldig"
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "Gebruiker is niet beschikbaar"
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr "Gebruiker kan niet %s."
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr "Geen titel"
+
+
diff --git a/objectapp/locale/pl/LC_MESSAGES/django.mo b/objectapp/locale/pl/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..99c231c
--- /dev/null
+++ b/objectapp/locale/pl/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/pl/LC_MESSAGES/django.po b/objectapp/locale/pl/LC_MESSAGES/django.po
new file mode 100644
index 0000000..dff1690
--- /dev/null
+++ b/objectapp/locale/pl/LC_MESSAGES/django.po
@@ -0,0 +1,1194 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# vvarp <vvarpp@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-12 18:21+0000\n"
+"Last-Translator: Fantomas42 <fantomas42@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pl\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "Ostatnie wpisy"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "Najnowsze wpisy na stronie %s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "Wpisy dla kategorii %s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr ""
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr ""
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr ""
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr ""
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr ""
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr ""
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr ""
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr ""
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr ""
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Komentarze do %s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr ""
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr ""
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr ""
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr ""
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr ""
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "tytuł"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr ""
+
+#: models.py:67
+msgid "description"
+msgstr "opis"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "kategoria nadrzędna"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "kategoria"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "kategorie"
+
+#: models.py:101
+msgid "draft"
+msgstr "szkic"
+
+#: models.py:102
+msgid "hidden"
+msgstr "ukryty"
+
+#: models.py:103
+msgid "published"
+msgstr "opublikowany"
+
+#: models.py:107
+msgid "image"
+msgstr ""
+
+#: models.py:108
+msgid "used for illustration"
+msgstr ""
+
+#: models.py:109
+msgid "content"
+msgstr ""
+
+#: models.py:110
+msgid "excerpt"
+msgstr ""
+
+#: models.py:111
+msgid "optional element"
+msgstr ""
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr ""
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr ""
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr ""
+
+#: models.py:126
+msgid "featured"
+msgstr ""
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr ""
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr ""
+
+#: models.py:130
+msgid "creation date"
+msgstr ""
+
+#: models.py:131
+msgid "last update"
+msgstr ""
+
+#: models.py:132
+msgid "start publication"
+msgstr ""
+
+#: models.py:133
+msgid "date start publish"
+msgstr ""
+
+#: models.py:135
+msgid "end publication"
+msgstr ""
+
+#: models.py:136
+msgid "date end publish"
+msgstr ""
+
+#: models.py:139
+msgid "sites publication"
+msgstr ""
+
+#: models.py:141
+msgid "login required"
+msgstr ""
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr ""
+
+#: models.py:143
+msgid "password"
+msgstr ""
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr ""
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr ""
+
+#: models.py:149
+msgid "Default template"
+msgstr ""
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr ""
+
+#: models.py:285
+msgid "gbobject"
+msgstr ""
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr ""
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr ""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr ""
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr ""
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr ""
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr ""
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr ""
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr ""
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr ""
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr ""
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr ""
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr ""
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr ""
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr ""
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr ""
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr ""
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr ""
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr ""
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr ""
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr ""
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr ""
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr ""
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr ""
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr ""
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr ""
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr ""
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr ""
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr ""
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr ""
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr ""
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr ""
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr ""
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr ""
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr ""
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr ""
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr ""
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr ""
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr ""
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr ""
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr ""
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr ""
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr ""
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr ""
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr ""
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr ""
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr ""
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr ""
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr ""
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr ""
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr ""
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr ""
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr ""
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr ""
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr ""
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr ""
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr ""
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr ""
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr ""
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr ""
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr ""
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr ""
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr ""
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr ""
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr ""
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr ""
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Autor"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr ""
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr ""
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr ""
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr ""
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr ""
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr ""
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr ""
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr ""
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr ""
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr ""
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr ""
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr ""
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr ""
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr ""
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr ""
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr ""
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Następny wpis"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "Poprzedni wpis"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Podobne wpisy"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Komentarze"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "Kategoria"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Strona"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "Nic nie znaleziono."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr ""
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr ""
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr ""
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr ""
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr ""
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr ""
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr ""
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr ""
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr ""
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "Wpisów w kategoriach"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr ""
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Wszystkie wpisy"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "Brak kategorii."
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "Kanał RSS najnowszych wpisów"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "Lista tagów"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr ""
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr ""
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr ""
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr ""
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "Brak podobnych wpisów."
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr ""
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr ""
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "Nazwa użytkownika jest nieprawidłowa."
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "Hasło jest nieprawidłowe."
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "Konto użytkownika niedostępne."
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr ""
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr ""
+
+
diff --git a/objectapp/locale/pt_BR/LC_MESSAGES/django.mo b/objectapp/locale/pt_BR/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..6411a1d
--- /dev/null
+++ b/objectapp/locale/pt_BR/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/pt_BR/LC_MESSAGES/django.po b/objectapp/locale/pt_BR/LC_MESSAGES/django.po
new file mode 100644
index 0000000..901f9b1
--- /dev/null
+++ b/objectapp/locale/pt_BR/LC_MESSAGES/django.po
@@ -0,0 +1,1195 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# rogerio rgrlinux <rgrlinux@gmail.com>, 2011.
+# Seta00 <reuben@seta00.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-12 18:21+0000\n"
+"Last-Translator: Fantomas42 <fantomas42@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_BR\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "Últimos artigos"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "Últimos artigos no site %s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "Artigos na categoria %s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr "Últimos artigos na categoria %s"
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "Artigos do autor %s"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr "Últimos artigos por %s"
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "Artigos com a tag %s"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr "Últimos artigos com a tag %s"
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr "Resultados da busca por '%s'"
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr "Os artigos contendo '%s'"
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr "Discussões sobre %s"
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr "Últimas discussões sobre o artigo %s"
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Comentários sobre %s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr "Os comentários mais recentes para o artigo %s"
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr "Pingbacks sobre %s"
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr "Os últimos pingbacks sobre o artigo %s"
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr "Trackbacks sobre %s"
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr "Os últimos trackbacks sobre o artigo %s"
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "título"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "utilizados para publicação"
+
+#: models.py:67
+msgid "description"
+msgstr "descrição"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "categoria pai"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "categoria"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "categorias"
+
+#: models.py:101
+msgid "draft"
+msgstr "projeto"
+
+#: models.py:102
+msgid "hidden"
+msgstr "oculto"
+
+#: models.py:103
+msgid "published"
+msgstr "publicado"
+
+#: models.py:107
+msgid "image"
+msgstr "imagem"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "utilizados para ilustração"
+
+#: models.py:109
+msgid "content"
+msgstr "conteúdo"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "resumo"
+
+#: models.py:111
+msgid "optional element"
+msgstr "elemento opcional"
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "tags"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "artigos relacionados"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "autores"
+
+#: models.py:126
+msgid "featured"
+msgstr "featured"
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "comentário habilitado"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr "linkback habilitado"
+
+#: models.py:130
+msgid "creation date"
+msgstr "data de criação"
+
+#: models.py:131
+msgid "last update"
+msgstr "última atualização"
+
+#: models.py:132
+msgid "start publication"
+msgstr "início da publicação"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "data de início da publicação"
+
+#: models.py:135
+msgid "end publication"
+msgstr "publicação final"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "data de término da publicação"
+
+#: models.py:139
+msgid "sites publication"
+msgstr "publicação sites"
+
+#: models.py:141
+msgid "login required"
+msgstr "é necessário fazer login"
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr "somente os usuários autenticados podem ver este artigo"
+
+#: models.py:143
+msgid "password"
+msgstr "senha"
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr "proteger o artigo com uma senha"
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr "modelo"
+
+#: models.py:149
+msgid "Default template"
+msgstr "modelo padrão"
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr "modelo usado para exibir o artigo"
+
+#: models.py:285
+msgid "gbobject"
+msgstr "artigo"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "artigos"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr "[ %(site)s ] Novo comentário postado em \" %(title)s \""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr "caminho de árvore"
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "Conteúdo"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "Opções"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr "Privacidade"
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr "Discussão"
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr "Publicação"
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr "%(title)s (%(word_count)i palavras)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr "%(title)s (%(comments)i comentários)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "autor (es)"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "categoria (s)"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "tag (s)"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "site (s)"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "é atual"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "é visível"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr "Ver"
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "Ver no site"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "Indisponível"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "url curta"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr "Os artigos selecionados agora pertencem a você."
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "Definir os artigos para o usuário"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr "Os artigos selecionados foram publicados."
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "Definir artigos selecionados conforme publicado"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr "Os artigos selecionados foram escondidos."
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "Definir artigos selecionados como oculto"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr "Os artigos selecionados foram twitados."
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "Tweet artigos selecionados"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr "Comentários foram desativados nos artigos selecionados."
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "Feche os comentários para os artigos selecionados"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr "Linkbacks foram desativados nos artigos selecionados."
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr "Feche os linkbacks para os artigos selecionados"
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr "Os artigos selecionados agora pertencem à data atual."
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr "Colocar os artigos selecionados no topo por hoje"
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr "%(directory)s diretório pingado com sucesso %(success)d artigos."
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "Pingar diretórios para os artigos selecionados"
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr "Nenhuma categoria pai"
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr "A categoria não pode ser pai de si mesmo."
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "Categorias"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr "Objectapp App Hook"
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "Seleção"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr "Avançado"
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "Artigos selecionados"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "Artigos aleatórios"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr "Objectapp Menu de Artigo"
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr "Objectapp Menu de Categoria"
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr "Objectapp Menu de Autor"
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "Autores"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr "Objectapp Menu de Tag"
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "Tags"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr "Lista de artigos (padrão)"
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr "Artigo detalhado"
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr "Incluir subcategorias"
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "número de artigos"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr "Modelo usado para exibir o plugin"
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s artigos"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr "Erro 404"
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr "Página não encontrada"
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr "Desculpe, mas a página solicitada não pode ser encontrada."
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr "Links úteis"
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr "Ãndice do Blog"
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr "Mapa do Site"
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "Artigos recentes"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "Pesquisa"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "Palavras-chave ..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr "Erro 500"
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr "Erro de servidor"
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+"Houve um erro. Foi relatado aos administradores do site via e-mail e deve "
+"ser corrigido em breve. Obrigado pela sua paciência."
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr "Conteúdos"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr "Discussões"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr " %(gbobjects)s artigos"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr "%(comments)s comentários"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr "%(objecttypes)s categorias"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr "%(pingbacks)s pingbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr "%(tags)s tags"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr "%(trackbacks)s trackbacks"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr "%(authors)s autores"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr "%(rejects)s rejeitados"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr "Editar o artigo"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "em"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "Visualizar"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr "Não há entradas de rascunho."
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr "Ver todas as entradas de rascunho"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "em"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr "Comentários sobre"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr "Editar o comentário"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr "Editar"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr "Ainda não há comentários."
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr "Gerenciar os comentários"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr "fez um linkback em"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr "Editar o linkback"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr "Não linkbacks ainda."
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr "Hoje"
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr "Rascunho de artigos"
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr "Publicação rápida"
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr "Título"
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr "Salvar como rascunho"
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr "Reset"
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr "Publicar"
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr "Comentários recentes"
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr "Linkbacks recentes"
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Autor"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr ""
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "Visualizar comentário"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] "Corrija o seguinte erro."
+msgstr[1] "Corrija os seguintes erros."
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "Visualização do comentário"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "Envie seu comentário"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "Enviar"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "Obrigado pelo seu comentário"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "Voltar à lista de artigos"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "Escrito por"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "Mostrar artigos do %(author)s "
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr "Escrito em"
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "Não há tags"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "url curta"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] "%(comment_count)s comentário"
+msgstr[1] "%(comment_count)s comentários"
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr "Seja o primeiro a comentar!"
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "Comentários estão fechados."
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] "%(pingback_count)s pingbacks"
+msgstr[1] "%(pingback_count)s pingbacks"
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] "%(trackback_count)s trackbacks"
+msgstr[1] "%(trackback_count)s trackbacks"
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "Lista de autores"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] "%(gbobject_count)s artigo"
+msgstr[1] "%(gbobject_count)s artigos"
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+"Você pode usar - para excluir palavras ou frases, \"aspas duplas\" para "
+"frases exatas e os operadores AND/OR combinados com parênteses para uma "
+"busca mais complexa."
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr "Calendário"
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "Artigos mais populares"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "Arquivos"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr "Ferramentas"
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr "Dashboard"
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr "Enviar um comentário"
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr "Sair da sessão"
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "Lista de categorias"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr "Arquivos diários"
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr "Arquivos mensais"
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr "RSS Feed das discussões"
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "RSS Feed dos comentários"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr "RSS Feed das pingbacks"
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr "RSS Feed dos trackbacks"
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Próximo artigo"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "Artigo anterior"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "Artigos relacionados"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Artigos semelhantes"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Comentários"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr "Pingbacks"
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr "Pingbacks estão abertas."
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr "Pingbacks estão fechados."
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr "Trackbacks"
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr "Trackback URL"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr "Ultimos artigos"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr "na categoria"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr "na tag"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr "do autor"
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr "na página"
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "RSS Feed"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "Categoria"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "Tag"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Página"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "Artigos por %(author)s"
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr "Não há artigos ainda."
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr "Página %(current_page)s de %(total_page)s"
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "Artigos mais recentes"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr "Página de artigos"
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "Artigos mais antigos"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr "Editar a categoria"
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr "Editar o tag"
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr "Editar o autor"
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr "Resultados da busca por"
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "RSS Feed do resultado da pesquisa de"
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] "%(gbobject_count)s artigo encontrado"
+msgstr[1] "%(gbobject_count)s artigos encontrados"
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "Nada foi encontrado."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr "Login necessário"
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr ""
+"Seu nome de usuário e a senha não correspondem. Por favor, tente novamente."
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr "Você precisa estar conectado para visualizar este comentário."
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr "Login"
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr "Senha necessária"
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr "A senha fornecida não é válida. Por favor, tente novamente."
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr "É preciso fornecer uma senha para visualizar este artigo."
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr "Senha"
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr "Válido"
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "Artigos por categorias"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "comentário"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Todas os artigos"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "Não há categorias ainda."
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "RSS Feed dos últimos artigos"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "Lista de tags"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr ""
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr "Não há arquivos ainda."
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr "Nenhum autor ainda."
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr "Linkback em"
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "Não há artigos semelhantes."
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "O padrão é muito curto"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "Nenhum padrão encontrado"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "Nome de usuário está incorreto."
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "A senha é inválida."
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "Conta de usuário não disponível."
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr "Usuário não pode %s."
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr "Sem título"
+
+
diff --git a/objectapp/locale/ru/LC_MESSAGES/django.mo b/objectapp/locale/ru/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..c763478
--- /dev/null
+++ b/objectapp/locale/ru/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/ru/LC_MESSAGES/django.po b/objectapp/locale/ru/LC_MESSAGES/django.po
new file mode 100644
index 0000000..9a9aadb
--- /dev/null
+++ b/objectapp/locale/ru/LC_MESSAGES/django.po
@@ -0,0 +1,1200 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Julien Fache <fantomas42@gmail.com>, 2011.
+# Max <makc.kekc@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-12 18:21+0000\n"
+"Last-Translator: Fantomas42 <fantomas42@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: ru\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "ПоÑледние запиÑи"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr "ПоÑледние новоÑти Ñайта %s"
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "ЗапиÑи в категории %s"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr "ПоÑледние новоÑти в категории %s"
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "ЗапиÑи от %s"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr "ПоÑледние новоÑти от %s"
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "ЗапиÑи Ñ Ñ‚ÐµÐ³Ð¾Ð¼ %s"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr "ПоÑледние новоÑти Ñ Ñ‚ÐµÐ³Ð¾Ð¼ %s"
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr ""
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr ""
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr "ОбÑуждений %s"
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr "ПоÑледние комментарии к %s"
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "Комментарии к %s"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr "ПоÑледние комментарии к %s"
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr "ПингбÑки к %s"
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr "ПоÑледние пингбÑки к %s"
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr "ТрÑкбÑки к %s"
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr "ПоÑледние Ñ‚Ñ€ÑкбÑки к %s"
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "заголовок"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "иÑпользуетÑÑ Ð¿Ñ€Ð¸ публикации"
+
+#: models.py:67
+msgid "description"
+msgstr "опиÑание"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr "родительÑÐºÐ°Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ"
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "категориÑ"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "категории"
+
+#: models.py:101
+msgid "draft"
+msgstr "черновик"
+
+#: models.py:102
+msgid "hidden"
+msgstr "Ñкрыта"
+
+#: models.py:103
+msgid "published"
+msgstr "опубликована"
+
+#: models.py:107
+msgid "image"
+msgstr "изображение"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "иÑпользуетÑÑ Ð´Ð»Ñ Ð¸Ð»Ð»ÑŽÑтрации"
+
+#: models.py:109
+msgid "content"
+msgstr "Ñодержимое"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "выдержка"
+
+#: models.py:111
+msgid "optional element"
+msgstr "необÑзательный Ñлемент"
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "Ñ‚Ñги"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "ÑвÑзанные запиÑи"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "авторы"
+
+#: models.py:126
+msgid "featured"
+msgstr "ПопулÑрные"
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "комментирование включено"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr "обратные ÑÑылки включены"
+
+#: models.py:130
+msgid "creation date"
+msgstr "дата ÑозданиÑ"
+
+#: models.py:131
+msgid "last update"
+msgstr "поÑледнее обновление"
+
+#: models.py:132
+msgid "start publication"
+msgstr "начало публикации"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "дата начала публикации"
+
+#: models.py:135
+msgid "end publication"
+msgstr "окончание публикации"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "дата Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ Ð¿ÑƒÐ±Ð»Ð¸ÐºÐ°Ñ†Ð¸Ð¸"
+
+#: models.py:139
+msgid "sites publication"
+msgstr "Ñайты Ð´Ð»Ñ Ð¿ÑƒÐ±Ð»Ð¸ÐºÐ°Ñ†Ð¸Ð¸"
+
+#: models.py:141
+msgid "login required"
+msgstr "требуетÑÑ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ"
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr "только авторизованные пользователи могут проÑматривать запиÑÑŒ"
+
+#: models.py:143
+msgid "password"
+msgstr "пароль"
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr "защитить запиÑÑŒ паролем"
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr "шаблон"
+
+#: models.py:149
+msgid "Default template"
+msgstr "Шаблон по умолчанию"
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr "шаблон, иÑпользуемый Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñи"
+
+#: models.py:285
+msgid "gbobject"
+msgstr "запиÑÑŒ"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "запиÑи"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr "[%(site)s] Ðовый комментарий к \"%(title)s\""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr "путь"
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "Содержимое"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "Опции"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr "приватноÑÑ‚ÑŒ"
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr "ОбÑуждение"
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr "ПубликациÑ"
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr "%(title)s (%(word_count)i Ñлов)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr "%(title)s (%(comments)i комментариев)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "автор(ы)"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "категориÑ(категории)"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "Ñ‚Ñг(и)"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "Ñайт(Ñ‹)"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "актуальна"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "видима"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr ""
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "Смотреть на Ñайте"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "ÐедоÑтупно"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ°Ñ ÑÑылка"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr ""
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "УÑтановить запиÑи пользователю"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr ""
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "Пометить выделенные запиÑи как опубликованные"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr ""
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "Пометить выделенные запиÑи как Ñкрытые"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr ""
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "Отправить выбранные запиÑи в твиттер"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "Закрыть комментирование Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… запиÑей"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr "Запретить обратные ÑÑылки Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… запиÑей"
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr ""
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr "ПоднÑÑ‚ÑŒ выбранные запиÑи на текущую дату"
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr "%(directory)s уÑпешно оповещены о %(success)d запиÑей"
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "ОповеÑтить каталоги о выбранных запиÑÑÑ…"
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr "ОтÑутÑтвует родительÑÐºÐ°Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ"
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr "ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ð½Ðµ может быть унаÑледована от Ñамой ÑебÑ."
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "Категории"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr "Objectapp App Hook"
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "Сортировка"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr "РаÑширенный"
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "Выбранные запиÑи"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "Случайные запиÑи"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr "Objectapp меню запиÑи"
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr "Objectapp меню каталога"
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr "Objectapp меню автора"
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "Ðвторы"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr "Objectapp меню тега"
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "Теги"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr "СпиÑок запиÑей (по умолчанию)"
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr "ПроÑмотреть запиÑÑŒ подробнее"
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr "включает подкатегории"
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "количеÑтво запиÑей"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr "Шаблон Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð°"
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s запиÑей"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr "Ошибка 404"
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr "Страница не найдена"
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr "Извините, Ð·Ð°Ð¿Ñ€Ð°ÑˆÐ¸Ð²Ð°ÐµÐ¼Ð°Ñ Ñтраница не найдена."
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr "Полезные ÑÑылки"
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr "Содержание блога"
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr "Карта Ñайта"
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "ПоÑледние запиÑи"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "ПоиÑк"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "Ключевые Ñлова..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr "Ошибка 500"
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr "Ошибка Ñервера"
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+"Произошла ошибка. ÐдминиÑтраторы Ñайта получили уведомление на e-mail, и "
+"решают проблему. СпаÑибо за ваше терпение."
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr "Содержимое"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr "ОбÑуждениÑ"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr "%(gbobjects)s запиÑей"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr "%(comments)s комментариев "
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr "%(objecttypes)s категорий"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr "%(pingbacks)s пингбÑков"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr "%(tags)s тегов"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr "%(trackbacks)s Ñ‚Ñ€ÑкбÑков"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr "%(authors)s авторов"
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr "%(rejects)s отклонены"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr "Изменить запиÑÑŒ"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "на"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "ПредпроÑмотр"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr "ЗапиÑи отÑутÑтвуют."
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr "ПроÑмотреть вÑе запиÑи"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "в"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr "Комментарий к "
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr "Редактировать комментарий"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr "Редактировать"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr "Еще нет комментариев."
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr "Модерировать комментарии"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr "включить обратные ÑÑылки"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr "Редактировать обратную ÑÑылку"
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr "Обратных ÑÑылок нет."
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr "СегоднÑ"
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr "СвÑзанные запиÑи"
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr "БыÑÑ‚Ñ€Ð°Ñ Ð¿ÑƒÐ±Ð»Ð¸ÐºÐ°Ñ†Ð¸Ñ"
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr "Заголовок"
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr "Сохранить как черновик"
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr "СброÑ"
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr "Опубликована"
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr "ПоÑледние комментарии"
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr "ПоÑледние обратные ÑÑылки"
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "Ðвтор"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr ""
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "ПредпроÑмотр комментариÑ"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] "ПожалуйÑта, иÑправьте Ñледующую ошибку."
+msgstr[1] "ПожалуйÑта, иÑправьте Ñледующие ошибки."
+msgstr[2] "ПожалуйÑта, иÑправьте Ñледующие ошибки."
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "Предварительный проÑмотр комментариÑ"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "ÐапиÑать комментарий"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "Опубликовать"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "СпаÑибо за комментарий"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "ВернутьÑÑ Ðº ÑпиÑку запиÑей"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "Ðвтор"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "Показать запиÑи %(author)s"
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "Ðет тегов"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "ÐšÐ¾Ñ€Ð¾Ñ‚ÐºÐ°Ñ ÑÑылка"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] "%(comment_count)s комментарий"
+msgstr[1] "%(comment_count)s комментариев"
+msgstr[2] "%(comment_count)s комментарии"
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr "ОÑтавьте первый комментарий"
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "Комментирование отключено."
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] "%(pingback_count)s оповещение"
+msgstr[1] "%(pingback_count)s оповещений"
+msgstr[2] "%(pingback_count)s оповещениÑ"
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] "%(trackback_count)s архив"
+msgstr[1] "%(trackback_count)s архивов"
+msgstr[2] "%(trackback_count)s архивы"
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "СпиÑок авторов"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] "%(gbobject_count)s запиÑÑŒ"
+msgstr[1] "%(gbobject_count)s запиÑей"
+msgstr[2] "%(gbobject_count)s запиÑи"
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+"Ð”Ð»Ñ ÐºÐ¾Ð¼Ð¿Ð»ÐµÐºÑного поиÑка вы можете иÑпользовать кавычки и двойные кавычки, а "
+"также операторы AND/OR."
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr "Календарь"
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "ПопулÑрные запиÑи"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "Ðрхив"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr "ИнÑтрументы"
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr "Панель управлениÑ"
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr "Опубликовать запиÑÑŒ"
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr "Выйти"
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "СпиÑок категорий"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr "Ежедневные архивы"
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr "Ðрхив за меÑÑц"
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr "Включена RSS раÑÑылка обÑуждений"
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "RSS лента комментариев к"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr "RSS лента пинбÑков к"
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr "RSS лента Ñ‚Ñ€ÑкбÑков к"
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "СвÑзанные запиÑи"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "Похожие запиÑи"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "Комментарии"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr "ПингбÑки"
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr "ÐžÐ¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹."
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr "ПингбÑки отключены."
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr "ТрÑкбÑки"
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr "URL архива"
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "RSS лента"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "КатегориÑ"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "Тег"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "Страница"
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "ЗапиÑи от %(author)s"
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr "ЗапиÑей еще нет."
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr "Страница %(current_page)s из %(total_page)s"
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "Самые новые запиÑи"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr "Страница запиÑей"
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "Самые Ñтарые запиÑи"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr "Редактировать категорию"
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr "Редактировать тег"
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr "Редактировать автора"
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "RSS лента результатов поиÑка "
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] "%(gbobject_count)s запиÑÑŒ найдена"
+msgstr[1] "%(gbobject_count)s запиÑей найдено"
+msgstr[2] "%(gbobject_count)s запиÑи найдены"
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "Ðичего не найдено."
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr "ТребуетÑÑ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ"
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr ""
+"Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸ пароль не Ñовпадают. ПожалуйÑта, попробуйте еще раз."
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr "Ð’Ñ‹ должны быть подключены, чтобы поÑмотреть Ñту запиÑÑŒ."
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr "Войти"
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr "ТребуетÑÑ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ"
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr "Ðеправильный пароль. ПожалуйÑта, попробуйте еще раз."
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr "Ð’Ñ‹ должны ввеÑти пароль Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра Ñтой запиÑи."
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr "Пароль"
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr "Проверка пройдена"
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr "ЗапиÑи в категории"
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "комментарий"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr "Ð’Ñе запиÑи"
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr "Категории отÑутÑтвуют."
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr "RSS лента поÑледних запиÑей"
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "СпиÑок тегов"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr ""
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr "Ðет запиÑей в архиве."
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr "Ðвторов нет."
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr "Обратные ÑÑылки включены"
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "Ðет похожих запиÑей."
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ñлишком короткий"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "Ðечего иÑкать"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr "Ðеверное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ."
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr "Ðеверный пароль."
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¾Ñ‚ÑутÑтвует"
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr "Пользователь не может %s."
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr "Заголовок отÑутÑтвует"
+
+
diff --git a/objectapp/locale/zh_CN/LC_MESSAGES/django.mo b/objectapp/locale/zh_CN/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..9940d4e
--- /dev/null
+++ b/objectapp/locale/zh_CN/LC_MESSAGES/django.mo
Binary files differ
diff --git a/objectapp/locale/zh_CN/LC_MESSAGES/django.po b/objectapp/locale/zh_CN/LC_MESSAGES/django.po
new file mode 100644
index 0000000..e988d35
--- /dev/null
+++ b/objectapp/locale/zh_CN/LC_MESSAGES/django.po
@@ -0,0 +1,1182 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Julien Fache <fantomas42@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: django-blog-objectapp\n"
+"Report-Msgid-Bugs-To: https://github.com/Fantomas42/django-blog-objectapp/issues\n"
+"POT-Creation-Date: 2011-04-12 13:21-0500\n"
+"PO-Revision-Date: 2011-04-12 18:21+0000\n"
+"Last-Translator: Fantomas42 <fantomas42@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: zh_CN\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+#: feeds.py:102 plugins/cms_plugins.py:23
+#: templates/objectapp/gbobject_archive.html:4
+#: templates/objectapp/gbobject_archive.html:25 templates/objectapp/gbobject_list.html:19
+#: tests/feeds.py:115
+msgid "Latest gbobjects"
+msgstr "最新日志"
+
+#: feeds.py:106 tests/feeds.py:117
+#, python-format
+msgid "The latest gbobjects for the site %s"
+msgstr ""
+
+#: feeds.py:126 tests/feeds.py:126
+#, python-format
+msgid "Gbobjects for the Objecttype %s"
+msgstr "类别 %s 的日志"
+
+#: feeds.py:130 tests/feeds.py:128
+#, python-format
+msgid "The latest gbobjects for the Objecttype %s"
+msgstr ""
+
+#: feeds.py:150 tests/feeds.py:137
+#, python-format
+msgid "Gbobjects for author %s"
+msgstr "作者 %s 的日志"
+
+#: feeds.py:154 tests/feeds.py:139
+#, python-format
+msgid "The latest gbobjects by %s"
+msgstr ""
+
+#: feeds.py:175 tests/feeds.py:149
+#, python-format
+msgid "Gbobjects for the tag %s"
+msgstr "æ ‡ç­¾å« %s 的日志"
+
+#: feeds.py:179 tests/feeds.py:151
+#, python-format
+msgid "The latest gbobjects for the tag %s"
+msgstr ""
+
+#: feeds.py:199 tests/feeds.py:160
+#, python-format
+msgid "Results of the search for '%s'"
+msgstr ""
+
+#: feeds.py:203 tests/feeds.py:162
+#, python-format
+msgid "The gbobjects containing the pattern '%s'"
+msgstr ""
+
+#: feeds.py:245 tests/feeds.py:178
+#, python-format
+msgid "Discussions on %s"
+msgstr ""
+
+#: feeds.py:249 tests/feeds.py:180
+#, python-format
+msgid "The latest discussions for the gbobject %s"
+msgstr ""
+
+#: feeds.py:267 tests/feeds.py:190
+#, python-format
+msgid "Comments on %s"
+msgstr "对于 %s 的评论"
+
+#: feeds.py:271 tests/feeds.py:192
+#, python-format
+msgid "The latest comments for the gbobject %s"
+msgstr ""
+
+#: feeds.py:301 tests/feeds.py:206
+#, python-format
+msgid "Pingbacks on %s"
+msgstr ""
+
+#: feeds.py:305 tests/feeds.py:208
+#, python-format
+msgid "The latest pingbacks for the gbobject %s"
+msgstr ""
+
+#: feeds.py:323 tests/feeds.py:218
+#, python-format
+msgid "Trackbacks on %s"
+msgstr ""
+
+#: feeds.py:327 tests/feeds.py:220
+#, python-format
+msgid "The latest trackbacks for the gbobject %s"
+msgstr ""
+
+#: models.py:64 models.py:105 admin/gbobject.py:73
+msgid "title"
+msgstr "标题"
+
+#: models.py:65 models.py:119
+msgid "used for publication"
+msgstr "用于å‘布"
+
+#: models.py:67
+msgid "description"
+msgstr "æè¿°"
+
+#: models.py:70 admin/forms.py:18
+msgid "parent Objecttype"
+msgstr ""
+
+#: models.py:95
+msgid "Objecttype"
+msgstr "类别"
+
+#: models.py:96 models.py:114 admin/forms.py:47 plugins/models.py:24
+msgid "objecttypes"
+msgstr "类别"
+
+#: models.py:101
+msgid "draft"
+msgstr "è‰ç¨¿"
+
+#: models.py:102
+msgid "hidden"
+msgstr "éšè—"
+
+#: models.py:103
+msgid "published"
+msgstr "å‘布"
+
+#: models.py:107
+msgid "image"
+msgstr "图片"
+
+#: models.py:108
+msgid "used for illustration"
+msgstr "用于æ’图"
+
+#: models.py:109
+msgid "content"
+msgstr "内容"
+
+#: models.py:110
+msgid "excerpt"
+msgstr "摘è¦"
+
+#: models.py:111
+msgid "optional element"
+msgstr "å¯é€‰é¡¹"
+
+#: models.py:113 plugins/models.py:30
+msgid "tags"
+msgstr "标签"
+
+#: models.py:116
+msgid "related gbobjects"
+msgstr "相关日志"
+
+#: models.py:123 plugins/models.py:28
+msgid "authors"
+msgstr "作者"
+
+#: models.py:126
+msgid "featured"
+msgstr ""
+
+#: models.py:127 admin/gbobject.py:124
+msgid "comment enabled"
+msgstr "å…许评论"
+
+#: models.py:128
+msgid "linkback enabled"
+msgstr ""
+
+#: models.py:130
+msgid "creation date"
+msgstr "创建日期"
+
+#: models.py:131
+msgid "last update"
+msgstr "最åŽæ›´æ–°"
+
+#: models.py:132
+msgid "start publication"
+msgstr "开始å‘布"
+
+#: models.py:133
+msgid "date start publish"
+msgstr "开始å‘布日期"
+
+#: models.py:135
+msgid "end publication"
+msgstr "结æŸå‘布"
+
+#: models.py:136
+msgid "date end publish"
+msgstr "结æŸå‘布日期"
+
+#: models.py:139
+msgid "sites publication"
+msgstr "å‘布网站"
+
+#: models.py:141
+msgid "login required"
+msgstr ""
+
+#: models.py:142
+msgid "only authenticated users can view the gbobject"
+msgstr ""
+
+#: models.py:143
+msgid "password"
+msgstr ""
+
+#: models.py:144
+msgid "protect the gbobject with a password"
+msgstr ""
+
+#: models.py:146 plugins/models.py:34 plugins/models.py:58
+#: plugins/models.py:80
+msgid "template"
+msgstr ""
+
+#: models.py:149
+msgid "Default template"
+msgstr ""
+
+#: models.py:151
+msgid "template used to display the gbobject"
+msgstr ""
+
+#: models.py:285
+msgid "gbobject"
+msgstr "日志"
+
+#: models.py:286 plugins/cms_plugins.py:21 plugins/cms_plugins.py:92
+#: plugins/cms_plugins.py:114 plugins/models.py:57
+#: templates/objectapp/base.html:40
+msgid "gbobjects"
+msgstr "日志"
+
+#: moderator.py:45 moderator.py:72
+#, python-format
+msgid "[%(site)s] New comment posted on \"%(title)s\""
+msgstr ""
+
+#: admin/Objecttype.py:30
+msgid "tree path"
+msgstr ""
+
+#: admin/gbobject.py:27 plugins/admin.py:17
+#: templates/admin/objectapp/widgets/quickpost.html:17
+msgid "Content"
+msgstr "内容"
+
+#: admin/gbobject.py:29 plugins/admin.py:19
+msgid "Options"
+msgstr "选项"
+
+#: admin/gbobject.py:34 plugins/admin.py:24
+msgid "Privacy"
+msgstr ""
+
+#: admin/gbobject.py:36 plugins/admin.py:26
+msgid "Discussion"
+msgstr ""
+
+#: admin/gbobject.py:38 plugins/admin.py:28
+msgid "Publication"
+msgstr ""
+
+#: admin/gbobject.py:66
+#, python-format
+msgid "%(title)s (%(word_count)i words)"
+msgstr "%(title)s (%(word_count)i 个字符)"
+
+#: admin/gbobject.py:70
+#, python-format
+msgid "%(title)s (%(comments)i comments)"
+msgstr "%(title)s (%(comments)i æ¡è¯„论)"
+
+#: admin/gbobject.py:86
+msgid "author(s)"
+msgstr "作者"
+
+#: admin/gbobject.py:99
+msgid "Objecttype(s)"
+msgstr "类别"
+
+#: admin/gbobject.py:111
+msgid "tag(s)"
+msgstr "标签"
+
+#: admin/gbobject.py:118
+msgid "site(s)"
+msgstr "网站"
+
+#: admin/gbobject.py:130
+msgid "is actual"
+msgstr "å‘布期间"
+
+#: admin/gbobject.py:136
+msgid "is visible"
+msgstr "是å¦å¯è§"
+
+#: admin/gbobject.py:141
+msgid "View"
+msgstr ""
+
+#: admin/gbobject.py:143
+msgid "View on site"
+msgstr "æµè§ˆç«™ç‚¹"
+
+#: admin/gbobject.py:149 templates/objectapp/_gbobject_detail.html:64
+msgid "Unavailable"
+msgstr "ä¸å­˜åœ¨"
+
+#: admin/gbobject.py:153
+msgid "short url"
+msgstr "缩略URL"
+
+#: admin/gbobject.py:207
+msgid "The selected gbobjects now belong to you."
+msgstr ""
+
+#: admin/gbobject.py:208
+msgid "Set the gbobjects to the user"
+msgstr "设置自己为所选日志的作者"
+
+#: admin/gbobject.py:214
+msgid "The selected gbobjects are now marked as published."
+msgstr ""
+
+#: admin/gbobject.py:215
+msgid "Set gbobjects selected as published"
+msgstr "å‘布所选日志"
+
+#: admin/gbobject.py:220
+msgid "The selected gbobjects are now marked as hidden."
+msgstr ""
+
+#: admin/gbobject.py:221
+msgid "Set gbobjects selected as hidden"
+msgstr "éšè—所选日志"
+
+#: admin/gbobject.py:234
+msgid "The selected gbobjects have been tweeted."
+msgstr ""
+
+#: admin/gbobject.py:235
+msgid "Tweet gbobjects selected"
+msgstr "推特所选日志"
+
+#: admin/gbobject.py:240
+msgid "Comments are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:241
+msgid "Close the comments for selected gbobjects"
+msgstr "关闭所选日志的评论功能"
+
+#: admin/gbobject.py:247
+msgid "Linkbacks are now closed for selected gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:248
+msgid "Close the linkbacks for selected gbobjects"
+msgstr ""
+
+#: admin/gbobject.py:254
+msgid "The selected gbobjects are now set at the current date."
+msgstr ""
+
+#: admin/gbobject.py:255
+msgid "Put the selected gbobjects on top at the current date"
+msgstr ""
+
+#: admin/gbobject.py:272
+#, python-format
+msgid "%(directory)s directory succesfully pinged %(success)d gbobjects."
+msgstr ""
+
+#: admin/gbobject.py:275
+msgid "Ping Directories for selected gbobjects"
+msgstr "æ交所选日志到æœç´¢å¼•æ“Ž"
+
+#: admin/forms.py:20
+msgid "No parent Objecttype"
+msgstr ""
+
+#: admin/forms.py:35
+msgid "A Objecttype cannot be parent of itself."
+msgstr ""
+
+#: admin/forms.py:46 plugins/menu.py:70 templates/objectapp/base.html:22
+#: templates/objectapp/Objecttype_list.html:6 templates/objectapp/sitemap.html:50
+#: templatetags/zbreadcrumbs.py:49
+msgid "Objecttypes"
+msgstr "类别"
+
+#: plugins/cms_app.py:12
+msgid "Objectapp App Hook"
+msgstr ""
+
+#: plugins/cms_plugins.py:33
+msgid "Sorting"
+msgstr "分类"
+
+#: plugins/cms_plugins.py:43
+msgid "Advanced"
+msgstr ""
+
+#: plugins/cms_plugins.py:94
+msgid "Selected gbobjects"
+msgstr "所选日志"
+
+#: plugins/cms_plugins.py:116 templates/objectapp/base.html:59
+msgid "Random gbobjects"
+msgstr "éšæœºæ—¥å¿—"
+
+#: plugins/menu.py:19
+msgid "Objectapp Gbobject Menu"
+msgstr ""
+
+#: plugins/menu.py:65
+msgid "Objectapp Objecttype Menu"
+msgstr ""
+
+#: plugins/menu.py:82
+msgid "Objectapp Author Menu"
+msgstr ""
+
+#: plugins/menu.py:87 templates/objectapp/author_list.html:6
+#: templates/objectapp/base.html:26 templatetags/zbreadcrumbs.py:46
+msgid "Authors"
+msgstr "作者"
+
+#: plugins/menu.py:100
+msgid "Objectapp Tag Menu"
+msgstr ""
+
+#: plugins/menu.py:105 templates/admin/objectapp/widgets/quickpost.html:25
+#: templates/objectapp/_gbobject_detail.html:47 templates/objectapp/base.html:34
+#: templates/objectapp/tag_list.html:6 templatetags/zbreadcrumbs.py:43
+msgid "Tags"
+msgstr "标签"
+
+#: plugins/models.py:16
+msgid "Gbobject list (default)"
+msgstr ""
+
+#: plugins/models.py:17
+msgid "Gbobject detailed"
+msgstr ""
+
+#: plugins/models.py:27
+msgid "include subobjecttypes"
+msgstr ""
+
+#: plugins/models.py:33 plugins/models.py:79
+msgid "number of gbobjects"
+msgstr "日志数é‡"
+
+#: plugins/models.py:36 plugins/models.py:60 plugins/models.py:82
+msgid "Template used to display the plugin"
+msgstr ""
+
+#: plugins/models.py:51 plugins/models.py:73 plugins/models.py:85
+#, python-format
+msgid "%s gbobjects"
+msgstr "%s 日志"
+
+#: templates/404.html:5
+msgid "Error 404"
+msgstr ""
+
+#: templates/404.html:8 templates/404.html.py:11
+msgid "Page not found"
+msgstr ""
+
+#: templates/404.html:13
+msgid "Sorry, but the requested page could not be found."
+msgstr ""
+
+#: templates/404.html:15 templates/500.html:20
+msgid "Useful links"
+msgstr ""
+
+#: templates/404.html:19 templates/404.html.py:20 templates/500.html:24
+#: templates/500.html.py:25
+msgid "Blog index"
+msgstr ""
+
+#: templates/404.html:24 templates/404.html.py:25 templates/500.html:29
+#: templates/500.html.py:30 templates/objectapp/sitemap.html:4
+#: templates/objectapp/sitemap.html.py:7 templates/objectapp/skeleton.html:36
+#: templates/objectapp/skeleton.html.py:37
+msgid "Sitemap"
+msgstr ""
+
+#: templates/404.html:31 templates/500.html:36 templates/objectapp/base.html:47
+msgid "Recent gbobjects"
+msgstr "最近日志"
+
+#: templates/404.html:36 templates/500.html:41 templates/objectapp/base.html:10
+msgid "Search"
+msgstr "æœç´¢"
+
+#: templates/404.html:40 templates/500.html:45 templates/objectapp/base.html:13
+msgid "Keywords..."
+msgstr "关键è¯..."
+
+#: templates/500.html:10
+msgid "Error 500"
+msgstr ""
+
+#: templates/500.html:13 templates/500.html.py:16
+msgid "Server error"
+msgstr ""
+
+#: templates/500.html:18
+msgid ""
+"There's been an error. It's been reported to the site administrators via "
+"e-mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:4
+msgid "Contents"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:5
+#: templates/objectapp/_gbobject_detail.html:69
+msgid "Discussions"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:10
+#, python-format
+msgid "%(gbobjects)s gbobjects"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:15
+#, python-format
+msgid "%(comments)s comments"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:22
+#, python-format
+msgid "%(objecttypes)s objecttypes"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:25
+#, python-format
+msgid "%(pingbacks)s pingbacks"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:30
+#, python-format
+msgid "%(tags)s tags"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:33
+#, python-format
+msgid "%(trackbacks)s trackbacks"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:38
+#, python-format
+msgid "%(authors)s authors"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_content_stats.html:43
+#, python-format
+msgid "%(rejects)s rejected"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:7
+#: templates/objectapp/gbobject_detail.html:186
+#: templates/objectapp/gbobject_detail.html:187
+msgid "Edit the gbobject"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:10
+#: templates/comments/objectapp_gbobject_preview.html:25
+#: templates/feeds/comment_title.html:2
+#: templates/feeds/discussion_title.html:2
+#: templates/feeds/pingback_title.html:2
+#: templates/feeds/trackback_title.html:2
+#: templates/objectapp/_gbobject_detail.html:18
+#: templates/objectapp/gbobject_detail.html:91
+#: templates/objectapp/gbobject_detail.html:126
+#: templates/objectapp/gbobject_detail.html:157
+msgid "on"
+msgstr "在"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:16
+#: templates/comments/objectapp/gbobject/form.html:19
+msgid "Preview"
+msgstr "预览"
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:23
+msgid "No draft gbobjects."
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:32
+#: templates/admin/objectapp/widgets/_draft_gbobjects.html:33
+msgid "View all draft gbobjects"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:11
+#: templates/objectapp/_gbobject_detail.html:24
+#: templates/objectapp/tags/recent_comments.html:7
+#: templates/objectapp/tags/recent_linkbacks.html:8
+msgid "in"
+msgstr "在"
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:14
+#: templates/objectapp/tags/recent_comments.html:9
+msgid "Comment on"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:23
+msgid "Edit the comment"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:24
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:19
+msgid "Edit"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:31
+#: templates/objectapp/_gbobject_detail.html:77
+#: templates/objectapp/gbobject_detail.html:107
+#: templates/objectapp/tags/recent_comments.html:16
+msgid "No comments yet."
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_comments.html:40
+#: templates/admin/objectapp/widgets/_recent_comments.html:41
+msgid "Manage the comments"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:8
+msgid "made a linkback on"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:18
+msgid "Edit the linkback"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/_recent_linkbacks.html:26
+#: templates/objectapp/tags/recent_linkbacks.html:17
+msgid "No linkbacks yet."
+msgstr ""
+
+#: templates/admin/objectapp/widgets/content_stats.html:6
+#: templates/admin/objectapp/widgets/content_stats.html:7
+msgid "Today"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:6
+#: templates/admin/objectapp/widgets/draft_gbobjects.html:7
+msgid "Draft gbobjects"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:5
+#: templates/admin/objectapp/widgets/quickpost.html:6
+msgid "Quick publishing"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:9
+msgid "Title"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:33
+msgid "Save as draft"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:34
+msgid "Reset"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/quickpost.html:35
+msgid "Publish"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/recent_comments.html:6
+#: templates/admin/objectapp/widgets/recent_comments.html:7
+#: templates/objectapp/base.html:51
+msgid "Recent comments"
+msgstr ""
+
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:6
+#: templates/admin/objectapp/widgets/recent_linkbacks.html:7
+#: templates/objectapp/base.html:55
+msgid "Recent linkbacks"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:1
+#: templates/comments/comment_reply_email.txt:1
+#: templates/objectapp/gbobject_list.html:19
+msgid "Author"
+msgstr "作者"
+
+#: templates/comments/comment_notification_email.txt:2
+msgid "Email"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:3
+msgid "IP"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:5
+#: templates/comments/comment_reply_email.txt:3
+msgid "Comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:8
+#: templates/comments/comment_reply_email.txt:6
+msgid "View this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:10
+msgid "Flag this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:12
+msgid "Delete this comment"
+msgstr ""
+
+#: templates/comments/comment_notification_email.txt:14
+msgid "Approve this comment"
+msgstr ""
+
+#: templates/comments/objectapp_gbobject_preview.html:4
+msgid "Comment preview"
+msgstr "评论预览"
+
+#: templates/comments/objectapp_gbobject_preview.html:9
+msgid "Please correct following error."
+msgid_plural "Please correct following errors."
+msgstr[0] ""
+
+#: templates/comments/objectapp_gbobject_preview.html:12
+msgid "Preview of the comment"
+msgstr "预览评论"
+
+#: templates/comments/objectapp/gbobject/form.html:7
+msgid "Post your comment"
+msgstr "å‘表评论"
+
+#: templates/comments/objectapp/gbobject/form.html:18
+msgid "Post"
+msgstr "å‘表"
+
+#: templates/comments/objectapp/gbobject/posted.html:4
+#: templates/comments/objectapp/gbobject/posted.html:7
+msgid "Thanks for your comment"
+msgstr "感谢你的评论"
+
+#: templates/comments/objectapp/gbobject/posted.html:9
+#: templates/comments/objectapp/gbobject/posted.html:10
+msgid "Return to gbobject list"
+msgstr "返回日志列表"
+
+#: templates/objectapp/_gbobject_detail.html:11
+msgid "Written by"
+msgstr "作者是"
+
+#: templates/objectapp/_gbobject_detail.html:15
+#, python-format
+msgid "Show %(author)s gbobjects"
+msgstr "显示 %(author)s 的日志"
+
+#: templates/objectapp/_gbobject_detail.html:20
+msgid "Written on"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:53
+msgid "No tags"
+msgstr "没有标签"
+
+#: templates/objectapp/_gbobject_detail.html:58
+msgid "Short url"
+msgstr "缩略URL"
+
+#: templates/objectapp/_gbobject_detail.html:73
+#, python-format
+msgid "%(comment_count)s comment"
+msgid_plural "%(comment_count)s comments"
+msgstr[0] ""
+
+#: templates/objectapp/_gbobject_detail.html:79
+msgid "Be first to comment!"
+msgstr ""
+
+#: templates/objectapp/_gbobject_detail.html:82
+#: templates/objectapp/gbobject_detail.html:103
+#: templates/objectapp/gbobject_detail.html:109
+msgid "Comments are closed."
+msgstr "评论已关闭"
+
+#: templates/objectapp/_gbobject_detail.html:89
+#, python-format
+msgid "%(pingback_count)s pingback"
+msgid_plural "%(pingback_count)s pingbacks"
+msgstr[0] ""
+
+#: templates/objectapp/_gbobject_detail.html:96
+#, python-format
+msgid "%(trackback_count)s trackback"
+msgid_plural "%(trackback_count)s trackbacks"
+msgstr[0] ""
+
+#: templates/objectapp/author_list.html:4 templates/objectapp/author_list.html:9
+msgid "Author list"
+msgstr "作者列表"
+
+#: templates/objectapp/author_list.html:18 templates/objectapp/Objecttype_list.html:14
+#: templates/objectapp/sitemap.html:55 templates/objectapp/tag_list.html:16
+#: templates/objectapp/tags/authors.html:8
+#: templates/objectapp/tags/objecttypes.html:6
+#, python-format
+msgid "%(gbobject_count)s gbobject"
+msgid_plural "%(gbobject_count)s gbobjects"
+msgstr[0] ""
+
+#: templates/objectapp/base.html:15
+msgid ""
+"You can use - to exclude words or phrases, &quot;double quotes&quot; for "
+"exact phrases and the AND/OR boolean operators combined with parenthesis for"
+" complex searchs."
+msgstr ""
+
+#: templates/objectapp/base.html:30
+msgid "Calendar"
+msgstr ""
+
+#: templates/objectapp/base.html:63
+msgid "Popular gbobjects"
+msgstr "热门日志"
+
+#: templates/objectapp/base.html:67 templates/objectapp/gbobject_archive_day.html:4
+#: templates/objectapp/gbobject_archive_day.html:6
+#: templates/objectapp/gbobject_archive_day.html:9
+#: templates/objectapp/gbobject_archive_month.html:4
+#: templates/objectapp/gbobject_archive_month.html:6
+#: templates/objectapp/gbobject_archive_month.html:9
+#: templates/objectapp/gbobject_archive_year.html:4
+#: templates/objectapp/gbobject_archive_year.html:6
+#: templates/objectapp/gbobject_archive_year.html:9
+#: templates/objectapp/tags/archives_gbobjects.html:5
+#: templates/objectapp/tags/archives_gbobjects_link.html:2
+#: templates/objectapp/tags/archives_gbobjects_tree.html:7
+#: templates/objectapp/tags/archives_gbobjects_tree.html:14
+#: templates/objectapp/tags/archives_gbobjects_tree.html:24
+msgid "Archives"
+msgstr "å½’æ¡£"
+
+#: templates/objectapp/base.html:72
+msgid "Tools"
+msgstr ""
+
+#: templates/objectapp/base.html:76 templates/objectapp/base.html.py:77
+msgid "Dashboard"
+msgstr ""
+
+#: templates/objectapp/base.html:83 templates/objectapp/base.html.py:84
+msgid "Post an gbobject"
+msgstr ""
+
+#: templates/objectapp/base.html:91 templates/objectapp/base.html.py:92
+msgid "Log out"
+msgstr ""
+
+#: templates/objectapp/Objecttype_list.html:4 templates/objectapp/Objecttype_list.html:9
+msgid "Objecttype list"
+msgstr "类别列表"
+
+#: templates/objectapp/gbobject_archive_month.html:13
+msgid "Daily archives"
+msgstr ""
+
+#: templates/objectapp/gbobject_archive_year.html:13
+#: templates/objectapp/sitemap.html:67
+msgid "Monthly archives"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:10
+msgid "RSS Feed of discussions on"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:11
+msgid "RSS Feed of comments on"
+msgstr "RSS æºï¼ˆåŸºäºŽè¯„论)"
+
+#: templates/objectapp/gbobject_detail.html:12
+msgid "RSS Feed of pingbacks on"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:13
+msgid "RSS Feed of trackbacks on"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:29
+msgid "Next gbobject"
+msgstr "下一篇"
+
+#: templates/objectapp/gbobject_detail.html:43
+msgid "Previous gbobject"
+msgstr "上一篇"
+
+#: templates/objectapp/gbobject_detail.html:56
+msgid "Related gbobjects"
+msgstr "相关日志"
+
+#: templates/objectapp/gbobject_detail.html:67
+msgid "Similar gbobjects"
+msgstr "类似日志"
+
+#: templates/objectapp/gbobject_detail.html:75
+msgid "Comments"
+msgstr "评论"
+
+#: templates/objectapp/gbobject_detail.html:117
+msgid "Pingbacks"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:139
+msgid "Pingbacks are open."
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:141
+msgid "Pingbacks are closed."
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:148
+msgid "Trackbacks"
+msgstr ""
+
+#: templates/objectapp/gbobject_detail.html:170
+msgid "Trackback URL"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "Latest gbobjects for"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the Objecttype"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4
+msgid "the author"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:4 templates/objectapp/gbobject_search.html:6
+msgid "page"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:9 templates/objectapp/gbobject_list.html:12
+#: templates/objectapp/gbobject_list.html:15 templates/objectapp/skeleton.html:40
+msgid "RSS Feed"
+msgstr "RSS æº"
+
+#: templates/objectapp/gbobject_list.html:19
+msgid "Objecttype"
+msgstr "类别"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_list.html:30
+msgid "Tag"
+msgstr "标签"
+
+#: templates/objectapp/gbobject_list.html:19 templates/objectapp/gbobject_search.html:4
+msgid "Page"
+msgstr "页ç "
+
+#: templates/objectapp/gbobject_list.html:34
+#, python-format
+msgid "Gbobjects by %(author)s"
+msgstr "作者 %(author)s 的日志"
+
+#: templates/objectapp/gbobject_list.html:45 templates/objectapp/sitemap.html:23
+#: templates/objectapp/sitemap.html.py:43
+#: templates/objectapp/cms/gbobject_detail.html:12
+#: templates/objectapp/cms/gbobject_list.html:9
+#: templates/objectapp/tags/featured_gbobjects.html:9
+#: templates/objectapp/tags/popular_gbobjects.html:13
+#: templates/objectapp/tags/random_gbobjects.html:9
+#: templates/objectapp/tags/recent_gbobjects.html:9
+msgid "No gbobjects yet."
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:51 templates/objectapp/gbobject_search.html:40
+#, python-format
+msgid "Page %(current_page)s of %(total_page)s"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:56 templates/objectapp/gbobject_search.html:45
+msgid "More recent gbobjects"
+msgstr "更多最近日志"
+
+#: templates/objectapp/gbobject_list.html:65 templates/objectapp/gbobject_search.html:54
+msgid "Gbobjects page"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:72 templates/objectapp/gbobject_search.html:61
+msgid "More old gbobjects"
+msgstr "更多以å‰æ—¥å¿—"
+
+#: templates/objectapp/gbobject_list.html:82 templates/objectapp/gbobject_list.html:83
+msgid "Edit the Objecttype"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:89 templates/objectapp/gbobject_list.html:90
+msgid "Edit the tag"
+msgstr ""
+
+#: templates/objectapp/gbobject_list.html:96 templates/objectapp/gbobject_list.html:97
+msgid "Edit the author"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:4 templates/objectapp/gbobject_search.html:6
+#: templates/objectapp/gbobject_search.html:14
+msgid "Search results for"
+msgstr ""
+
+#: templates/objectapp/gbobject_search.html:10
+msgid "RSS Feed of search result of"
+msgstr "RSS æºï¼ˆåŸºäºŽæœç´¢ï¼‰"
+
+#: templates/objectapp/gbobject_search.html:22
+#, python-format
+msgid "%(gbobject_count)s gbobject found"
+msgid_plural "%(gbobject_count)s gbobjects found"
+msgstr[0] ""
+
+#: templates/objectapp/gbobject_search.html:34
+msgid "Nothing found."
+msgstr "没有找到"
+
+#: templates/objectapp/login.html:4 templates/objectapp/login.html.py:7
+msgid "Login required"
+msgstr ""
+
+#: templates/objectapp/login.html:12
+msgid "Your username and password didn't match. Please try again."
+msgstr ""
+
+#: templates/objectapp/login.html:16
+msgid "You need to be connected to view this gbobject."
+msgstr ""
+
+#: templates/objectapp/login.html:33
+msgid "Login"
+msgstr ""
+
+#: templates/objectapp/password.html:4 templates/objectapp/password.html.py:7
+msgid "Password required"
+msgstr ""
+
+#: templates/objectapp/password.html:12
+msgid "The password provided is not valid. Please try again."
+msgstr ""
+
+#: templates/objectapp/password.html:16
+msgid "You need to provide a password to view this gbobject."
+msgstr ""
+
+#: templates/objectapp/password.html:23
+msgid "Password"
+msgstr ""
+
+#: templates/objectapp/password.html:28
+msgid "Valid"
+msgstr ""
+
+#: templates/objectapp/sitemap.html:10
+msgid "Gbobjects per objecttypes"
+msgstr ""
+
+#: templates/objectapp/sitemap.html:18 templates/objectapp/sitemap.html.py:38
+#: templates/objectapp/tags/popular_gbobjects.html:8
+msgid "comment"
+msgstr "评论"
+
+#: templates/objectapp/sitemap.html:31
+msgid "All the gbobjects"
+msgstr ""
+
+#: templates/objectapp/sitemap.html:60 templates/objectapp/tags/objecttypes.html:11
+msgid "No objecttypes yet."
+msgstr ""
+
+#: templates/objectapp/skeleton.html:27 templates/objectapp/skeleton.html.py:39
+msgid "RSS Feed of latest gbobjects"
+msgstr ""
+
+#: templates/objectapp/tag_list.html:4 templates/objectapp/tag_list.html.py:9
+msgid "Tag list"
+msgstr "标签列表"
+
+#: templates/objectapp/wlwmanifest.xml:40
+msgid "View site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:41
+msgid "Admin. site"
+msgstr ""
+
+#: templates/objectapp/wlwmanifest.xml:53
+msgid "Manage comments"
+msgstr ""
+
+#: templates/objectapp/tags/archives_gbobjects.html:12
+#: templates/objectapp/tags/archives_gbobjects_tree.html:38
+msgid "No archives yet."
+msgstr ""
+
+#: templates/objectapp/tags/authors.html:12
+msgid "No authors yet."
+msgstr ""
+
+#: templates/objectapp/tags/recent_linkbacks.html:10
+msgid "Linkback on"
+msgstr ""
+
+#: templates/objectapp/tags/similar_gbobjects.html:9
+msgid "No similar gbobjects."
+msgstr "没有类似日志"
+
+#: tests/views.py:165 views/search.py:18
+msgid "The pattern is too short"
+msgstr "模å¼é•¿åº¦ä¸å¤Ÿ"
+
+#: tests/views.py:168 views/search.py:22
+msgid "No pattern to search found"
+msgstr "没有æœç´¢åˆ°ç›¸å…³æ¨¡å¼"
+
+#: xmlrpc/metaweblog.py:35
+msgid "Username is incorrect."
+msgstr ""
+
+#: xmlrpc/metaweblog.py:37
+msgid "Password is invalid."
+msgstr ""
+
+#: xmlrpc/metaweblog.py:39
+msgid "User account unavailable."
+msgstr ""
+
+#: xmlrpc/metaweblog.py:42
+#, python-format
+msgid "User cannot %s."
+msgstr ""
+
+#: xmlrpc/pingback.py:93
+msgid "No title"
+msgstr ""
+
+
diff --git a/objectapp/management/__init__.py b/objectapp/management/__init__.py
new file mode 100644
index 0000000..e207fd6
--- /dev/null
+++ b/objectapp/management/__init__.py
@@ -0,0 +1 @@
+"""Management module of Objectapp"""
diff --git a/objectapp/management/commands/__init__.py b/objectapp/management/commands/__init__.py
new file mode 100644
index 0000000..15ffe11
--- /dev/null
+++ b/objectapp/management/commands/__init__.py
@@ -0,0 +1 @@
+"""Commands module of Objectapp"""
diff --git a/objectapp/management/commands/blogger2objectapp.py b/objectapp/management/commands/blogger2objectapp.py
new file mode 100644
index 0000000..1ce332b
--- /dev/null
+++ b/objectapp/management/commands/blogger2objectapp.py
@@ -0,0 +1,319 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Blogger to Objectapp command module
+Based on Elijah Rutschman's code"""
+import sys
+from getpass import getpass
+from datetime import datetime
+from optparse import make_option
+
+from django.utils.encoding import smart_str
+from django.contrib.sites.models import Site
+from django.contrib.auth.models import User
+from django.template.defaultfilters import slugify
+from django.core.management.base import CommandError
+from django.core.management.base import NoArgsCommand
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.comments import get_model as get_comment_model
+
+from objectapp import __version__
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.managers import DRAFT, PUBLISHED
+
+gdata_service = None
+Comment = get_comment_model()
+
+
+class Command(NoArgsCommand):
+ """Command object for importing a Blogger blog
+ into Objectapp via Google's gdata API."""
+ help = 'Import a Blogger blog into Objectapp.'
+
+ option_list = NoArgsCommand.option_list + (
+ make_option('--blogger-username', dest='blogger_username', default='',
+ help='The username to login to Blogger with'),
+ make_option('--Objecttype-title', dest='Objecttype_title', default='',
+ help='The Objectapp Objecttype to import Blogger posts to'),
+ make_option('--blogger-blog-id', dest='blogger_blog_id', default='',
+ help='The id of the Blogger blog to import'),
+ make_option('--author', dest='author', default='',
+ help='All imported gbobjects belong to specified author')
+ )
+
+ SITE = Site.objects.get_current()
+
+ def __init__(self):
+ """Init the Command and add custom styles"""
+ super(Command, self).__init__()
+ self.style.TITLE = self.style.SQL_FIELD
+ self.style.STEP = self.style.SQL_COLTYPE
+ self.style.ITEM = self.style.HTTP_INFO
+
+ def write_out(self, message, verbosity_level=1):
+ """Convenient method for outputing"""
+ if self.verbosity and self.verbosity >= verbosity_level:
+ sys.stdout.write(smart_str(message))
+ sys.stdout.flush()
+
+ def handle_noargs(self, **options):
+ global gdata_service
+ try:
+ from gdata import service
+ gdata_service = service
+ except ImportError:
+ raise CommandError('You need to install the gdata ' \
+ 'module to run this command.')
+
+ self.verbosity = int(options.get('verbosity', 1))
+ self.blogger_username = options.get('blogger_username')
+ self.Objecttype_title = options.get('Objecttype_title')
+ self.blogger_blog_id = options.get('blogger_blog_id')
+
+ self.write_out(self.style.TITLE(
+ 'Starting migration from Blogger to Objectapp %s\n' % __version__))
+
+ if not self.blogger_username:
+ self.blogger_username = raw_input('Blogger username: ')
+ if not self.blogger_username:
+ raise CommandError('Invalid Blogger username')
+
+ self.blogger_password = getpass('Blogger password: ')
+ try:
+ self.blogger_manager = BloggerManager(self.blogger_username,
+ self.blogger_password)
+ except gdata_service.BadAuthentication:
+ raise CommandError('Incorrect Blogger username or password')
+
+ default_author = options.get('author')
+ if default_author:
+ try:
+ self.default_author = User.objects.get(username=default_author)
+ except User.DoesNotExist:
+ raise CommandError(
+ 'Invalid Objectapp username for default author "%s"' % \
+ default_author)
+ else:
+ self.default_author = User.objects.all()[0]
+
+ if not self.blogger_blog_id:
+ self.select_blog_id()
+
+ if not self.Objecttype_title:
+ self.Objecttype_title = raw_input(
+ 'Objecttype title for imported gbobjects: ')
+ if not self.Objecttype_title:
+ raise CommandError('Invalid Objecttype title')
+
+ self.import_posts()
+
+ def select_blog_id(self):
+ self.write_out(self.style.STEP('- Requesting your weblogs\n'))
+ blogs_list = [blog for blog in self.blogger_manager.get_blogs()]
+ while True:
+ i = 0
+ blogs = {}
+ for blog in blogs_list:
+ i += 1
+ blogs[i] = blog
+ self.write_out('%s. %s (%s)' % (i, blog.title.text,
+ get_blog_id(blog)))
+ try:
+ blog_index = int(raw_input('\nSelect a blog to import: '))
+ blog = blogs[blog_index]
+ break
+ except (ValueError, KeyError):
+ self.write_out(self.style.ERROR(
+ 'Please enter a valid blog number\n'))
+
+ self.blogger_blog_id = get_blog_id(blog)
+
+ def get_Objecttype(self):
+ Objecttype, created = Objecttype.objects.get_or_create(
+ title=self.Objecttype_title,
+ slug=slugify(self.Objecttype_title)[:255])
+
+ if created:
+ Objecttype.save()
+
+ return Objecttype
+
+ def import_posts(self):
+ Objecttype = self.get_Objecttype()
+ self.write_out(self.style.STEP('- Importing gbobjects\n'))
+ for post in self.blogger_manager.get_posts(self.blogger_blog_id):
+ creation_date = convert_blogger_timestamp(post.published.text)
+ status = DRAFT if is_draft(post) else PUBLISHED
+ title = post.title.text or ''
+ content = post.content.text or ''
+ slug = slugify(post.title.text or get_post_id(post))[:255]
+ try:
+ gbobject = Gbobject.objects.get(creation_date=creation_date,
+ slug=slug)
+ output = self.style.NOTICE('> Skipped %s (already migrated)\n'
+ % gbobject)
+ except Gbobject.DoesNotExist:
+ gbobject = Gbobject(status=status, title=title, content=content,
+ creation_date=creation_date, slug=slug)
+ if self.default_author:
+ gbobject.author = self.default_author
+ gbobject.tags = ','.join([slugify(cat.term) for
+ cat in post.Objecttype])
+ gbobject.last_update = convert_blogger_timestamp(
+ post.updated.text)
+ gbobject.save()
+ gbobject.sites.add(self.SITE)
+ gbobject.objecttypes.add(Objecttype)
+ gbobject.authors.add(self.default_author)
+ try:
+ self.import_comments(gbobject, post)
+ except gdata_service.RequestError:
+ # comments not available for this post
+ pass
+ output = self.style.ITEM('> Migrated %s + %s comments\n'
+ % (gbobject.title, len(Comment.objects.for_model(gbobject))))
+
+ self.write_out(output)
+
+ def import_comments(self, gbobject, post):
+ blog_id = self.blogger_blog_id
+ post_id = get_post_id(post)
+ comments = self.blogger_manager.get_comments(blog_id, post_id)
+ gbobject_content_type = ContentType.objects.get_for_model(Gbobject)
+
+ for comment in comments:
+ submit_date = convert_blogger_timestamp(comment.published.text)
+ content = comment.content.text
+
+ author = comment.author[0]
+ if author:
+ user_name = author.name.text if author.name else ''
+ user_email = author.email.text if author.email else ''
+ user_url = author.uri.text if author.uri else ''
+
+ else:
+ user_name = ''
+ user_email = ''
+ user_url = ''
+
+ com, created = Comment.objects.get_or_create(
+ content_type=gbobject_content_type,
+ object_pk=gbobject.pk,
+ comment=content,
+ submit_date=submit_date,
+ site=self.SITE,
+ user_name=user_name,
+ user_email=user_email,
+ user_url=user_url)
+
+ if created:
+ com.save()
+
+
+def convert_blogger_timestamp(timestamp):
+ # parse 2010-12-19T15:37:00.003
+ date_string = timestamp[:-6]
+ return datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S.%f')
+
+
+def is_draft(post):
+ if post.control:
+ if post.control.draft:
+ if post.control.draft.text == 'yes':
+ return True
+ return False
+
+
+def get_blog_id(blog):
+ return blog.GetSelfLink().href.split('/')[-1]
+
+
+def get_post_id(post):
+ return post.GetSelfLink().href.split('/')[-1]
+
+
+class BloggerManager(object):
+
+ def __init__(self, username, password):
+ self.service = gdata_service.GDataService(username, password)
+ self.service.server = 'www.blogger.com'
+ self.service.service = 'blogger'
+ self.service.ProgrammaticLogin()
+
+ def get_blogs(self):
+ feed = self.service.Get('/feeds/default/blogs')
+ for blog in feed.gbobject:
+ yield blog
+
+ def get_posts(self, blog_id):
+ feed = self.service.Get('/feeds/%s/posts/default' % blog_id)
+ for post in feed.gbobject:
+ yield post
+
+ def get_comments(self, blog_id, post_id):
+ feed = self.service.Get('/feeds/%s/%s/comments/default' % \
+ (blog_id, post_id))
+ for comment in feed.gbobject:
+ yield comment
diff --git a/objectapp/management/commands/feed2objectapp.py b/objectapp/management/commands/feed2objectapp.py
new file mode 100644
index 0000000..3a35508
--- /dev/null
+++ b/objectapp/management/commands/feed2objectapp.py
@@ -0,0 +1,209 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Feed to Objectapp command module"""
+import sys
+from datetime import datetime
+from optparse import make_option
+
+from django.utils.html import strip_tags
+from django.db.utils import IntegrityError
+from django.utils.encoding import smart_str
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.utils.text import truncate_words
+from django.template.defaultfilters import slugify
+from django.core.management.base import CommandError
+from django.core.management.base import LabelCommand
+
+from objectapp import __version__
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.managers import PUBLISHED
+from objectapp.signals import disconnect_objectapp_signals
+
+
+class Command(LabelCommand):
+ """Command object for importing a RSS or Atom
+ feed into Objectapp."""
+ help = 'Import a RSS or Atom feed into Objectapp.'
+ label = 'feed url'
+ args = 'url'
+
+ option_list = LabelCommand.option_list + (
+ make_option('--noautoexcerpt', action='store_false',
+ dest='auto_excerpt', default=True,
+ help='Do NOT generate an excerpt if not present.'),
+ make_option('--author', dest='author', default='',
+ help='All imported gbobjects belong to specified author'),
+ make_option('--Objecttype-is-tag', action='store_true',
+ dest='Objecttype-tag', default=False,
+ help='Store objecttypes as tags'),
+ )
+ SITE = Site.objects.get_current()
+
+ def __init__(self):
+ """Init the Command and add custom styles"""
+ super(Command, self).__init__()
+ self.style.TITLE = self.style.SQL_FIELD
+ self.style.STEP = self.style.SQL_COLTYPE
+ self.style.ITEM = self.style.HTTP_INFO
+ disconnect_objectapp_signals()
+
+ def write_out(self, message, verbosity_level=1):
+ """Convenient method for outputing"""
+ if self.verbosity and self.verbosity >= verbosity_level:
+ sys.stdout.write(smart_str(message))
+ sys.stdout.flush()
+
+ def handle_label(self, url, **options):
+ try:
+ import feedparser
+ except ImportError:
+ raise CommandError('You need to install the feedparser ' \
+ 'module to run this command.')
+
+ self.verbosity = int(options.get('verbosity', 1))
+ self.auto_excerpt = options.get('auto_excerpt', True)
+ self.default_author = options.get('author')
+ self.Objecttype_tag = options.get('Objecttype-tag', False)
+ if self.default_author:
+ try:
+ self.default_author = User.objects.get(
+ username=self.default_author)
+ except User.DoesNotExist:
+ raise CommandError('Invalid username for default author')
+
+ self.write_out(self.style.TITLE(
+ 'Starting importation of %s to Objectapp %s:\n' % (url, __version__)))
+
+ feed = feedparser.parse(url)
+ self.import_gbobjects(feed.gbobjects)
+
+ def import_gbobjects(self, feed_gbobjects):
+ """Import gbobjects"""
+ for feed_gbobject in feed_gbobjects:
+ self.write_out('> %s... ' % feed_gbobject.title)
+ creation_date = datetime(*feed_gbobject.date_parsed[:6])
+ slug = slugify(feed_gbobject.title)[:255]
+
+ if Gbobject.objects.filter(creation_date__year=creation_date.year,
+ creation_date__month=creation_date.month,
+ creation_date__day=creation_date.day,
+ slug=slug):
+ self.write_out(self.style.NOTICE(
+ 'SKIPPED (already imported)\n'))
+ continue
+
+ objecttypes = self.import_objecttypes(feed_gbobject)
+ gbobject_dict = {'title': feed_gbobject.title[:255],
+ 'content': feed_gbobject.description,
+ 'excerpt': feed_gbobject.get('summary'),
+ 'status': PUBLISHED,
+ 'creation_date': creation_date,
+ 'start_publication': creation_date,
+ 'last_update': datetime.now(),
+ 'slug': slug}
+
+ if not gbobject_dict['excerpt'] and self.auto_excerpt:
+ gbobject_dict['excerpt'] = truncate_words(
+ strip_tags(feed_gbobject.description), 50)
+ if self.Objecttype_tag:
+ gbobject_dict['tags'] = self.import_tags(objecttypes)
+
+ gbobject = Gbobject(**gbobject_dict)
+ gbobject.save()
+ gbobject.objecttypes.add(*objecttypes)
+ gbobject.sites.add(self.SITE)
+
+ if self.default_author:
+ gbobject.authors.add(self.default_author)
+ elif feed_gbobject.get('author_detail'):
+ try:
+ user = User.objects.create_user(
+ slugify(feed_gbobject.author_detail.get('name')),
+ feed_gbobject.author_detail.get('email', ''))
+ except IntegrityError:
+ user = User.objects.get(
+ username=slugify(feed_gbobject.author_detail.get('name')))
+ gbobject.authors.add(user)
+
+ self.write_out(self.style.ITEM('OK\n'))
+
+ def import_objecttypes(self, feed_gbobject):
+ objecttypes = []
+ for cat in feed_gbobject.get('tags', ''):
+ Objecttype, created = Objecttype.objects.get_or_create(
+ slug=slugify(cat.term), defaults={'title': cat.term})
+ objecttypes.append(Objecttype)
+ return objecttypes
+
+ def import_tags(self, objecttypes):
+ tags = []
+ for cat in objecttypes:
+ if len(cat.title.split()) > 1:
+ tags.append('"%s"' % slugify(cat.title).replace('-', ' '))
+ else:
+ tags.append(slugify(cat.title).replace('-', ' '))
+ return ', '.join(tags)
diff --git a/objectapp/management/commands/objectapp2wp.py b/objectapp/management/commands/objectapp2wp.py
new file mode 100644
index 0000000..1b53f17
--- /dev/null
+++ b/objectapp/management/commands/objectapp2wp.py
@@ -0,0 +1,96 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Objectapp to WordPress command module"""
+from django.conf import settings
+from django.utils.encoding import smart_str
+from django.contrib.sites.models import Site
+from django.template.loader import render_to_string
+from django.core.management.base import NoArgsCommand
+
+from tagging.models import Tag
+
+from objectapp import __version__
+from objectapp.settings import PROTOCOL
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+
+
+class Command(NoArgsCommand):
+ """Command object for exporting a Objectapp blog
+ into WordPress via a WordPress eXtended RSS (WXR) file."""
+ help = 'Export Objectapp to WXR file.'
+
+ def handle_noargs(self, **options):
+ site = Site.objects.get_current()
+ blog_context = {'gbobjects': Gbobject.objects.all(),
+ 'objecttypes': Objecttype.objects.all(),
+ 'tags': Tag.objects.usage_for_model(Gbobject),
+ 'version': __version__,
+ 'description': 'Blog exported for django-blog-objectapp',
+ 'language': settings.LANGUAGE_CODE,
+ 'site': site,
+ 'site_url': '%s://%s' % (PROTOCOL, site.domain)}
+ export = render_to_string('objectapp/wxr.xml', blog_context)
+ print smart_str(export)
diff --git a/objectapp/management/commands/spam_cleanup.py b/objectapp/management/commands/spam_cleanup.py
new file mode 100644
index 0000000..441fc7b
--- /dev/null
+++ b/objectapp/management/commands/spam_cleanup.py
@@ -0,0 +1,72 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Spam cleanup command module for Objectapp"""
+from django.contrib import comments
+from django.contrib.contenttypes.models import ContentType
+from django.core.management.base import NoArgsCommand
+
+from objectapp.models import Gbobject
+
+
+class Command(NoArgsCommand):
+ """Command object for removing comments
+ flagged as spam"""
+ help = "Remove gbobject's comments flagged as spam."
+
+ def handle_noargs(self, **options):
+ verbosity = int(options.get('verbosity', 1))
+
+ content_type = ContentType.objects.get_for_model(Gbobject)
+ spams = comments.get_model().objects.filter(is_public=False,
+ content_type=content_type,
+ flags__flag='spam')
+ spams_count = spams.count()
+ spams.delete()
+
+ if verbosity:
+ print '%i spam comments deleted.' % spams_count
diff --git a/objectapp/management/commands/wp2objectapp.py b/objectapp/management/commands/wp2objectapp.py
new file mode 100644
index 0000000..5f2d42d
--- /dev/null
+++ b/objectapp/management/commands/wp2objectapp.py
@@ -0,0 +1,406 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""WordPress to Objectapp command module"""
+import sys
+from datetime import datetime
+from optparse import make_option
+from xml.etree import ElementTree as ET
+
+from django.utils.html import strip_tags
+from django.db.utils import IntegrityError
+from django.utils.encoding import smart_str
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.utils.text import truncate_words
+from django.template.defaultfilters import slugify
+from django.contrib import comments
+from django.core.management.base import CommandError
+from django.core.management.base import LabelCommand
+
+from tagging.models import Tag
+
+from objectapp import __version__
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.signals import disconnect_objectapp_signals
+from objectapp.managers import DRAFT, HIDDEN, PUBLISHED
+
+WP_NS = 'http://wordpress.org/export/%s/'
+
+
+class Command(LabelCommand):
+ """Command object for importing a WordPress blog
+ into Objectapp via a WordPress eXtended RSS (WXR) file."""
+ help = 'Import a Wordpress blog into Objectapp.'
+ label = 'WXR file'
+ args = 'wordpress.xml'
+
+ option_list = LabelCommand.option_list + (
+ make_option('--noautoexcerpt', action='store_false',
+ dest='auto_excerpt', default=True,
+ help='Do NOT generate an excerpt if not present.'),
+ make_option('--author', dest='author', default='',
+ help='All imported gbobjects belong to specified author'),
+ make_option('--wxr_version', dest='wxr_version', default='1.0',
+ help='Wordpress XML export version'),
+ )
+
+ SITE = Site.objects.get_current()
+ REVERSE_STATUS = {'pending': DRAFT,
+ 'draft': DRAFT,
+ 'auto-draft': DRAFT,
+ 'inherit': DRAFT,
+ 'publish': PUBLISHED,
+ 'future': PUBLISHED,
+ 'trash': HIDDEN,
+ 'private': PUBLISHED}
+
+ def __init__(self):
+ """Init the Command and add custom styles"""
+ super(Command, self).__init__()
+ self.style.TITLE = self.style.SQL_FIELD
+ self.style.STEP = self.style.SQL_COLTYPE
+ self.style.ITEM = self.style.HTTP_INFO
+ disconnect_objectapp_signals()
+
+ def write_out(self, message, verbosity_level=1):
+ """Convenient method for outputing"""
+ if self.verbosity and self.verbosity >= verbosity_level:
+ sys.stdout.write(smart_str(message))
+ sys.stdout.flush()
+
+ def handle_label(self, wxr_file, **options):
+ global WP_NS
+ self.verbosity = int(options.get('verbosity', 1))
+ self.auto_excerpt = options.get('auto_excerpt', True)
+ WP_NS = WP_NS % options.get('wxr_version')
+ self.default_author = options.get('author')
+ if self.default_author:
+ try:
+ self.default_author = User.objects.get(
+ username=self.default_author)
+ except User.DoesNotExist:
+ raise CommandError('Invalid username for default author')
+
+ self.write_out(self.style.TITLE(
+ 'Starting migration from Wordpress to Objectapp %s:\n' % __version__))
+
+ tree = ET.parse(wxr_file)
+
+ self.authors = self.import_authors(tree)
+
+ self.objecttypes = self.import_objecttypes(
+ tree.findall('channel/{%s}Objecttype' % WP_NS))
+
+ self.import_tags(tree.findall('channel/{%s}tag' % WP_NS))
+
+ self.import_gbobjects(tree.findall('channel/item'))
+
+ def import_authors(self, tree):
+ """Retrieve all the authors used in posts
+ and convert it to new or existing user, and
+ return the convertion"""
+ self.write_out(self.style.STEP('- Importing authors\n'))
+
+ post_authors = set()
+ for item in tree.findall('channel/item'):
+ post_type = item.find('{%s}post_type' % WP_NS).text
+ if post_type == 'post':
+ post_authors.add(item.find(
+ '{http://purl.org/dc/elements/1.1/}creator').text)
+
+ self.write_out('%i authors found.\n' % len(post_authors))
+
+ authors = {}
+ for post_author in post_authors:
+ if self.default_author:
+ authors[post_author] = self.default_author
+ else:
+ authors[post_author] = self.migrate_author(post_author)
+ return authors
+
+ def migrate_author(self, author_name):
+ """Handle actions for migrating the users"""
+ action_text = "The author '%s' needs to be migrated to an User:\n"\
+ "1. Use an existing user ?\n"\
+ "2. Create a new user ?\n"\
+ "Please select a choice: " % author_name
+ while 42:
+ selection = raw_input(smart_str(action_text))
+ if selection in '12':
+ break
+ if selection == '1':
+ users = User.objects.all()
+ usernames = [user.username for user in users]
+ while 42:
+ user_text = "1. Select your user, by typing " \
+ "one of theses usernames:\n"\
+ "[%s]\n"\
+ "Please select a choice: " % ', '.join(usernames)
+ user_selected = raw_input(user_text)
+ if user_selected in usernames:
+ break
+ return users.get(username=user_selected)
+ else:
+ create_text = "2. Please type the email of the '%s' user: " % \
+ author_name
+ author_mail = raw_input(create_text)
+ try:
+ return User.objects.create_user(author_name, author_mail)
+ except IntegrityError:
+ return User.objects.get(username=author_name)
+
+ def import_objecttypes(self, Objecttype_nodes):
+ """Import all the objecttypes from 'wp:Objecttype' nodes,
+ because objecttypes in 'item' nodes are not necessarily
+ all the objecttypes and returning it in a dict for
+ database optimizations."""
+ self.write_out(self.style.STEP('- Importing objecttypes\n'))
+
+ objecttypes = {}
+ for Objecttype_node in Objecttype_nodes:
+ title = Objecttype_node.find('{%s}cat_name' % WP_NS).text[:255]
+ slug = Objecttype_node.find(
+ '{%s}Objecttype_nicename' % WP_NS).text[:255]
+ try:
+ parent = Objecttype_node.find(
+ '{%s}Objecttype_parent' % WP_NS).text[:255]
+ except TypeError:
+ parent = None
+ self.write_out('> %s... ' % title)
+ Objecttype, created = Objecttype.objects.get_or_create(
+ title=title, slug=slug, parent=objecttypes.get(parent))
+ objecttypes[title] = Objecttype
+ self.write_out(self.style.ITEM('OK\n'))
+ return objecttypes
+
+ def import_tags(self, tag_nodes):
+ """Import all the tags form 'wp:tag' nodes,
+ because tags in 'item' nodes are not necessarily
+ all the tags, then use only the nicename, because it's like
+ a slug and the true tag name may be not valid for url usage."""
+ self.write_out(self.style.STEP('- Importing tags\n'))
+ for tag_node in tag_nodes:
+ tag_name = tag_node.find(
+ '{%s}tag_slug' % WP_NS).text[:50]
+ self.write_out('> %s... ' % tag_name)
+ Tag.objects.get_or_create(name=tag_name)
+ self.write_out(self.style.ITEM('OK\n'))
+
+ def get_gbobject_tags(self, objecttypes):
+ """Return a list of gbobject's tags,
+ by using the nicename for url compatibility"""
+ tags = []
+ for Objecttype in objecttypes:
+ domain = Objecttype.attrib.get('domain', 'Objecttype')
+ if domain == 'tag' and Objecttype.attrib.get('nicename'):
+ tags.append(Objecttype.attrib.get('nicename'))
+ return tags
+
+ def get_gbobject_objecttypes(self, Objecttype_nodes):
+ """Return a list of gbobject's objecttypes
+ based of imported objecttypes"""
+ objecttypes = []
+ for Objecttype_node in Objecttype_nodes:
+ domain = Objecttype_node.attrib.get('domain')
+ if domain == 'Objecttype':
+ objecttypes.append(self.objecttypes[Objecttype_node.text])
+ return objecttypes
+
+ def import_gbobject(self, title, content, item_node):
+ """Importing an gbobject but some data are missing like
+ the image, related gbobjects, start_publication and end_publication.
+ start_publication and creation_date will use the same value,
+ wich is always in Wordpress $post->post_date"""
+ creation_date = datetime.strptime(
+ item_node.find('{%s}post_date' % WP_NS).text, '%Y-%m-%d %H:%M:%S')
+
+ excerpt = item_node.find('{%sexcerpt/}encoded' % WP_NS).text
+ if not excerpt:
+ if self.auto_excerpt:
+ excerpt = truncate_words(strip_tags(content), 50)
+ else:
+ excerpt = ''
+
+ gbobject_dict = {
+ 'content': content,
+ 'excerpt': excerpt,
+ # Prefer use this function than
+ # item_node.find('{%s}post_name' % WP_NS).text
+ # Because slug can be not well formated
+ 'slug': slugify(title)[:255] or 'post-%s' % item_node.find(
+ '{%s}post_id' % WP_NS).text,
+ 'tags': ', '.join(self.get_gbobject_tags(item_node.findall(
+ 'Objecttype'))),
+ 'status': self.REVERSE_STATUS[item_node.find(
+ '{%s}status' % WP_NS).text],
+ 'comment_enabled': item_node.find(
+ '{%s}comment_status' % WP_NS).text == 'open',
+ 'pingback_enabled': item_node.find(
+ '{%s}ping_status' % WP_NS).text == 'open',
+ 'featured': item_node.find('{%s}is_sticky' % WP_NS).text == '1',
+ 'password': item_node.find('{%s}post_password' % WP_NS).text or '',
+ 'login_required': item_node.find(
+ '{%s}status' % WP_NS).text == 'private',
+ 'creation_date': creation_date,
+ 'last_update': datetime.now(),
+ 'start_publication': creation_date}
+
+ gbobject, created = Gbobject.objects.get_or_create(title=title,
+ defaults=gbobject_dict)
+
+ gbobject.objecttypes.add(*self.get_gbobject_objecttypes(
+ item_node.findall('Objecttype')))
+ gbobject.authors.add(self.authors[item_node.find(
+ '{http://purl.org/dc/elements/1.1/}creator').text])
+ gbobject.sites.add(self.SITE)
+
+ #current_id = item_node.find('{%s}post_id' % WP_NS).text
+ #parent_id = item_node.find('%s}post_parent' % WP_NS).text
+
+ return gbobject
+
+ def import_gbobjects(self, items):
+ """Loops over items and find gbobject to import,
+ an gbobject need to have 'post_type' set to 'post' and
+ have content."""
+ self.write_out(self.style.STEP('- Importing gbobjects\n'))
+
+ for item_node in items:
+ title = (item_node.find('title').text or '')[:255]
+ post_type = item_node.find('{%s}post_type' % WP_NS).text
+ content = item_node.find(
+ '{http://purl.org/rss/1.0/modules/content/}encoded').text
+
+ if post_type == 'post' and content and title:
+ self.write_out('> %s... ' % title)
+ gbobject = self.import_gbobject(title, content, item_node)
+ self.write_out(self.style.ITEM('OK\n'))
+ self.import_comments(gbobject, item_node.findall(
+ '{%s}comment/' % WP_NS))
+ else:
+ self.write_out('> %s... ' % title, 2)
+ self.write_out(self.style.NOTICE('SKIPPED (not a post)\n'), 2)
+
+ def import_comments(self, gbobject, comment_nodes):
+ """Loops over comments nodes and import then
+ in django.contrib.comments"""
+ for comment_node in comment_nodes:
+ is_pingback = comment_node.find(
+ '{%s}comment_type' % WP_NS).text == 'pingback'
+ is_trackback = comment_node.find(
+ '{%s}comment_type' % WP_NS).text == 'trackback'
+
+ title = 'Comment #%s' % (comment_node.find(
+ '{%s}comment_id/' % WP_NS).text)
+ self.write_out(' > %s... ' % title)
+
+ content = comment_node.find(
+ '{%s}comment_content/' % WP_NS).text
+ if not content:
+ self.write_out(self.style.NOTICE('SKIPPED (unfilled)\n'))
+ return
+
+ submit_date = datetime.strptime(
+ comment_node.find('{%s}comment_date' % WP_NS).text,
+ '%Y-%m-%d %H:%M:%S')
+
+ approvation = comment_node.find(
+ '{%s}comment_approved' % WP_NS).text
+ is_public = True
+ is_removed = False
+ if approvation != '1':
+ is_removed = True
+ if approvation == 'spam':
+ is_public = False
+
+ comment_dict = {
+ 'content_object': gbobject,
+ 'site': self.SITE,
+ 'user_name': comment_node.find(
+ '{%s}comment_author/' % WP_NS).text[:50],
+ 'user_email': comment_node.find(
+ '{%s}comment_author_email/' % WP_NS).text or '',
+ 'user_url': comment_node.find(
+ '{%s}comment_author_url/' % WP_NS).text or '',
+ 'comment': content,
+ 'submit_date': submit_date,
+ 'ip_address': comment_node.find(
+ '{%s}comment_author_IP/' % WP_NS).text or '',
+ 'is_public': is_public,
+ 'is_removed': is_removed, }
+ comment = comments.get_model()(**comment_dict)
+ comment.save()
+ if approvation == 'spam':
+ comment.flags.create(
+ user=gbobject.authors.all()[0], flag='spam')
+ if is_pingback:
+ comment.flags.create(
+ user=gbobject.authors.all()[0], flag='pingback')
+ if is_trackback:
+ comment.flags.create(
+ user=gbobject.authors.all()[0], flag='trackback')
+
+ self.write_out(self.style.ITEM('OK\n'))
diff --git a/objectapp/managers.py b/objectapp/managers.py
new file mode 100644
index 0000000..9910581
--- /dev/null
+++ b/objectapp/managers.py
@@ -0,0 +1,146 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Managers of Objectapp"""
+from datetime import datetime
+
+from django.db import models
+from django.contrib.sites.models import Site
+
+DRAFT = 0
+HIDDEN = 1
+PUBLISHED = 2
+
+
+def tags_published():
+ """Return the published tags"""
+ from tagging.models import Tag
+ from objectapp.models import Gbobject
+ tags_gbobject_published = Tag.objects.usage_for_queryset(
+ Gbobject.published.all())
+ # Need to do that until the issue #44 of django-tagging is fixed
+ return Tag.objects.filter(name__in=[t.name for t in tags_gbobject_published])
+
+
+class AuthorPublishedManager(models.Manager):
+ """Manager to retrieve published authors"""
+
+ def get_query_set(self):
+ """Return published authors"""
+ now = datetime.now()
+ return super(AuthorPublishedManager, self).get_query_set().filter(
+ gbobjects__status=PUBLISHED,
+ gbobjects__start_publication__lte=now,
+ gbobjects__end_publication__gt=now,
+ gbobjects__sites=Site.objects.get_current()
+ ).distinct()
+
+
+def gbobjects_published(queryset):
+ """Return only the gbobjects published"""
+ now = datetime.now()
+ return queryset.filter(status=PUBLISHED,
+ start_publication__lte=now,
+ end_publication__gt=now,
+ sites=Site.objects.get_current())
+
+
+class GbobjectPublishedManager(models.Manager):
+ """Manager to retrieve published gbobjects"""
+
+ def get_query_set(self):
+ """Return published gbobjects"""
+ return gbobjects_published(
+ super(GbobjectPublishedManager, self).get_query_set())
+
+ def on_site(self):
+ """Return gbobjects published on current site"""
+ return super(GbobjectPublishedManager, self).get_query_set(
+ ).filter(sites=Site.objects.get_current())
+
+ def search(self, pattern):
+ """Top level search method on gbobjects"""
+ try:
+ return self.advanced_search(pattern)
+ except:
+ return self.basic_search(pattern)
+
+ def advanced_search(self, pattern):
+ """Advanced search on gbobjects"""
+ from objectapp.search import advanced_search
+ return advanced_search(pattern)
+
+ def basic_search(self, pattern):
+ """Basic search on gbobjects"""
+ lookup = None
+ for pattern in pattern.split():
+ query_part = models.Q(content__icontains=pattern) | \
+ models.Q(excerpt__icontains=pattern) | \
+ models.Q(title__icontains=pattern)
+ if lookup is None:
+ lookup = query_part
+ else:
+ lookup |= query_part
+
+ return self.get_query_set().filter(lookup)
diff --git a/objectapp/migrations/0001_initial.py b/objectapp/migrations/0001_initial.py
new file mode 100644
index 0000000..3c380ed
--- /dev/null
+++ b/objectapp/migrations/0001_initial.py
@@ -0,0 +1,459 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'Gbobject'
+ db.create_table('objectapp_gbobject', (
+ ('node_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['gstudio.Node'], unique=True, primary_key=True)),
+ ('content', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100, blank=True)),
+ ('excerpt', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('tags', self.gf('tagging.fields.TagField')()),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ('status', self.gf('django.db.models.fields.IntegerField')(default=2)),
+ ('featured', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('comment_enabled', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('pingback_enabled', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('creation_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('last_update', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('start_publication', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('end_publication', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2042, 3, 15, 0, 0))),
+ ('login_required', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('password', self.gf('django.db.models.fields.CharField')(max_length=50, blank=True)),
+ ('template', self.gf('django.db.models.fields.CharField')(default='objectapp/gbobject_detail.html', max_length=250)),
+ ))
+ db.send_create_signal('objectapp', ['Gbobject'])
+
+ # Adding M2M table for field prior_nodes on 'Gbobject'
+ db.create_table('objectapp_gbobject_prior_nodes', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('from_gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False)),
+ ('to_gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False))
+ ))
+ db.create_unique('objectapp_gbobject_prior_nodes', ['from_gbobject_id', 'to_gbobject_id'])
+
+ # Adding M2M table for field posterior_nodes on 'Gbobject'
+ db.create_table('objectapp_gbobject_posterior_nodes', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('from_gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False)),
+ ('to_gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False))
+ ))
+ db.create_unique('objectapp_gbobject_posterior_nodes', ['from_gbobject_id', 'to_gbobject_id'])
+
+ # Adding M2M table for field objecttypes on 'Gbobject'
+ db.create_table('objectapp_gbobject_objecttypes', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False)),
+ ('nodetype', models.ForeignKey(orm['gstudio.nodetype'], null=False))
+ ))
+ db.create_unique('objectapp_gbobject_objecttypes', ['gbobject_id', 'nodetype_id'])
+
+ # Adding M2M table for field authors on 'Gbobject'
+ db.create_table('objectapp_gbobject_authors', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False)),
+ ('user', models.ForeignKey(orm['auth.user'], null=False))
+ ))
+ db.create_unique('objectapp_gbobject_authors', ['gbobject_id', 'user_id'])
+
+ # Adding M2M table for field sites on 'Gbobject'
+ db.create_table('objectapp_gbobject_sites', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False)),
+ ('site', models.ForeignKey(orm['sites.site'], null=False))
+ ))
+ db.create_unique('objectapp_gbobject_sites', ['gbobject_id', 'site_id'])
+
+ # Adding model 'Process'
+ db.create_table('objectapp_process', (
+ ('gbobject_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['objectapp.Gbobject'], unique=True, primary_key=True)),
+ ))
+ db.send_create_signal('objectapp', ['Process'])
+
+ # Adding M2M table for field processtypes on 'Process'
+ db.create_table('objectapp_process_processtypes', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('process', models.ForeignKey(orm['objectapp.process'], null=False)),
+ ('processtype', models.ForeignKey(orm['gstudio.processtype'], null=False))
+ ))
+ db.create_unique('objectapp_process_processtypes', ['process_id', 'processtype_id'])
+
+ # Adding M2M table for field priorstate_attribute_set on 'Process'
+ db.create_table('objectapp_process_priorstate_attribute_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('process', models.ForeignKey(orm['objectapp.process'], null=False)),
+ ('attribute', models.ForeignKey(orm['gstudio.attribute'], null=False))
+ ))
+ db.create_unique('objectapp_process_priorstate_attribute_set', ['process_id', 'attribute_id'])
+
+ # Adding M2M table for field priorstate_relation_set on 'Process'
+ db.create_table('objectapp_process_priorstate_relation_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('process', models.ForeignKey(orm['objectapp.process'], null=False)),
+ ('relation', models.ForeignKey(orm['gstudio.relation'], null=False))
+ ))
+ db.create_unique('objectapp_process_priorstate_relation_set', ['process_id', 'relation_id'])
+
+ # Adding M2M table for field poststate_attribute_set on 'Process'
+ db.create_table('objectapp_process_poststate_attribute_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('process', models.ForeignKey(orm['objectapp.process'], null=False)),
+ ('attribute', models.ForeignKey(orm['gstudio.attribute'], null=False))
+ ))
+ db.create_unique('objectapp_process_poststate_attribute_set', ['process_id', 'attribute_id'])
+
+ # Adding M2M table for field poststate_relation_set on 'Process'
+ db.create_table('objectapp_process_poststate_relation_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('process', models.ForeignKey(orm['objectapp.process'], null=False)),
+ ('relation', models.ForeignKey(orm['gstudio.relation'], null=False))
+ ))
+ db.create_unique('objectapp_process_poststate_relation_set', ['process_id', 'relation_id'])
+
+ # Adding model 'System'
+ db.create_table('objectapp_system', (
+ ('gbobject_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['objectapp.Gbobject'], unique=True, primary_key=True)),
+ ))
+ db.send_create_signal('objectapp', ['System'])
+
+ # Adding M2M table for field systemtypes on 'System'
+ db.create_table('objectapp_system_systemtypes', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('system', models.ForeignKey(orm['objectapp.system'], null=False)),
+ ('systemtype', models.ForeignKey(orm['gstudio.systemtype'], null=False))
+ ))
+ db.create_unique('objectapp_system_systemtypes', ['system_id', 'systemtype_id'])
+
+ # Adding M2M table for field gbobject_set on 'System'
+ db.create_table('objectapp_system_gbobject_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('system', models.ForeignKey(orm['objectapp.system'], null=False)),
+ ('gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False))
+ ))
+ db.create_unique('objectapp_system_gbobject_set', ['system_id', 'gbobject_id'])
+
+ # Adding M2M table for field relation_set on 'System'
+ db.create_table('objectapp_system_relation_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('system', models.ForeignKey(orm['objectapp.system'], null=False)),
+ ('relation', models.ForeignKey(orm['gstudio.relation'], null=False))
+ ))
+ db.create_unique('objectapp_system_relation_set', ['system_id', 'relation_id'])
+
+ # Adding M2M table for field attribute_set on 'System'
+ db.create_table('objectapp_system_attribute_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('system', models.ForeignKey(orm['objectapp.system'], null=False)),
+ ('attribute', models.ForeignKey(orm['gstudio.attribute'], null=False))
+ ))
+ db.create_unique('objectapp_system_attribute_set', ['system_id', 'attribute_id'])
+
+ # Adding M2M table for field process_set on 'System'
+ db.create_table('objectapp_system_process_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('system', models.ForeignKey(orm['objectapp.system'], null=False)),
+ ('process', models.ForeignKey(orm['objectapp.process'], null=False))
+ ))
+ db.create_unique('objectapp_system_process_set', ['system_id', 'process_id'])
+
+ # Adding M2M table for field system_set on 'System'
+ db.create_table('objectapp_system_system_set', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('from_system', models.ForeignKey(orm['objectapp.system'], null=False)),
+ ('to_system', models.ForeignKey(orm['objectapp.system'], null=False))
+ ))
+ db.create_unique('objectapp_system_system_set', ['from_system_id', 'to_system_id'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'Gbobject'
+ db.delete_table('objectapp_gbobject')
+
+ # Removing M2M table for field prior_nodes on 'Gbobject'
+ db.delete_table('objectapp_gbobject_prior_nodes')
+
+ # Removing M2M table for field posterior_nodes on 'Gbobject'
+ db.delete_table('objectapp_gbobject_posterior_nodes')
+
+ # Removing M2M table for field objecttypes on 'Gbobject'
+ db.delete_table('objectapp_gbobject_objecttypes')
+
+ # Removing M2M table for field authors on 'Gbobject'
+ db.delete_table('objectapp_gbobject_authors')
+
+ # Removing M2M table for field sites on 'Gbobject'
+ db.delete_table('objectapp_gbobject_sites')
+
+ # Deleting model 'Process'
+ db.delete_table('objectapp_process')
+
+ # Removing M2M table for field processtypes on 'Process'
+ db.delete_table('objectapp_process_processtypes')
+
+ # Removing M2M table for field priorstate_attribute_set on 'Process'
+ db.delete_table('objectapp_process_priorstate_attribute_set')
+
+ # Removing M2M table for field priorstate_relation_set on 'Process'
+ db.delete_table('objectapp_process_priorstate_relation_set')
+
+ # Removing M2M table for field poststate_attribute_set on 'Process'
+ db.delete_table('objectapp_process_poststate_attribute_set')
+
+ # Removing M2M table for field poststate_relation_set on 'Process'
+ db.delete_table('objectapp_process_poststate_relation_set')
+
+ # Deleting model 'System'
+ db.delete_table('objectapp_system')
+
+ # Removing M2M table for field systemtypes on 'System'
+ db.delete_table('objectapp_system_systemtypes')
+
+ # Removing M2M table for field gbobject_set on 'System'
+ db.delete_table('objectapp_system_gbobject_set')
+
+ # Removing M2M table for field relation_set on 'System'
+ db.delete_table('objectapp_system_relation_set')
+
+ # Removing M2M table for field attribute_set on 'System'
+ db.delete_table('objectapp_system_attribute_set')
+
+ # Removing M2M table for field process_set on 'System'
+ db.delete_table('objectapp_system_process_set')
+
+ # Removing M2M table for field system_set on 'System'
+ db.delete_table('objectapp_system_system_set')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'gstudio.attribute': {
+ 'Meta': {'unique_together': "(('subject_scope', 'subject', 'attributetype_scope', 'attributetype', 'value_scope', 'svalue'),)", 'object_name': 'Attribute', '_ormbases': ['gstudio.Edge']},
+ 'attributetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gstudio.Attributetype']"}),
+ 'attributetype_scope': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'edge_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Edge']", 'unique': 'True', 'primary_key': 'True'}),
+ 'subject': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subject_of'", 'to': "orm['gstudio.NID']"}),
+ 'subject_scope': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'svalue': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'value_scope': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'})
+ },
+ 'gstudio.attributetype': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Attributetype', '_ormbases': ['gstudio.Nodetype']},
+ 'applicable_nodetypes': ('django.db.models.fields.CharField', [], {'default': "'OT'", 'max_length': '2'}),
+ 'auto_now': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'auto_now_add': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'blank': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'dataType': ('django.db.models.fields.CharField', [], {'default': "'01'", 'max_length': '2'}),
+ 'decimal_places': ('django.db.models.fields.IntegerField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
+ 'default': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}),
+ 'editable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'help_text': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'max_digits': ('django.db.models.fields.IntegerField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}),
+ 'min_length': ('django.db.models.fields.IntegerField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'nodetype_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Nodetype']", 'unique': 'True', 'primary_key': 'True'}),
+ 'null': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}),
+ 'required': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'subjecttype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subjecttype_of'", 'to': "orm['gstudio.NID']"}),
+ 'unique': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'upload_to': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}),
+ 'validators': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'validators_rel_+'", 'null': 'True', 'to': "orm['gstudio.Attributetype']"}),
+ 'verbose_name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}),
+ 'verify_exists': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'gstudio.edge': {
+ 'Meta': {'object_name': 'Edge', '_ormbases': ['gstudio.NID']},
+ 'nid_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.NID']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'gstudio.metatype': {
+ 'Meta': {'ordering': "['title']", 'object_name': 'Metatype', '_ormbases': ['gstudio.Node']},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Node']", 'unique': 'True', 'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['gstudio.Metatype']"}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'gstudio.nid': {
+ 'Meta': {'object_name': 'NID'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'gstudio.node': {
+ 'Meta': {'object_name': 'Node', '_ormbases': ['gstudio.NID']},
+ 'altnames': ('tagging.fields.TagField', [], {'null': 'True'}),
+ 'nid_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.NID']", 'unique': 'True', 'primary_key': 'True'}),
+ 'plural': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'rating_score': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
+ 'rating_votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
+ },
+ 'gstudio.nodetype': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Nodetype', '_ormbases': ['gstudio.Node']},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'nodetypes'", 'blank': 'True', 'to': "orm['auth.User']"}),
+ 'comment_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'end_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2042, 3, 15, 0, 0)'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'metatypes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'member_types'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Metatype']"}),
+ 'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Node']", 'unique': 'True', 'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['gstudio.Nodetype']"}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'pingback_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'posterior_nodes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'posterior_nodes_rel_+'", 'null': 'True', 'to': "orm['gstudio.Nodetype']"}),
+ 'prior_nodes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'prior_nodes_rel_+'", 'null': 'True', 'to': "orm['gstudio.Nodetype']"}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodetypes'", 'symmetrical': 'False', 'to': "orm['sites.Site']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'start_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '2'}),
+ 'tags': ('tagging.fields.TagField', [], {}),
+ 'template': ('django.db.models.fields.CharField', [], {'default': "'gstudio/nodetype_detail.html'", 'max_length': '250'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'gstudio.processtype': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Processtype', '_ormbases': ['gstudio.Nodetype']},
+ 'changing_attributetype_set': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "' changing_attributetype_set_of'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Attributetype']"}),
+ 'changing_relationtype_set': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'changing_relationtype_set_of'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Relationtype']"}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'nodetype_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Nodetype']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'gstudio.relation': {
+ 'Meta': {'unique_together': "(('left_subject_scope', 'left_subject', 'relationtype_scope', 'relationtype', 'right_subject_scope', 'right_subject'),)", 'object_name': 'Relation', '_ormbases': ['gstudio.Edge']},
+ 'edge_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Edge']", 'unique': 'True', 'primary_key': 'True'}),
+ 'left_subject': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'left_subject_of'", 'to': "orm['gstudio.NID']"}),
+ 'left_subject_scope': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'relationtype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gstudio.Relationtype']"}),
+ 'relationtype_scope': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'right_subject': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'right_subject_of'", 'to': "orm['gstudio.NID']"}),
+ 'right_subject_scope': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'})
+ },
+ 'gstudio.relationtype': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Relationtype', '_ormbases': ['gstudio.Nodetype']},
+ 'inverse': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'is_reflexive': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'is_symmetrical': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'is_transitive': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'left_applicable_nodetypes': ('django.db.models.fields.CharField', [], {'default': "'OT'", 'max_length': '2'}),
+ 'left_cardinality': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'left_subjecttype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'left_subjecttype_of'", 'to': "orm['gstudio.NID']"}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'nodetype_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Nodetype']", 'unique': 'True', 'primary_key': 'True'}),
+ 'right_applicable_nodetypes': ('django.db.models.fields.CharField', [], {'default': "'OT'", 'max_length': '2'}),
+ 'right_cardinality': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'right_subjecttype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'right_subjecttype_of'", 'to': "orm['gstudio.NID']"})
+ },
+ 'gstudio.systemtype': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Systemtype', '_ormbases': ['gstudio.Nodetype']},
+ 'attributetype_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'attributetype_set_of'", 'blank': 'True', 'to': "orm['gstudio.Attributetype']"}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'metatype_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'metatype_set_of'", 'blank': 'True', 'to': "orm['gstudio.Metatype']"}),
+ 'nodetype_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Nodetype']", 'unique': 'True', 'primary_key': 'True'}),
+ 'nodetype_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'nodetype_set_of'", 'blank': 'True', 'to': "orm['gstudio.Nodetype']"}),
+ 'processtype_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'processtype_set_of'", 'blank': 'True', 'to': "orm['gstudio.Processtype']"}),
+ 'relationtype_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'relationtype_set_of'", 'blank': 'True', 'to': "orm['gstudio.Relationtype']"})
+ },
+ 'objectapp.gbobject': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Gbobject', '_ormbases': ['gstudio.Node']},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'gbobjects'", 'blank': 'True', 'to': "orm['auth.User']"}),
+ 'comment_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'end_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2042, 3, 15, 0, 0)'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gstudio.Node']", 'unique': 'True', 'primary_key': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'member_objects'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Nodetype']"}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'pingback_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'posterior_nodes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'posterior_nodes_rel_+'", 'null': 'True', 'to': "orm['objectapp.Gbobject']"}),
+ 'prior_nodes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'prior_nodes_rel_+'", 'null': 'True', 'to': "orm['objectapp.Gbobject']"}),
+ 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'gbobjects'", 'symmetrical': 'False', 'to': "orm['sites.Site']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'start_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '2'}),
+ 'tags': ('tagging.fields.TagField', [], {}),
+ 'template': ('django.db.models.fields.CharField', [], {'default': "'objectapp/gbobject_detail.html'", 'max_length': '250'})
+ },
+ 'objectapp.process': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Process', '_ormbases': ['objectapp.Gbobject']},
+ 'gbobject_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['objectapp.Gbobject']", 'unique': 'True', 'primary_key': 'True'}),
+ 'poststate_attribute_set': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'in_post_state_attrset_of'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Attribute']"}),
+ 'poststate_relation_set': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'in_post_state_relset_of'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Relation']"}),
+ 'priorstate_attribute_set': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'in_prior_state_attrset_of'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Attribute']"}),
+ 'priorstate_relation_set': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'in_prior_state_relset_of'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Relation']"}),
+ 'processtypes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'member_processes'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Processtype']"})
+ },
+ 'objectapp.system': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'System', '_ormbases': ['objectapp.Gbobject']},
+ 'attribute_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'in_attribute_set_of'", 'blank': 'True', 'to': "orm['gstudio.Attribute']"}),
+ 'gbobject_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['objectapp.Gbobject']", 'unique': 'True', 'primary_key': 'True'}),
+ 'gbobject_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'in_gbobject_set_of'", 'blank': 'True', 'to': "orm['objectapp.Gbobject']"}),
+ 'process_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'in_process_set_of'", 'blank': 'True', 'to': "orm['objectapp.Process']"}),
+ 'relation_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'in_relation_set_of'", 'blank': 'True', 'to': "orm['gstudio.Relation']"}),
+ 'system_set': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'system_set_rel_+'", 'blank': 'True', 'to': "orm['objectapp.System']"}),
+ 'systemtypes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'member_systems'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['gstudio.Systemtype']"})
+ },
+ 'sites.site': {
+ 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
+ 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ }
+ }
+
+ complete_apps = ['objectapp']
diff --git a/objectapp/migrations/__init__.py b/objectapp/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/objectapp/migrations/__init__.py
diff --git a/objectapp/models.py b/objectapp/models.py
new file mode 100644
index 0000000..94fa7e1
--- /dev/null
+++ b/objectapp/models.py
@@ -0,0 +1,700 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Models of Objectapp"""
+import warnings
+from datetime import datetime
+
+from django.db import models
+from django.db.models import Q
+from django.utils.html import strip_tags
+from django.utils.html import linebreaks
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.db.models.signals import post_save
+from django.utils.importlib import import_module
+from django.contrib import comments
+from django.contrib.comments.models import CommentFlag
+from django.contrib.comments.moderation import moderator
+from django.utils.translation import ugettext_lazy as _
+import json
+from django.contrib.markup.templatetags.markup import markdown
+from django.contrib.markup.templatetags.markup import textile
+from django.contrib.markup.templatetags.markup import restructuredtext
+
+
+from djangoratings.fields import RatingField
+from tagging.fields import TagField
+from gstudio.models import Nodetype
+from gstudio.models import Objecttype
+from gstudio.models import Relationtype
+from gstudio.models import Systemtype
+from gstudio.models import Processtype
+from gstudio.models import Attributetype
+from gstudio.models import Attribute
+from gstudio.models import Relation
+from gstudio.models import Node
+from gstudio.models import Edge
+from gstudio.models import Author
+
+import reversion
+from objectapp.settings import UPLOAD_TO
+from objectapp.settings import MARKUP_LANGUAGE
+from objectapp.settings import GBOBJECT_TEMPLATES
+from objectapp.settings import GBOBJECT_BASE_MODEL
+from objectapp.settings import MARKDOWN_EXTENSIONS
+from objectapp.settings import AUTO_CLOSE_COMMENTS_AFTER
+from objectapp.managers import gbobjects_published
+from objectapp.managers import GbobjectPublishedManager
+from objectapp.managers import AuthorPublishedManager
+from objectapp.managers import DRAFT, HIDDEN, PUBLISHED
+from objectapp.moderator import GbobjectCommentModerator
+from objectapp.url_shortener import get_url_shortener
+from objectapp.signals import ping_directories_handler
+from objectapp.signals import ping_external_urls_handler
+
+'''
+class Author(User):
+ """Proxy Model around User"""
+
+ objects = models.Manager()
+ published = AuthorPublishedManager()
+
+ def gbobjects_published(self):
+ """Return only the gbobjects published"""
+ return gbobjects_published(self.gbobjects)
+
+ #@models.permalink
+ def get_absolute_url(self):
+ """Return author's URL"""
+ return "/authors/%s/" %(self.username)
+ #return ('objectapp_author_detail', (self.username,))
+
+ class Meta:
+ """Author's Meta"""
+ proxy = True
+'''
+
+
+class Gbobject(Node):
+ """
+ Member nodes of object types. This is actually to be named the
+ Object class, since 'Object' is a reserved name in Python, we
+ prefix this with 'Gb', to suggest that it is an object of the gnowledge
+ base. System and Process classes also inherit this class.
+ """
+
+
+ STATUS_CHOICES = ((DRAFT, _('draft')),
+ (HIDDEN, _('hidden')),
+ (PUBLISHED, _('published')))
+
+ content = models.TextField(_('content'), null=True, blank=True)
+ image = models.ImageField(_('image'), upload_to=UPLOAD_TO,
+ blank=True, help_text=_('used for illustration'))
+
+ excerpt = models.TextField(_('excerpt'), blank=True,
+ help_text=_('optional element'))
+
+ prior_nodes = models.ManyToManyField('self', null=True, blank=True,
+ verbose_name=_('depends on'),
+ related_name='posterior_nodes')
+
+ posterior_nodes = models.ManyToManyField('self', null=True, blank=True,
+ verbose_name=_('required for'),
+ related_name='prior_nodes')
+
+
+ tags = TagField(_('tags'))
+ objecttypes = models.ManyToManyField(Nodetype, verbose_name=_('member of'),
+ related_name='member_objects',
+ blank=True, null=True)
+
+
+ authors = models.ManyToManyField(User, verbose_name=_('authors'),
+ related_name='gbobjects',
+ blank=True, null=False)
+ status = models.IntegerField(choices=STATUS_CHOICES, default=PUBLISHED)
+
+ featured = models.BooleanField(_('featured'), default=False)
+ comment_enabled = models.BooleanField(_('comment enabled'), default=True)
+ pingback_enabled = models.BooleanField(_('linkback enabled'), default=True)
+
+
+ start_publication = models.DateTimeField(_('start publication'),
+ help_text=_('date start publish'),
+ default=datetime.now)
+ end_publication = models.DateTimeField(_('end publication'),
+ help_text=_('date end publish'),
+ default=datetime(2042, 3, 15))
+
+ sites = models.ManyToManyField(Site, verbose_name=_('sites publication'),
+ related_name='gbobjects')
+
+ login_required = models.BooleanField(
+ _('login required'), default=False,
+ help_text=_('only authenticated users can view the gbobject'))
+ password = models.CharField(
+ _('password'), max_length=50, blank=True,
+ help_text=_('protect the gbobject with a password'))
+
+ template = models.CharField(
+ _('template'), max_length=250,
+ default='objectapp/gbobject_detail.html',
+ choices=[('objectapp/gbobject_detail.html', _('Default template'))] + \
+ GBOBJECT_TEMPLATES,
+ help_text=_('template used to display the gbobject'))
+
+ objects = models.Manager()
+ published = GbobjectPublishedManager()
+
+
+ def get_relations(self):
+ relation_set = {}
+ # ALGO to find the relations and their left-subjecttypes and right_subjecttypes
+ # 1. Get the relations containing a reference to the object. Retrieve where it occurs (left or right)
+ # 2. Find out which RT they come from.
+ # 3. For each RT, create a dict key and a value as a dict. And add the relation as a new key-value pair (rid:subject).
+ # 4. If self is in right value, then add inverse relation as RT and add the relation as a new key-value pair (rid:subject).
+
+ left_relset = Relation.objects.filter(left_subject=self.id)
+ right_relset = Relation.objects.filter(right_subject=self.id)
+
+ #return left_relset + right_relset
+
+ # RT dictionary to store a single relation
+ rel_dict ={}
+ rel_dict['left-subjecttypes'] = {}
+ rel_dict['right_subjecttypes'] ={}
+
+
+ for relation in left_relset:
+ # check if relation already exists
+ if relation.relationtype.title not in rel_dict['left-subjecttypes'].keys():
+ # create a new list field and add to it
+ rel_dict['left-subjecttypes'][str(relation.relationtype.title)] = []
+ # add
+ rel_dict['left-subjecttypes'][str(relation.relationtype.title)].append(relation)
+
+ for relation in right_relset:
+ # check if relation exists
+ if relation.relationtype.inverse not in rel_dict['right_subjecttypes'].keys():
+ # create a new list key field and add to it
+ rel_dict['right_subjecttypes'][str(relation.relationtype.inverse)] = []
+ # add to the existing key
+ rel_dict['right_subjecttypes'][str(relation.relationtype.inverse)].append(relation)
+
+ relation_set.update(rel_dict['left-subjecttypes'])
+ relation_set.update(rel_dict['right_subjecttypes'])
+
+ return relation_set
+
+
+ def get_attributes(self):
+ attributes = {}
+ for attribute in Attribute.objects.filter(subject=self.id):
+ for key,value in attribute.edge_node_dict.iteritems():
+ attributes[key]= value
+
+ return attributes
+
+
+
+ def get_possible_rels(self):
+ """
+ Gets the relations possible for this metatype
+ 1. Recursively create a set of all the ancestors i.e. parent/subtypes of the MT.
+ 2. Get all the R's linked to each ancestor
+ """
+ #Step 1.
+ ancestor_list = []
+ this_parent = self.parent
+
+ # append
+ while this_parent:
+ ancestor_list.append(this_parent)
+ this_parent = this_parent.parent
+
+ #Step 2.
+ rels = {}
+ rt_set = Relation.objects.all()
+ right_subset = []
+ left_subset = []
+
+ for each in ancestor_list:
+ # retrieve all the RT's from each ancestor
+ right_subset.extend(rt_set.filter(subject1=each.id))
+ left_subset.extend(rt_set.filter(subject2=each.id))
+
+ rels['possible_leftroles'] = left_subset
+ rels['possible_rightroles'] = right_subset
+
+ return rels
+
+
+ def get_possible_attributes(self):
+ """
+ Gets the relations possible for this metatype
+ 1. Recursively create a set of all the ancestors i.e. parent/subtypes of the MT.
+ 2. Get all the RT's linked to each ancestor
+ """
+ #Step 1.
+ ancestor_list = []
+ this_parent = self.parent
+
+ # recursive thru parent field and append
+ while this_parent:
+ ancestor_list.append(this_parent)
+ this_parent = this_parent.parent
+
+ #Step 2.
+ attrs = []
+
+ for each in ancestor_list:
+ # retrieve all the AT's from each ancestor
+ attrs.extend(Attribute.objects.filter(subject=each.id))
+
+ return attrs
+
+
+
+
+ @property
+ def get_nbh(self):
+ """
+ Returns the neighbourhood of the object
+ """
+ fields = ['title','altname','pluralform']
+ nbh = {}
+ nbh['title'] = self.title
+ nbh['altnames'] = self.altnames
+ nbh['plural'] = self.plural
+ nbh['content'] = self.content
+ #return all OTs the object is linked to
+ nbh['member_of'] = self.objecttypes.all()
+
+ # get all the relations of the object
+ nbh.update(self.get_relations())
+ nbh.update(self.get_attributes())
+ # encapsulate the dictionary with its node name as key
+ return nbh
+
+
+ def get_graph_json(self):
+
+ predicate_id={"plural":"a1","altnames":"a2","member_of":"a3"}
+ g_json = {}
+ g_json["node_metadata"]= []
+
+
+ nbh = self.get_nbh
+
+ this_node = {"_id":str(self.id),"title":self.title,"screen_name":self.title, "url":self.get_absolute_url()}
+ for key in predicate_id.keys():
+ if nbh[key]:
+ try:
+ g_json[str(key)]=[]
+ g_json["node_metadata"].append({"_id":str(predicate_id[key]),"screen_name":key})
+ g_json[str(key)].append({"from":self.id , "to":predicate_id[key],"value":1 })
+ if not isinstance(nbh[key],basestring):
+ for item in nbh[key]:
+
+ g_json["node_metadata"].append({"_id":str(item.id),"screen_name":item.title, "title":item.title, "url":item.get_absolute_url()})
+ g_json[str(key)].append({"from":predicate_id[key] , "to":item.id ,"value":1 })
+
+ else:
+ value={nbh["plural"]:"a4",nbh["altnames"]:"a5"}
+ this_node[str(key)]=nbh[key]
+
+ for item in value.keys():
+ g_json["node_metadata"].append({"_id":str(value[nbh[key]]),"screen_name":nbh[key]})
+ g_json[str(key)].append({"from":predicate_id[key] , "to":value[nbh[key]] ,"value":1 })
+
+
+ except:
+ pass
+ g_json["node_metadata"].append(this_node)
+ return json.dumps(g_json)
+
+
+ @property
+ def get_relations1(self):
+ """
+ Returns all the relations of the nodetype
+ """
+ relations={}
+ reltype={}
+ left_relations=Relation.objects.filter(left_subject=self.id)
+ if left_relations:
+ for each in left_relations:
+ relation=each.relationtype.title
+ predicate=each.right_subject
+ predicate_values=[]
+ if reltype:
+ for key,value in reltype.items():
+ predicate_values=value
+ if each.relationtype.title==key:
+ predicate_values.append(predicate)
+ reltype[key]=predicate_values
+ break
+ else:
+ predicate_values=predicate
+ reltype[relation]=predicate_values
+ break
+ else:
+ predicate_values.append(predicate)
+ reltype[relation]=predicate_values
+ relations['lrelations']=reltype
+
+ right_relations=Relation.objects.filter(right_subject=self.id)
+ reltype={}
+ if right_relations:
+ for each in right_relations:
+ relation=each.relationtype.inverse
+ predicate=each.left_subject
+ predicate_values=[]
+ if reltype:
+ for key,value in reltype.items():
+ predicate_values=value
+ if each.relationtype.inverse==key:
+ predicate_values.append(predicate)
+ reltype[key]=predicate_values
+ break
+ else:
+ predicate_values=predicate
+ reltype[relation]=predicate_values
+ break
+ else:
+ predicate_values.append(predicate)
+ reltype[relation]=predicate_values
+ relations['rrelations']=reltype
+ return relations
+ @property
+ def get_rendered_nbh(self):
+ """
+ Returns the neighbourhood of the object
+ """
+ fields = ['title','altname','pluralform']
+ nbh = {}
+ nbh['title'] = self.title
+ nbh['altnames'] = self.altnames
+ nbh['plural']=self.plural
+ nbh['content'] = self.content
+ #return all OTs the object is linked to
+ member_of_dict = {}
+ for each in self.objecttypes.all():
+ member_of_dict[each.title]= each.get_absolute_url()
+ nbh['member_of']=member_of_dict
+ #get Relations
+ relns={}
+ rellft={}
+ relrgt={}
+ if self.get_relations1:
+ NTrelns=self.get_relations1
+ for key,value in NTrelns.items():
+ if key=="rrelations":
+ relrgt={}
+ for rgtkey,rgtvalue in value.items():
+ relnvalue={}
+ if isinstance(rgtvalue,list):
+ for items in rgtvalue:
+ relnvalue[items]=items.get_absolute_url()
+ else:
+ relnvalue[rgtvalue]=rgtvalue.get_absolute_url()
+
+ relrgt[rgtkey]=relnvalue
+
+ else:
+ rellft={}
+ relns['left']=rellft
+ for lftkey,lftvalue in value.items():
+ relnvalue={}
+ if isinstance(lftvalue,list):
+ for items in lftvalue:
+ relnvalue[items]=items.get_absolute_url()
+ else:
+ relnvalue[lftvalue]=lftvalue.get_absolute_url()
+
+ rellft[lftkey]=relnvalue
+
+ nbh['relations']=relrgt
+ nbh['relations'].update(rellft)
+
+ #get Attributes
+ attributes ={}
+ for each in self.subject_of.all():
+ attributes[each.attributetype]=each.svalue
+ nbh['attributes']=attributes
+ return nbh
+
+
+
+ @property
+ def html_content(self):
+ """Return the content correctly formatted"""
+ if MARKUP_LANGUAGE == 'markdown':
+ return markdown(self.content, MARKDOWN_EXTENSIONS)
+ elif MARKUP_LANGUAGE == 'textile':
+ return textile(self.content)
+ elif MARKUP_LANGUAGE == 'restructuredtext':
+ return restructuredtext(self.content)
+ elif not '</p>' in self.content:
+ return linebreaks(self.content)
+ return self.content
+
+
+ @property
+ def previous_gbobject(self):
+ """Return the previous gbobject"""
+ gbobjects = Gbobject.published.filter(
+ creation_date__lt=self.creation_date)[:1]
+ if gbobjects:
+ return gbobjects[0]
+
+ @property
+ def next_gbobject(self):
+ """Return the next gbobject"""
+ gbobjects = Gbobject.published.filter(
+ creation_date__gt=self.creation_date).order_by('creation_date')[:1]
+ if gbobjects:
+ return gbobjects[0]
+
+ @property
+ def word_count(self):
+ """Count the words of an gbobject"""
+ return len(strip_tags(self.html_content).split())
+
+ @property
+ def is_actual(self):
+ """Check if an gbobject is within publication period"""
+ now = datetime.now()
+ return now >= self.start_publication and now < self.end_publication
+
+ @property
+ def is_visible(self):
+ """Check if an gbobject is visible on site"""
+ return self.is_actual and self.status == PUBLISHED
+
+ @property
+ def related_published(self):
+ """Return only related gbobjects published"""
+ return gbobjects_published(self.related)
+
+ @property
+ def discussions(self):
+ """Return published discussions"""
+ return comments.get_model().objects.for_model(
+ self).filter(is_public=True)
+
+ @property
+ def comments(self):
+ """Return published comments"""
+ return self.discussions.filter(Q(flags=None) | Q(
+ flags__flag=CommentFlag.MODERATOR_APPROVAL))
+
+ @property
+ def pingbacks(self):
+ """Return published pingbacks"""
+ return self.discussions.filter(flags__flag='pingback')
+
+ @property
+ def trackbacks(self):
+ """Return published trackbacks"""
+ return self.discussions.filter(flags__flag='trackback')
+
+ @property
+ def comments_are_open(self):
+ """Check if comments are open"""
+ if AUTO_CLOSE_COMMENTS_AFTER and self.comment_enabled:
+ return (datetime.now() - self.start_publication).days < \
+ AUTO_CLOSE_COMMENTS_AFTER
+ return self.comment_enabled
+
+ @property
+ def short_url(self):
+ """Return the gbobject's short url"""
+ return get_url_shortener()(self)
+
+ def __unicode__(self):
+ return self.title
+
+ @property
+ def memberof_sentence(self):
+ """Return the objecttype of which the gbobject is a member of"""
+
+ if self.objecttypes.count:
+ for each in self.objecttypes.all():
+ return '%s is a member of objecttype %s' % (self.title, each)
+ return '%s is not a fully defined name, consider making it a member of a suitable objecttype' % (self.title)
+
+
+ @models.permalink
+ def get_absolute_url(self):
+ """Return gbobject's URL"""
+ return ('objectapp_gbobject_detail', (), {
+ 'year': self.creation_date.strftime('%Y'),
+ 'month': self.creation_date.strftime('%m'),
+ 'day': self.creation_date.strftime('%d'),
+ 'slug': self.slug})
+
+ class Meta:
+ """Gbobject's Meta"""
+ ordering = ['-creation_date']
+ verbose_name = _('object')
+ verbose_name_plural = _('objects')
+ permissions = (('can_view_all', 'Can view all'),
+ ('can_change_author', 'Can change author'), )
+
+
+
+class Process(Gbobject):
+
+ """
+ A store processes, events or changes described as changes in attributes and relations
+ """
+ processtypes = models.ManyToManyField(Processtype, verbose_name=_('member of process type'),
+ related_name='member_processes',
+ blank=True, null=True)
+ priorstate_attribute_set = models.ManyToManyField(Attribute, null=True, blank=True,
+ verbose_name=_('in prior state attribute set of'),
+ related_name='in_prior_state_attrset_of')
+ priorstate_relation_set = models.ManyToManyField(Relation, null=True, blank=True,
+ verbose_name=_('priorsate of relation set'),
+ related_name='in_prior_state_relset_of')
+
+ poststate_attribute_set = models.ManyToManyField(Attribute, null=True, blank=True,
+ verbose_name=_('poststate of attribute set'),
+ related_name='in_post_state_attrset_of')
+
+ poststate_relation_set = models.ManyToManyField(Relation, null=True, blank=True,
+ verbose_name=_('in poststate of relation set'),
+ related_name='in_post_state_relset_of')
+
+
+
+
+ def __unicode__(self):
+ return self.title
+
+ class Meta:
+ verbose_name = _('process')
+ verbose_name_plural = _('processes')
+ permissions = (('can_view_all', 'Can view all'),
+ ('can_change_author', 'Can change author'), )
+
+
+class System(Gbobject):
+
+ """
+ instance of a Systemtype
+ """
+
+ systemtypes = models.ManyToManyField(Systemtype, verbose_name=_('member of systemtype'),
+ related_name='member_systems',
+ blank=True, null=True)
+
+ gbobject_set = models.ManyToManyField(Gbobject, related_name="in_gbobject_set_of",
+ verbose_name='objects in the system',
+ blank=True, null=False)
+
+ relation_set = models.ManyToManyField(Relation, related_name="in_relation_set_of",
+ verbose_name='relations in the system',
+ blank=True, null=False)
+
+ attribute_set = models.ManyToManyField(Attribute, related_name="in_attribute_set_of",
+ verbose_name='attributes in the system',
+ blank=True, null=False)
+
+ process_set = models.ManyToManyField(Process, related_name="in_process_set_of",
+ verbose_name='processes in the system',
+ blank=True, null=False)
+
+ system_set = models.ManyToManyField('self', related_name="in_system_set_of",
+ verbose_name='nested systems',
+ blank=True, null=False)
+
+
+ def __unicode__(self):
+ return self.title
+
+
+
+if not reversion.is_registered(Process):
+ reversion.register(Process, follow=["priorstate_attribute_set", "priorstate_relation_set", "poststate_attribute_set", "poststate_relation_set", "prior_nodes", "posterior_nodes"])
+
+if not reversion.is_registered(System):
+ reversion.register(System, follow=["systemtypes", "gbobject_set", "relation_set", "attribute_set", "process_set", "system_set", "prior_nodes", "posterior_nodes"])
+
+if not reversion.is_registered(Gbobject):
+ reversion.register(Gbobject, follow=["objecttypes", "prior_nodes", "posterior_nodes"])
+
+
+moderator.register(Gbobject, GbobjectCommentModerator)
+
+post_save.connect(ping_directories_handler, sender=Gbobject,
+ dispatch_uid='objectapp.gbobject.post_save.ping_directories')
+post_save.connect(ping_external_urls_handler, sender=Gbobject,
+ dispatch_uid='objectapp.gbobject.post_save.ping_external_urls')
+
+
diff --git a/objectapp/moderator.py b/objectapp/moderator.py
new file mode 100644
index 0000000..3b87440
--- /dev/null
+++ b/objectapp/moderator.py
@@ -0,0 +1,194 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Moderator of Objectapp comments"""
+from django.conf import settings
+from django.template import Context
+from django.template import loader
+from django.core.mail import send_mail
+from django.core.mail import EmailMessage
+from django.contrib.sites.models import Site
+from django.utils.translation import activate
+from django.utils.translation import get_language
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.comments.moderation import CommentModerator
+
+from objectapp.settings import PROTOCOL
+from objectapp.settings import MAIL_COMMENT_REPLY
+from objectapp.settings import MAIL_COMMENT_AUTHORS
+from objectapp.settings import AUTO_MODERATE_COMMENTS
+from objectapp.settings import AUTO_CLOSE_COMMENTS_AFTER
+from objectapp.settings import MAIL_COMMENT_NOTIFICATION_RECIPIENTS
+from objectapp.settings import SPAM_CHECKER_BACKENDS
+from objectapp.spam_checker import check_is_spam
+
+
+class GbobjectCommentModerator(CommentModerator):
+ """Moderate the comment of Gbobject"""
+ email_reply = MAIL_COMMENT_REPLY
+ email_authors = MAIL_COMMENT_AUTHORS
+ enable_field = 'comment_enabled'
+ auto_close_field = 'start_publication'
+ close_after = AUTO_CLOSE_COMMENTS_AFTER
+ spam_checker_backends = SPAM_CHECKER_BACKENDS
+ auto_moderate_comments = AUTO_MODERATE_COMMENTS
+ mail_comment_notification_recipients = MAIL_COMMENT_NOTIFICATION_RECIPIENTS
+
+ def email(self, comment, content_object, request):
+ if comment.is_public:
+ current_language = get_language()
+ try:
+ activate(settings.LANGUAGE_CODE)
+ if self.mail_comment_notification_recipients:
+ self.do_email_notification(comment, content_object,
+ request)
+ if self.email_authors:
+ self.do_email_authors(comment, content_object,
+ request)
+ if self.email_reply:
+ self.do_email_reply(comment, content_object, request)
+ finally:
+ activate(current_language)
+
+ def do_email_notification(self, comment, content_object, request):
+ """Send email notification of a new comment to site staff when email
+ notifications have been requested."""
+ site = Site.objects.get_current()
+ template = loader.get_template(
+ 'comments/comment_notification_email.txt')
+ context = Context({'comment': comment, 'site': site,
+ 'protocol': PROTOCOL,
+ 'content_object': content_object})
+ subject = _('[%(site)s] New comment posted on "%(title)s"') % \
+ {'site': site.name,
+ 'title': content_object.title}
+ message = template.render(context)
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
+ self.mail_comment_notification_recipients,
+ fail_silently=not settings.DEBUG)
+
+ def do_email_authors(self, comment, content_object, request):
+ """Send email notification of a new comment to the authors of the
+ gbobject when email notifications have been requested."""
+ exclude_list = self.mail_comment_notification_recipients
+ recipient_list = set([author.email
+ for author in content_object.authors.all()]) ^ \
+ set(exclude_list)
+
+ if recipient_list:
+ site = Site.objects.get_current()
+ template = loader.get_template(
+ 'comments/comment_authors_email.txt')
+ context = Context({'comment': comment, 'site': site,
+ 'protocol': PROTOCOL,
+ 'content_object': content_object})
+ subject = _('[%(site)s] New comment posted on "%(title)s"') % \
+ {'site': site.name,
+ 'title': content_object.title}
+ message = template.render(context)
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
+ recipient_list, fail_silently=not settings.DEBUG)
+
+ def do_email_reply(self, comment, content_object, request):
+ """Send email notification of a new comment to the authors of
+ the previous comments when email notifications have been requested."""
+ exclude_list = self.mail_comment_notification_recipients + \
+ [author.email
+ for author in content_object.authors.all()] + \
+ [comment.userinfo['email']]
+ recipient_list = set([comment.userinfo['email']
+ for comment in content_object.comments
+ if comment.userinfo['email']]) ^ \
+ set(exclude_list)
+
+ if recipient_list:
+ site = Site.objects.get_current()
+ template = loader.get_template('comments/comment_reply_email.txt')
+ context = Context({'comment': comment, 'site': site,
+ 'protocol': PROTOCOL,
+ 'content_object': content_object})
+ subject = _('[%(site)s] New comment posted on "%(title)s"') % \
+ {'site': site.name,
+ 'title': content_object.title}
+ message = template.render(context)
+ mail = EmailMessage(subject, message,
+ settings.DEFAULT_FROM_EMAIL,
+ bcc=recipient_list)
+ mail.send(fail_silently=not settings.DEBUG)
+
+ def moderate(self, comment, content_object, request):
+ """Determine whether a given comment on a given object should be
+ allowed to show up immediately, or should be marked non-public
+ and await approval."""
+ if self.auto_moderate_comments:
+ return True
+
+ if check_is_spam(comment, content_object, request,
+ self.spam_checker_backends):
+ comment.save()
+ user = comment.content_object.authors.all()[0]
+ comment.flags.create(user=user, flag='spam')
+ return True
+
+ return False
diff --git a/objectapp/ping.py b/objectapp/ping.py
new file mode 100644
index 0000000..3a816e3
--- /dev/null
+++ b/objectapp/ping.py
@@ -0,0 +1,231 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Pings utilities for Objectapp"""
+import socket
+import xmlrpclib
+import threading
+from urllib2 import urlopen
+from urlparse import urlsplit
+from logging import getLogger
+
+from BeautifulSoup import BeautifulSoup
+
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import reverse
+
+from objectapp.settings import PROTOCOL
+
+
+class URLRessources(object):
+ """Object defining the ressources of the website"""
+
+ def __init__(self):
+ self.current_site = Site.objects.get_current()
+ self.site_url = '%s://%s' % (PROTOCOL, self.current_site.domain)
+ self.blog_url = '%s%s' % (self.site_url,
+ reverse('objectapp_gbobject_archive_index'))
+ self.blog_feed = '%s%s' % (self.site_url,
+ reverse('objectapp_gbobject_latest_feed'))
+
+
+class DirectoryPinger(threading.Thread):
+ """Threaded Directory Pinger"""
+
+ def __init__(self, server_name, gbobjects, timeout=10, start_now=True):
+ self.results = []
+ self.timeout = timeout
+ self.gbobjects = gbobjects
+ self.server_name = server_name
+ self.server = xmlrpclib.ServerProxy(self.server_name)
+ self.ressources = URLRessources()
+
+ threading.Thread.__init__(self)
+ if start_now:
+ self.start()
+
+ def run(self):
+ """Ping gbobjects to a Directory in a Thread"""
+ logger = getLogger('objectapp.ping.directory')
+ socket.setdefaulttimeout(self.timeout)
+ for gbobject in self.gbobjects:
+ reply = self.ping_gbobject(gbobject)
+ self.results.append(reply)
+ logger.info('%s : %s' % (self.server_name, reply['message']))
+ socket.setdefaulttimeout(None)
+
+ def ping_gbobject(self, gbobject):
+ """Ping an gbobject to a Directory"""
+ gbobject_url = '%s%s' % (self.ressources.site_url,
+ gbobject.get_absolute_url())
+ objecttypes = '|'.join([c.title for c in gbobject.objecttypes.all()])
+
+ try:
+ reply = self.server.weblogUpdates.extendedPing(
+ self.ressources.current_site.name,
+ self.ressources.blog_url, gbobject_url,
+ self.ressources.blog_feed, objecttypes)
+ except Exception:
+ try:
+ reply = self.server.weblogUpdates.ping(
+ self.ressources.current_site.name,
+ self.ressources.blog_url, gbobject_url,
+ objecttypes)
+ except Exception:
+ reply = {'message': '%s is an invalid directory.' % \
+ self.server_name,
+ 'flerror': True}
+ return reply
+
+
+class ExternalUrlsPinger(threading.Thread):
+ """Threaded ExternalUrls Pinger"""
+
+ def __init__(self, gbobject, timeout=10, start_now=True):
+ self.results = []
+ self.gbobject = gbobject
+ self.timeout = timeout
+ self.ressources = URLRessources()
+ self.gbobject_url = '%s%s' % (self.ressources.site_url,
+ self.gbobject.get_absolute_url())
+
+ threading.Thread.__init__(self)
+ if start_now:
+ self.start()
+
+ def run(self):
+ """Ping external URLS in a Thread"""
+ logger = getLogger('objectapp.ping.external_urls')
+ socket.setdefaulttimeout(self.timeout)
+
+ external_urls = self.find_external_urls(self.gbobject)
+ external_urls_pingable = self.find_pingback_urls(external_urls)
+
+ for url, server_name in external_urls_pingable.items():
+ reply = self.pingback_url(server_name, url)
+ self.results.append(reply)
+ logger.info('%s : %s' % (url, reply))
+
+ socket.setdefaulttimeout(None)
+
+ def is_external_url(self, url, site_url):
+ """Check of the url in an external url"""
+ url_splitted = urlsplit(url)
+ if not url_splitted.netloc:
+ return False
+ return url_splitted.netloc != urlsplit(site_url).netloc
+
+ def find_external_urls(self, gbobject):
+ """Find external urls in an gbobject"""
+ soup = BeautifulSoup(gbobject.html_content)
+ external_urls = [a['href'] for a in soup.findAll('a')
+ if self.is_external_url(
+ a['href'], self.ressources.site_url)]
+ return external_urls
+
+ def find_pingback_href(self, content):
+ """Try to find Link markup to pingback url"""
+ soup = BeautifulSoup(content)
+ for link in soup.findAll('link'):
+ dict_attr = dict(link.attrs)
+ if 'rel' in dict_attr and 'href' in dict_attr:
+ if dict_attr['rel'].lower() == 'pingback':
+ return dict_attr.get('href')
+
+ def find_pingback_urls(self, urls):
+ """Find the pingback urls of each urls"""
+ pingback_urls = {}
+
+ for url in urls:
+ try:
+ page = urlopen(url)
+ headers = page.info()
+
+ if 'text/' not in headers.get('Content-Type', '').lower():
+ continue
+
+ server_url = headers.get('X-Pingback')
+ if not server_url:
+ server_url = self.find_pingback_href(page.read())
+
+ if server_url:
+ server_url_splitted = urlsplit(server_url)
+ if not server_url_splitted.netloc:
+ url_splitted = urlsplit(url)
+ server_url = '%s://%s%s' % (url_splitted.scheme,
+ url_splitted.netloc,
+ server_url)
+ pingback_urls[url] = server_url
+ except IOError:
+ pass
+ return pingback_urls
+
+ def pingback_url(self, server_name, target_url):
+ """Do a pingback call for the target url"""
+ try:
+ server = xmlrpclib.ServerProxy(server_name)
+ reply = server.pingback.ping(self.gbobject_url, target_url)
+ except (xmlrpclib.Error, socket.error):
+ reply = '%s cannot be pinged.' % target_url
+ return reply
diff --git a/objectapp/plugins/__init__.py b/objectapp/plugins/__init__.py
new file mode 100644
index 0000000..499b630
--- /dev/null
+++ b/objectapp/plugins/__init__.py
@@ -0,0 +1 @@
+"""Plugins for Objectapp"""
diff --git a/objectapp/plugins/admin.py b/objectapp/plugins/admin.py
new file mode 100644
index 0000000..677ea8b
--- /dev/null
+++ b/objectapp/plugins/admin.py
@@ -0,0 +1,78 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Admin of Objectapp CMS Plugins"""
+from django.contrib import admin
+from django.template import RequestContext
+from django.utils.translation import ugettext_lazy as _
+
+from cms.plugin_rendering import render_placeholder
+from cms.admin.placeholderadmin import PlaceholderAdmin
+
+from objectapp.models import Gbobject
+from objectapp.admin.gbobject import GbobjectAdmin
+from objectapp.settings import GBOBJECT_BASE_MODEL
+
+
+class GbobjectPlaceholderAdmin(PlaceholderAdmin, GbobjectAdmin):
+ """GbobjectPlaceholder Admin"""
+ fieldsets = ((None, {'fields': ('title', 'image', 'status')}),
+ (_('Content'), {'fields': ('content_placeholder',),
+ 'classes': ('plugin-holder',
+ 'plugin-holder-nopage')}),
+ (_('Options'), {'fields': ('featured', 'excerpt', 'template',
+ 'related', 'authors',
+ 'creation_date',
+ 'start_publication',
+ 'end_publication'),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Privacy'), {'fields': ('password', 'login_required',),
+ 'classes': ('collapse', 'collapse-closed')}),
+ (_('Discussion'), {'fields': ('comment_enabled',
+ 'pingback_enabled')}),
+ (_('Publication'), {'fields': ('sites', 'objecttypes',
+ 'tags', 'slug')}))
+
+ def save_model(self, request, gbobject, form, change):
+ """Fill the content field with the interpretation
+ of the placeholder"""
+ context = RequestContext(request)
+ gbobject.content = render_placeholder(gbobject.content_placeholder, context)
+ super(GbobjectPlaceholderAdmin, self).save_model(
+ request, gbobject, form, change)
+
+
+if GBOBJECT_BASE_MODEL == 'objectapp.plugins.placeholder.GbobjectPlaceholder':
+ admin.site.unregister(Gbobject)
+ admin.site.register(Gbobject, GbobjectPlaceholderAdmin)
diff --git a/objectapp/plugins/cms_app.py b/objectapp/plugins/cms_app.py
new file mode 100644
index 0000000..01b0313
--- /dev/null
+++ b/objectapp/plugins/cms_app.py
@@ -0,0 +1,63 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Applications hooks for objectapp.plugins"""
+from django.utils.translation import ugettext_lazy as _
+
+from cms.app_base import CMSApp
+from cms.apphook_pool import apphook_pool
+
+from objectapp.plugins.settings import APP_MENUS
+
+
+class ObjectappApphook(CMSApp):
+ """Objectapp's Apphook"""
+ name = _('Objectapp App Hook')
+ urls = ['objectapp.urls']
+ menus = APP_MENUS
+
+apphook_pool.register(ObjectappApphook)
diff --git a/objectapp/plugins/cms_plugins.py b/objectapp/plugins/cms_plugins.py
new file mode 100644
index 0000000..e72496b
--- /dev/null
+++ b/objectapp/plugins/cms_plugins.py
@@ -0,0 +1,182 @@
+g# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Plugins for CMS"""
+import itertools
+
+from django.conf import settings
+from django.utils.translation import ugettext as _
+
+from tagging.models import TaggedItem
+from cms.plugin_base import CMSPluginBase
+from cms.plugin_pool import plugin_pool
+
+from objectapp.models import Gbobject
+from objectapp.models import Author
+from objectapp.managers import tags_published
+from objectapp.plugins.models import RandomGbobjectsPlugin
+from objectapp.plugins.models import LatestGbobjectsPlugin
+from objectapp.plugins.models import SelectedGbobjectsPlugin
+
+
+class CMSLatestGbobjectsPlugin(CMSPluginBase):
+ """Django-cms plugin for the latest gbobjects filtered"""
+ module = _('gbobjects')
+ model = LatestGbobjectsPlugin
+ name = _('Latest gbobjects')
+ render_template = 'objectapp/cms/gbobject_list.html'
+ filter_horizontal = ['objecttypes', 'authors', 'tags']
+ fieldsets = (
+ (None, {
+ 'fields': (
+ 'number_of_gbobjects',
+ 'template_to_render'
+ )
+ }),
+ (_('Sorting'), {
+ 'fields': (
+ 'objecttypes',
+ 'authors',
+ 'tags'
+ ),
+ 'classes': (
+ 'collapse',
+ )
+ }),
+ (_('Advanced'), {
+ 'fields': (
+ 'subobjecttypes',
+ ),
+ }),
+ )
+
+ text_enabled = True
+
+ def formfield_for_manytomany(self, db_field, request, **kwargs):
+ """Filtering manytomany field"""
+ if db_field.name == 'authors':
+ kwargs['queryset'] = Author.published.all()
+ if db_field.name == 'tags':
+ kwargs['queryset'] = tags_published()
+ return super(CMSLatestGbobjectsPlugin, self).formfield_for_manytomany(
+ db_field, request, **kwargs)
+
+ def render(self, context, instance, placeholder):
+ """Update the context with plugin's data"""
+ gbobjects = Gbobject.published.all()
+
+ if instance.objecttypes.count():
+ cats = instance.objecttypes.all()
+
+ if instance.subobjecttypes:
+ cats = itertools.chain(cats, *[c.get_descendants()
+ for c in cats])
+
+ gbobjects = gbobjects.filter(objecttypes__in=cats)
+ if instance.authors.count():
+ gbobjects = gbobjects.filter(authors__in=instance.authors.all())
+ if instance.tags.count():
+ gbobjects = TaggedItem.objects.get_union_by_model(
+ gbobjects, instance.tags.all())
+
+ gbobjects = gbobjects.distinct()[:instance.number_of_gbobjects]
+ context.update({'gbobjects': gbobjects,
+ 'object': instance,
+ 'placeholder': placeholder})
+ return context
+
+ def icon_src(self, instance):
+ """Icon source of the plugin"""
+ return settings.STATIC_URL + u'objectapp/img/plugin.png'
+
+
+class CMSSelectedGbobjectsPlugin(CMSPluginBase):
+ """Django-cms plugin for a selection of gbobjects"""
+ module = _('gbobjects')
+ model = SelectedGbobjectsPlugin
+ name = _('Selected gbobjects')
+ render_template = 'objectapp/cms/gbobject_list.html'
+ fields = ('gbobjects', 'template_to_render')
+ filter_horizontal = ['gbobjects']
+ text_enabled = True
+
+ def render(self, context, instance, placeholder):
+ """Update the context with plugin's data"""
+ context.update({'gbobjects': instance.gbobjects.all(),
+ 'object': instance,
+ 'placeholder': placeholder})
+ return context
+
+ def icon_src(self, instance):
+ """Icon source of the plugin"""
+ return settings.STATIC_URL + u'objectapp/img/plugin.png'
+
+
+class CMSRandomGbobjectsPlugin(CMSPluginBase):
+ """Django-cms plugin for random gbobjects"""
+ module = _('gbobjects')
+ model = RandomGbobjectsPlugin
+ name = _('Random gbobjects')
+ render_template = 'objectapp/cms/random_gbobjects.html'
+ fields = ('number_of_gbobjects', 'template_to_render')
+ text_enabled = True
+
+ def render(self, context, instance, placeholder):
+ """Update the context with plugin's data"""
+ context.update(
+ {'number_of_gbobjects': instance.number_of_gbobjects,
+ 'template_to_render': str(instance.template_to_render) or
+ 'objectapp/tags/random_gbobjects.html'})
+ return context
+
+ def icon_src(self, instance):
+ """Icon source of the plugin"""
+ return settings.STATIC_URL + u'objectapp/img/plugin.png'
+
+plugin_pool.register_plugin(CMSLatestGbobjectsPlugin)
+plugin_pool.register_plugin(CMSSelectedGbobjectsPlugin)
+plugin_pool.register_plugin(CMSRandomGbobjectsPlugin)
diff --git a/objectapp/plugins/menu.py b/objectapp/plugins/menu.py
new file mode 100644
index 0000000..1f9871e
--- /dev/null
+++ b/objectapp/plugins/menu.py
@@ -0,0 +1,180 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Menus for objectapp.plugins"""
+from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from menus.base import Modifier
+from menus.base import NavigationNode
+from menus.menu_pool import menu_pool
+from cms.menu_bases import CMSAttachMenu
+
+from objectapp.models import Gbobject
+from objectapp.models import Author
+from objectapp.models import Objecttype
+from objectapp.managers import tags_published
+from objectapp.plugins.settings import HIDE_GBOBJECT_MENU
+
+
+class GbobjectMenu(CMSAttachMenu):
+ """Menu for the gbobjects organized by archives dates"""
+ name = _('Objectapp Gbobject Menu')
+
+ def get_nodes(self, request):
+ """Return menu's node for gbobjects"""
+ nodes = []
+ archives = []
+ attributes = {'hidden': HIDE_GBOBJECT_MENU}
+ for gbobject in Gbobject.published.all():
+ year = gbobject.creation_date.strftime('%Y')
+ month = gbobject.creation_date.strftime('%m')
+ month_text = gbobject.creation_date.strftime('%b')
+ day = gbobject.creation_date.strftime('%d')
+
+ key_archive_year = 'year-%s' % year
+ key_archive_month = 'month-%s-%s' % (year, month)
+ key_archive_day = 'day-%s-%s-%s' % (year, month, day)
+
+ if not key_archive_year in archives:
+ nodes.append(NavigationNode(
+ year, reverse('objectapp_gbobject_archive_year', args=[year]),
+ key_archive_year, attr=attributes))
+ archives.append(key_archive_year)
+
+ if not key_archive_month in archives:
+ nodes.append(NavigationNode(
+ month_text,
+ reverse('objectapp_gbobject_archive_month', args=[year, month]),
+ key_archive_month, key_archive_year,
+ attr=attributes))
+ archives.append(key_archive_month)
+
+ if not key_archive_day in archives:
+ nodes.append(NavigationNode(
+ day, reverse('objectapp_gbobject_archive_day',
+ args=[year, month, day]),
+ key_archive_day, key_archive_month,
+ attr=attributes))
+ archives.append(key_archive_day)
+
+ nodes.append(NavigationNode(gbobject.title, gbobject.get_absolute_url(),
+ gbobject.pk, key_archive_day))
+ return nodes
+
+
+class ObjecttypeMenu(CMSAttachMenu):
+ """Menu for the objecttypes"""
+ name = _('Objectapp Objecttype Menu')
+
+ def get_nodes(self, request):
+ """Return menu's node for objecttypes"""
+ nodes = []
+ nodes.append(NavigationNode(_('Objecttypes'),
+ reverse('objectapp_Objecttype_list'),
+ 'objecttypes'))
+ for Objecttype in Objecttype.objects.all():
+ nodes.append(NavigationNode(Objecttype.title,
+ Objecttype.get_absolute_url(),
+ Objecttype.pk, 'objecttypes'))
+ return nodes
+
+
+class AuthorMenu(CMSAttachMenu):
+ """Menu for the authors"""
+ name = _('Objectapp Author Menu')
+
+ def get_nodes(self, request):
+ """Return menu's node for authors"""
+ nodes = []
+ nodes.append(NavigationNode(_('Authors'),
+ reverse('objectapp_author_list'),
+ 'authors'))
+ for author in Author.published.all():
+ nodes.append(NavigationNode(author.username,
+ reverse('objectapp_author_detail',
+ args=[author.username]),
+ author.pk, 'authors'))
+ return nodes
+
+
+class TagMenu(CMSAttachMenu):
+ """Menu for the tags"""
+ name = _('Objectapp Tag Menu')
+
+ def get_nodes(self, request):
+ """Return menu's node for tags"""
+ nodes = []
+ nodes.append(NavigationNode(_('Tags'), reverse('objectapp_tag_list'),
+ 'tags'))
+ for tag in tags_published():
+ nodes.append(NavigationNode(tag.name,
+ reverse('objectapp_tag_detail',
+ args=[tag.name]),
+ tag.pk, 'tags'))
+ return nodes
+
+
+class GbobjectModifier(Modifier):
+ """Menu Modifier for gbobjects,
+ hide the MenuGbobject in navigation, not in breadcrumbs"""
+
+ def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
+ """Modify nodes of a menu"""
+ if breadcrumb:
+ return nodes
+ for node in nodes:
+ if node.attr.get('hidden'):
+ nodes.remove(node)
+ return nodes
+
+
+menu_pool.register_menu(GbobjectMenu)
+menu_pool.register_menu(ObjecttypeMenu)
+menu_pool.register_menu(AuthorMenu)
+menu_pool.register_menu(TagMenu)
+menu_pool.register_modifier(GbobjectModifier)
diff --git a/objectapp/plugins/migrations/0001_initial.py b/objectapp/plugins/migrations/0001_initial.py
new file mode 100644
index 0000000..893309e
--- /dev/null
+++ b/objectapp/plugins/migrations/0001_initial.py
@@ -0,0 +1,151 @@
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'LatestGbobjectsPlugin'
+ db.create_table('cmsplugin_latestgbobjectsplugin', (
+ ('Objecttype', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['objectapp.Objecttype'], null=True, blank=True)),
+ ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
+ ('number_of_gbobjects', self.gf('django.db.models.fields.IntegerField')(default=5)),
+ ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+ ))
+ db.send_create_signal('plugins', ['LatestGbobjectsPlugin'])
+
+ # Adding model 'SelectedGbobjectsPlugin'
+ db.create_table('cmsplugin_selectedgbobjectsplugin', (
+ ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
+ ))
+ db.send_create_signal('plugins', ['SelectedGbobjectsPlugin'])
+
+ # Adding M2M table for field gbobjects on 'SelectedGbobjectsPlugin'
+ db.create_table('plugins_selectedgbobjectsplugin_gbobjects', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('selectedgbobjectsplugin', models.ForeignKey(orm['plugins.selectedgbobjectsplugin'], null=False)),
+ ('gbobject', models.ForeignKey(orm['objectapp.gbobject'], null=False))
+ ))
+ db.create_unique('plugins_selectedgbobjectsplugin_gbobjects', ['selectedgbobjectsplugin_id', 'gbobject_id'])
+
+ def backwards(self, orm):
+
+ # Deleting model 'LatestGbobjectsPlugin'
+ db.delete_table('cmsplugin_latestgbobjectsplugin')
+
+ # Deleting model 'SelectedGbobjectsPlugin'
+ db.delete_table('cmsplugin_selectedgbobjectsplugin')
+
+ # Removing M2M table for field gbobjects on 'SelectedGbobjectsPlugin'
+ db.delete_table('plugins_selectedgbobjectsplugin_gbobjects')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'cms.cmsplugin': {
+ 'Meta': {'object_name': 'CMSPlugin'},
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.CharField', [], {'max_length': '5', 'db_index': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
+ 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
+ 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+ 'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.CMSPlugin']"}),
+ 'publisher_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'cms.placeholder': {
+ 'Meta': {'object_name': 'Placeholder'},
+ 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'plugins.latestgbobjectsplugin': {
+ 'Meta': {'object_name': 'LatestGbobjectsPlugin', 'db_table': "'cmsplugin_latestgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'Objecttype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objectapp.Objecttype']", 'null': 'True', 'blank': 'True'}),
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'number_of_gbobjects': ('django.db.models.fields.IntegerField', [], {'default': '5'})
+ },
+ 'plugins.selectedgbobjectsplugin': {
+ 'Meta': {'object_name': 'SelectedGbobjectsPlugin', 'db_table': "'cmsplugin_selectedgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'gbobjects': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Gbobject']", 'symmetrical': 'False'})
+ },
+ 'sites.site': {
+ 'Meta': {'object_name': 'Site', 'db_table': "'django_site'"},
+ 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'objectapp.Objecttype': {
+ 'Meta': {'object_name': 'Objecttype'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'objectapp.gbobject': {
+ 'Meta': {'object_name': 'Gbobject'},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Objecttype']", 'symmetrical': 'False'}),
+ 'comment_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'end_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2042, 3, 15, 0, 0)'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'related_rel_+'", 'null': 'True', 'to': "orm['objectapp.Gbobject']"}),
+ 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'start_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'tags': ('tagging.fields.TagField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['plugins']
diff --git a/objectapp/plugins/migrations/0002_auto__add_field_latestgbobjectsplugin_template_to_render__add_field_sele.py b/objectapp/plugins/migrations/0002_auto__add_field_latestgbobjectsplugin_template_to_render__add_field_sele.py
new file mode 100644
index 0000000..f1a52a2
--- /dev/null
+++ b/objectapp/plugins/migrations/0002_auto__add_field_latestgbobjectsplugin_template_to_render__add_field_sele.py
@@ -0,0 +1,132 @@
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'LatestGbobjectsPlugin.template_to_render'
+ db.add_column('cmsplugin_latestgbobjectsplugin', 'template_to_render', self.gf('django.db.models.fields.CharField')(default='objectapp/cms/gbobject_list.html', max_length=250, blank=True), keep_default=False)
+
+ # Adding field 'SelectedGbobjectsPlugin.template_to_render'
+ db.add_column('cmsplugin_selectedgbobjectsplugin', 'template_to_render', self.gf('django.db.models.fields.CharField')(default='objectapp/cms/gbobject_list.html', max_length=250, blank=True), keep_default=False)
+
+ def backwards(self, orm):
+
+ # Deleting field 'LatestGbobjectsPlugin.template_to_render'
+ db.delete_column('cmsplugin_latestgbobjectsplugin', 'template_to_render')
+
+ # Deleting field 'SelectedGbobjectsPlugin.template_to_render'
+ db.delete_column('cmsplugin_selectedgbobjectsplugin', 'template_to_render')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'cms.cmsplugin': {
+ 'Meta': {'object_name': 'CMSPlugin'},
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.CharField', [], {'max_length': '5', 'db_index': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
+ 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
+ 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+ 'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.CMSPlugin']"}),
+ 'publisher_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'cms.placeholder': {
+ 'Meta': {'object_name': 'Placeholder'},
+ 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'plugins.latestgbobjectsplugin': {
+ 'Meta': {'object_name': 'LatestGbobjectsPlugin', 'db_table': "'cmsplugin_latestgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'Objecttype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objectapp.Objecttype']", 'null': 'True', 'blank': 'True'}),
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'number_of_gbobjects': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'plugins.selectedgbobjectsplugin': {
+ 'Meta': {'object_name': 'SelectedGbobjectsPlugin', 'db_table': "'cmsplugin_selectedgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'gbobjects': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Gbobject']", 'symmetrical': 'False'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'sites.site': {
+ 'Meta': {'object_name': 'Site', 'db_table': "'django_site'"},
+ 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'objectapp.Objecttype': {
+ 'Meta': {'object_name': 'Objecttype'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'objectapp.gbobject': {
+ 'Meta': {'object_name': 'Gbobject'},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Objecttype']", 'symmetrical': 'False'}),
+ 'comment_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'end_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2042, 3, 15, 0, 0)'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'related_rel_+'", 'null': 'True', 'to': "orm['objectapp.Gbobject']"}),
+ 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'start_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'tags': ('tagging.fields.TagField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['plugins']
diff --git a/objectapp/plugins/migrations/0003_auto__del_field_latestgbobjectsplugin_objecttype__del_field_latestgbobjectsp.py b/objectapp/plugins/migrations/0003_auto__del_field_latestgbobjectsplugin_objecttype__del_field_latestgbobjectsp.py
new file mode 100644
index 0000000..4aefa95
--- /dev/null
+++ b/objectapp/plugins/migrations/0003_auto__del_field_latestgbobjectsplugin_objecttype__del_field_latestgbobjectsp.py
@@ -0,0 +1,172 @@
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Deleting field 'LatestGbobjectsPlugin.Objecttype'
+ db.delete_column('cmsplugin_latestgbobjectsplugin', 'Objecttype_id')
+
+ # Deleting field 'LatestGbobjectsPlugin.author'
+ db.delete_column('cmsplugin_latestgbobjectsplugin', 'author_id')
+
+ # Adding M2M table for field tags on 'LatestGbobjectsPlugin'
+ db.create_table('plugins_latestgbobjectsplugin_tags', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('latestgbobjectsplugin', models.ForeignKey(orm['plugins.latestgbobjectsplugin'], null=False)),
+ ('tag', models.ForeignKey(orm['tagging.tag'], null=False))
+ ))
+ db.create_unique('plugins_latestgbobjectsplugin_tags', ['latestgbobjectsplugin_id', 'tag_id'])
+
+ # Adding M2M table for field objecttypes on 'LatestGbobjectsPlugin'
+ db.create_table('plugins_latestgbobjectsplugin_objecttypes', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('latestgbobjectsplugin', models.ForeignKey(orm['plugins.latestgbobjectsplugin'], null=False)),
+ ('Objecttype', models.ForeignKey(orm['objectapp.Objecttype'], null=False))
+ ))
+ db.create_unique('plugins_latestgbobjectsplugin_objecttypes', ['latestgbobjectsplugin_id', 'Objecttype_id'])
+
+ # Adding M2M table for field authors on 'LatestGbobjectsPlugin'
+ db.create_table('plugins_latestgbobjectsplugin_authors', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('latestgbobjectsplugin', models.ForeignKey(orm['plugins.latestgbobjectsplugin'], null=False)),
+ ('user', models.ForeignKey(orm['auth.user'], null=False))
+ ))
+ db.create_unique('plugins_latestgbobjectsplugin_authors', ['latestgbobjectsplugin_id', 'user_id'])
+
+ def backwards(self, orm):
+
+ # Adding field 'LatestGbobjectsPlugin.Objecttype'
+ db.add_column('cmsplugin_latestgbobjectsplugin', 'Objecttype', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['objectapp.Objecttype'], null=True, blank=True), keep_default=False)
+
+ # Adding field 'LatestGbobjectsPlugin.author'
+ db.add_column('cmsplugin_latestgbobjectsplugin', 'author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True), keep_default=False)
+
+ # Removing M2M table for field tags on 'LatestGbobjectsPlugin'
+ db.delete_table('plugins_latestgbobjectsplugin_tags')
+
+ # Removing M2M table for field objecttypes on 'LatestGbobjectsPlugin'
+ db.delete_table('plugins_latestgbobjectsplugin_objecttypes')
+
+ # Removing M2M table for field authors on 'LatestGbobjectsPlugin'
+ db.delete_table('plugins_latestgbobjectsplugin_authors')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'cms.cmsplugin': {
+ 'Meta': {'object_name': 'CMSPlugin'},
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.CharField', [], {'max_length': '5', 'db_index': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
+ 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
+ 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+ 'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.CMSPlugin']"}),
+ 'publisher_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'cms.placeholder': {
+ 'Meta': {'object_name': 'Placeholder'},
+ 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'plugins.latestgbobjectsplugin': {
+ 'Meta': {'object_name': 'LatestGbobjectsPlugin', 'db_table': "'cmsplugin_latestgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['objectapp.Objecttype']", 'null': 'True', 'blank': 'True'}),
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'number_of_gbobjects': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['tagging.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'plugins.selectedgbobjectsplugin': {
+ 'Meta': {'object_name': 'SelectedGbobjectsPlugin', 'db_table': "'cmsplugin_selectedgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'gbobjects': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Gbobject']", 'symmetrical': 'False'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'sites.site': {
+ 'Meta': {'object_name': 'Site', 'db_table': "'django_site'"},
+ 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'tagging.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'objectapp.Objecttype': {
+ 'Meta': {'object_name': 'Objecttype'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'objectapp.gbobject': {
+ 'Meta': {'object_name': 'Gbobject'},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Objecttype']", 'symmetrical': 'False'}),
+ 'comment_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'end_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2042, 3, 15, 0, 0)'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'related_rel_+'", 'null': 'True', 'to': "orm['objectapp.Gbobject']"}),
+ 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'start_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'tags': ('tagging.fields.TagField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['plugins']
diff --git a/objectapp/plugins/migrations/0004_auto__add_field_latestgbobjectsplugin_subobjecttypes.py b/objectapp/plugins/migrations/0004_auto__add_field_latestgbobjectsplugin_subobjecttypes.py
new file mode 100644
index 0000000..448e480
--- /dev/null
+++ b/objectapp/plugins/migrations/0004_auto__add_field_latestgbobjectsplugin_subobjecttypes.py
@@ -0,0 +1,142 @@
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'LatestGbobjectsPlugin.subobjecttypes'
+ db.add_column('cmsplugin_latestgbobjectsplugin', 'subobjecttypes', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False)
+
+ def backwards(self, orm):
+
+ # Deleting field 'LatestGbobjectsPlugin.subobjecttypes'
+ db.delete_column('cmsplugin_latestgbobjectsplugin', 'subobjecttypes')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'cms.cmsplugin': {
+ 'Meta': {'object_name': 'CMSPlugin'},
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
+ 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
+ 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.CMSPlugin']"}),
+ 'publisher_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'cms.placeholder': {
+ 'Meta': {'object_name': 'Placeholder'},
+ 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'plugins.latestgbobjectsplugin': {
+ 'Meta': {'object_name': 'LatestGbobjectsPlugin', 'db_table': "'cmsplugin_latestgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['objectapp.Objecttype']", 'null': 'True', 'blank': 'True'}),
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'number_of_gbobjects': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
+ 'subobjecttypes': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['tagging.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'plugins.selectedgbobjectsplugin': {
+ 'Meta': {'object_name': 'SelectedGbobjectsPlugin', 'db_table': "'cmsplugin_selectedgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'gbobjects': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Gbobject']", 'symmetrical': 'False'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'sites.site': {
+ 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
+ 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'tagging.tag': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'objectapp.Objecttype': {
+ 'Meta': {'ordering': "['title']", 'object_name': 'Objecttype'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['objectapp.Objecttype']"}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'objectapp.gbobject': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Gbobject'},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Objecttype']", 'symmetrical': 'False'}),
+ 'comment_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'end_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2042, 3, 15, 0, 0)'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'pingback_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'related_rel_+'", 'null': 'True', 'to': "orm['objectapp.Gbobject']"}),
+ 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'start_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'tags': ('tagging.fields.TagField', [], {}),
+ 'template': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['plugins']
diff --git a/objectapp/plugins/migrations/0005_auto__add_randomgbobjectsplugin.py b/objectapp/plugins/migrations/0005_auto__add_randomgbobjectsplugin.py
new file mode 100644
index 0000000..baa01a8
--- /dev/null
+++ b/objectapp/plugins/migrations/0005_auto__add_randomgbobjectsplugin.py
@@ -0,0 +1,151 @@
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'RandomGbobjectsPlugin'
+ db.create_table('cmsplugin_randomgbobjectsplugin', (
+ ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
+ ('number_of_gbobjects', self.gf('django.db.models.fields.IntegerField')(default=5)),
+ ('template_to_render', self.gf('django.db.models.fields.CharField')(max_length=250, blank=True)),
+ ))
+ db.send_create_signal('plugins', ['RandomGbobjectsPlugin'])
+
+ def backwards(self, orm):
+
+ # Deleting model 'RandomGbobjectsPlugin'
+ db.delete_table('cmsplugin_randomgbobjectsplugin')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'cms.cmsplugin': {
+ 'Meta': {'object_name': 'CMSPlugin'},
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
+ 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
+ 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'cms.placeholder': {
+ 'Meta': {'object_name': 'Placeholder'},
+ 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'plugins.latestgbobjectsplugin': {
+ 'Meta': {'object_name': 'LatestGbobjectsPlugin', 'db_table': "'cmsplugin_latestgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['objectapp.Objecttype']", 'null': 'True', 'blank': 'True'}),
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'number_of_gbobjects': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
+ 'subobjecttypes': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['tagging.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'plugins.randomgbobjectsplugin': {
+ 'Meta': {'object_name': 'RandomGbobjectsPlugin', 'db_table': "'cmsplugin_randomgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'number_of_gbobjects': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'plugins.selectedgbobjectsplugin': {
+ 'Meta': {'object_name': 'SelectedGbobjectsPlugin', 'db_table': "'cmsplugin_selectedgbobjectsplugin'", '_ormbases': ['cms.CMSPlugin']},
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'gbobjects': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['objectapp.Gbobject']", 'symmetrical': 'False'}),
+ 'template_to_render': ('django.db.models.fields.CharField', [], {'max_length': '250', 'blank': 'True'})
+ },
+ 'sites.site': {
+ 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
+ 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'tagging.tag': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'objectapp.Objecttype': {
+ 'Meta': {'ordering': "['title']", 'object_name': 'Objecttype'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['objectapp.Objecttype']"}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'objectapp.gbobject': {
+ 'Meta': {'ordering': "['-creation_date']", 'object_name': 'Gbobject'},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'objecttypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['objectapp.Objecttype']", 'null': 'True', 'blank': 'True'}),
+ 'comment_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'end_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2042, 3, 15, 0, 0)'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'pingback_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'related_rel_+'", 'null': 'True', 'to': "orm['objectapp.Gbobject']"}),
+ 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'start_publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'tags': ('tagging.fields.TagField', [], {}),
+ 'template': ('django.db.models.fields.CharField', [], {'default': "'objectapp/gbobject_detail.html'", 'max_length': '250'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['plugins']
diff --git a/objectapp/plugins/migrations/__init__.py b/objectapp/plugins/migrations/__init__.py
new file mode 100644
index 0000000..66f67de
--- /dev/null
+++ b/objectapp/plugins/migrations/__init__.py
@@ -0,0 +1 @@
+"""Migrations for Objectapp Plugins"""
diff --git a/objectapp/plugins/models.py b/objectapp/plugins/models.py
new file mode 100644
index 0000000..8e991a1
--- /dev/null
+++ b/objectapp/plugins/models.py
@@ -0,0 +1,152 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Models of Objectapp CMS Plugins"""
+from django.db import models
+from django.contrib.auth.models import User
+from django.db.models.signals import post_save
+from django.db.models.signals import post_delete
+from django.utils.translation import ugettext_lazy as _
+
+from tagging.models import Tag
+from cms.models import CMSPlugin
+from menus.menu_pool import menu_pool
+
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.plugins.settings import PLUGINS_TEMPLATES
+
+TEMPLATES = [('objectapp/cms/gbobject_list.html', _('Gbobject list (default)')),
+ ('objectapp/cms/gbobject_detail.html', _('Gbobject detailed'))] + \
+ PLUGINS_TEMPLATES
+
+
+class LatestGbobjectsPlugin(CMSPlugin):
+ """CMS Plugin for displaying latest gbobjects"""
+
+ objecttypes = models.ManyToManyField(
+ Objecttype, verbose_name=_('objecttypes'),
+ blank=True, null=True)
+ subobjecttypes = models.BooleanField(
+ default=True, verbose_name=_('include subobjecttypes'))
+ authors = models.ManyToManyField(
+ User, verbose_name=_('authors'), blank=True, null=True)
+ tags = models.ManyToManyField(
+ Tag, verbose_name=_('tags'), blank=True, null=True)
+
+ number_of_gbobjects = models.IntegerField(
+ _('number of gbobjects'), default=5)
+ template_to_render = models.CharField(
+ _('template'), blank=True,
+ max_length=250, choices=TEMPLATES,
+ help_text=_('Template used to display the plugin'))
+
+ @property
+ def render_template(self):
+ """Override render_template to use
+ the template_to_render attribute"""
+ return self.template_to_render
+
+ def copy_relations(self, old_instance):
+ """Duplicate ManyToMany relations on plugin copy"""
+ self.tags = old_instance.tags.all()
+ self.authors = old_instance.authors.all()
+ self.objecttypes = old_instance.objecttypes.all()
+
+ def __unicode__(self):
+ return _('%s gbobjects') % self.number_of_gbobjects
+
+
+class SelectedGbobjectsPlugin(CMSPlugin):
+ """CMS Plugin for displaying custom gbobjects"""
+
+ gbobjects = models.ManyToManyField(
+ Gbobject, verbose_name=_('gbobjects'))
+ template_to_render = models.CharField(
+ _('template'), blank=True,
+ max_length=250, choices=TEMPLATES,
+ help_text=_('Template used to display the plugin'))
+
+ @property
+ def render_template(self):
+ """Override render_template to use
+ the template_to_render attribute"""
+ return self.template_to_render
+
+ def copy_relations(self, old_instance):
+ """Duplicate ManyToMany relations on plugin copy"""
+ self.gbobjects = old_instance.gbobjects.all()
+
+ def __unicode__(self):
+ return _('%s gbobjects') % self.gbobjects.count()
+
+
+class RandomGbobjectsPlugin(CMSPlugin):
+ """CMS Plugin for displaying random gbobjects"""
+
+ number_of_gbobjects = models.IntegerField(
+ _('number of gbobjects'), default=5)
+ template_to_render = models.CharField(
+ _('template'), blank=True,
+ max_length=250, choices=TEMPLATES,
+ help_text=_('Template used to display the plugin'))
+
+ def __unicode__(self):
+ return _('%s gbobjects') % self.number_of_gbobjects
+
+
+def invalidate_menu_cache(sender, **kwargs):
+ """Signal receiver to invalidate the menu_pool
+ cache when an gbobject is posted"""
+ menu_pool.clear()
+
+post_save.connect(
+ invalidate_menu_cache, sender=Gbobject,
+ dispatch_uid='objectapp.gbobject.postsave.invalidate_menu_cache')
+post_delete.connect(
+ invalidate_menu_cache, sender=Gbobject,
+ dispatch_uid='objectapp.gbobject.postdelete.invalidate_menu_cache')
diff --git a/objectapp/plugins/placeholder.py b/objectapp/plugins/placeholder.py
new file mode 100644
index 0000000..f2f8973
--- /dev/null
+++ b/objectapp/plugins/placeholder.py
@@ -0,0 +1,61 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Placeholder model for Objectapp"""
+from cms.models.fields import PlaceholderField
+
+from objectapp.models import GbobjectAbstractClass
+
+
+class GbobjectPlaceholder(GbobjectAbstractClass):
+ """Gbobject with a Placeholder to edit content"""
+
+ content_placeholder = PlaceholderField('content')
+
+ class Meta:
+ """GbobjectPlaceholder's Meta"""
+ abstract = True
diff --git a/objectapp/plugins/settings.py b/objectapp/plugins/settings.py
new file mode 100644
index 0000000..e679e3e
--- /dev/null
+++ b/objectapp/plugins/settings.py
@@ -0,0 +1,74 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Settings of Objectapp.plugins"""
+import warnings
+
+from django.conf import settings
+from django.utils.importlib import import_module
+
+
+HIDE_GBOBJECT_MENU = getattr(settings, 'OBJECTAPP_HIDE_GBOBJECT_MENU', True)
+
+PLUGINS_TEMPLATES = getattr(settings, 'OBJECTAPP_PLUGINS_TEMPLATES', [])
+
+
+APP_MENUS = []
+DEFAULT_APP_MENUS = ['objectapp.plugins.menu.GbobjectMenu',
+ 'objectapp.plugins.menu.ObjecttypeMenu',
+ 'objectapp.plugins.menu.TagMenu',
+ 'objectapp.plugins.menu.AuthorMenu']
+
+for menu_string in getattr(settings, 'OBJECTAPP_APP_MENUS', DEFAULT_APP_MENUS):
+ try:
+ dot = menu_string.rindex('.')
+ menu_module = menu_string[:dot]
+ menu_name = menu_string[dot + 1:]
+ APP_MENUS.append(getattr(import_module(menu_module), menu_name))
+ except (ImportError, AttributeError):
+ warnings.warn('%s menu cannot be imported' % menu_string,
+ RuntimeWarning)
diff --git a/objectapp/search.py b/objectapp/search.py
new file mode 100644
index 0000000..1540299
--- /dev/null
+++ b/objectapp/search.py
@@ -0,0 +1,180 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Search module with complex query parsing for Objectapp"""
+from pyparsing import Word
+from pyparsing import alphas
+from pyparsing import WordEnd
+from pyparsing import Combine
+from pyparsing import opAssoc
+from pyparsing import Optional
+from pyparsing import OneOrMore
+from pyparsing import StringEnd
+from pyparsing import printables
+from pyparsing import quotedString
+from pyparsing import removeQuotes
+from pyparsing import ParseResults
+from pyparsing import CaselessLiteral
+from pyparsing import operatorPrecedence
+
+from django.db.models import Q
+
+from objectapp.models import Gbobject
+from objectapp.settings import STOP_WORDS
+
+
+def createQ(token):
+ """Creates the Q() object"""
+ meta = getattr(token, 'meta', None)
+ query = getattr(token, 'query', '')
+ wildcards = None
+
+ if isinstance(query, basestring): # Unicode -> Quoted string
+ search = query
+ else: # List -> No quoted string (possible wildcards)
+ if len(query) == 1:
+ search = query[0]
+ elif len(query) == 3:
+ wildcards = 'BOTH'
+ search = query[1]
+ elif len(query) == 2:
+ if query[0] == '*':
+ wildcards = 'START'
+ search = query[1]
+ else:
+ wildcards = 'END'
+ search = query[0]
+
+ # Ignore connective words (of, a, an...) and STOP_WORDS
+ if (len(search) < 3 and not search.isdigit()) or \
+ search in STOP_WORDS:
+ return Q()
+
+ if not meta:
+ return Q(content__icontains=search) | \
+ Q(excerpt__icontains=search) | \
+ Q(title__icontains=search)
+
+ if meta == 'Objecttype':
+ if wildcards == 'BOTH':
+ return Q(objecttypes__title__icontains=search) | \
+ Q(objecttypes__slug__icontains=search)
+ elif wildcards == 'START':
+ return Q(objecttypes__title__iendswith=search) | \
+ Q(objecttypes__slug__iendswith=search)
+ elif wildcards == 'END':
+ return Q(objecttypes__title__istartswith=search) | \
+ Q(objecttypes__slug__istartswith=search)
+ else:
+ return Q(objecttypes__title__iexact=search) | \
+ Q(objecttypes__slug__iexact=search)
+ elif meta == 'author':
+ if wildcards == 'BOTH':
+ return Q(authors__username__icontains=search)
+ elif wildcards == 'START':
+ return Q(authors__username__iendswith=search)
+ elif wildcards == 'END':
+ return Q(authors__username__istartswith=search)
+ else:
+ return Q(authors__username__iexact=search)
+ elif meta == 'tag': # TODO: tags ignore wildcards
+ return Q(tags__icontains=search)
+
+
+def unionQ(token):
+ """Appends all the Q() objects"""
+ query = Q()
+ operation = 'and'
+ negation = False
+
+ for t in token:
+ if type(t) is ParseResults: # See tokens recursively
+ query &= unionQ(t)
+ else:
+ if t in ('or', 'and'): # Set the new op and go to next token
+ operation = t
+ elif t == '-': # Next tokens needs to be negated
+ negation = True
+ else: # Append to query the token
+ if negation:
+ t = ~t
+ if operation == 'or':
+ query |= t
+ else:
+ query &= t
+ return query
+
+
+NO_BRTS = printables.replace('(', '').replace(')', '')
+SINGLE = Word(NO_BRTS.replace('*', ''))
+WILDCARDS = Optional('*') + SINGLE + Optional('*') + WordEnd(wordChars=NO_BRTS)
+QUOTED = quotedString.setParseAction(removeQuotes)
+
+OPER_AND = CaselessLiteral('and')
+OPER_OR = CaselessLiteral('or')
+OPER_NOT = '-'
+
+TERM = Combine(Optional(Word(alphas).setResultsName('meta') + ':') +
+ (QUOTED.setResultsName('query') |
+ WILDCARDS.setResultsName('query')))
+TERM.setParseAction(createQ)
+
+EXPRESSION = operatorPrecedence(TERM, [
+ (OPER_NOT, 1, opAssoc.RIGHT),
+ (OPER_OR, 2, opAssoc.LEFT),
+ (Optional(OPER_AND, default='and'), 2, opAssoc.LEFT)])
+EXPRESSION.setParseAction(unionQ)
+
+QUERY = OneOrMore(EXPRESSION) + StringEnd()
+QUERY.setParseAction(unionQ)
+
+
+def advanced_search(pattern):
+ """Parse the grammar of a pattern
+ and build a queryset with it"""
+ query_parsed = QUERY.parseString(pattern)
+ return Gbobject.published.filter(query_parsed[0]).distinct()
diff --git a/objectapp/settings.py b/objectapp/settings.py
new file mode 100644
index 0000000..885a870
--- /dev/null
+++ b/objectapp/settings.py
@@ -0,0 +1,135 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Settings of Objectapp"""
+from django.conf import settings
+
+PING_DIRECTORIES = getattr(settings, 'OBJECTAPP_PING_DIRECTORIES',
+ ('http://django-blog-objectapp.com/xmlrpc/',))
+SAVE_PING_DIRECTORIES = getattr(settings, 'OBJECTAPP_SAVE_PING_DIRECTORIES',
+ bool(PING_DIRECTORIES))
+SAVE_PING_EXTERNAL_URLS = getattr(settings, 'OBJECTAPP_PING_EXTERNAL_URLS', True)
+
+COPYRIGHT = getattr(settings, 'OBJECTAPP_COPYRIGHT', 'Objectapp')
+
+PAGINATION = getattr(settings, 'OBJECTAPP_PAGINATION', 10)
+ALLOW_EMPTY = getattr(settings, 'OBJECTAPP_ALLOW_EMPTY', True)
+ALLOW_FUTURE = getattr(settings, 'OBJECTAPP_ALLOW_FUTURE', True)
+
+GBOBJECT_TEMPLATES = getattr(settings, 'OBJECTAPP_GBOBJECT_TEMPLATES', [])
+GBOBJECT_BASE_MODEL = getattr(settings, 'OBJECTAPP_GBOBJECT_BASE_MODEL', '')
+
+MARKUP_LANGUAGE = getattr(settings, 'OBJECTAPP_MARKUP_LANGUAGE', 'html')
+
+MARKDOWN_EXTENSIONS = getattr(settings, 'OBJECTAPP_MARKDOWN_EXTENSIONS', '')
+
+WYSIWYG_MARKUP_MAPPING = {
+ 'textile': 'markitup',
+ 'markdown': 'markitup',
+ 'restructuredtext': 'markitup',
+ 'html': 'tinymce' in settings.INSTALLED_APPS and 'tinymce' or 'wymeditor'}
+
+WYSIWYG = getattr(settings, 'OBJECTAPP_WYSIWYG',
+ WYSIWYG_MARKUP_MAPPING.get(MARKUP_LANGUAGE))
+
+AUTO_CLOSE_COMMENTS_AFTER = getattr(
+ settings, 'OBJECTAPP_AUTO_CLOSE_COMMENTS_AFTER', None)
+
+AUTO_MODERATE_COMMENTS = getattr(settings, 'OBJECTAPP_AUTO_MODERATE_COMMENTS',
+ False)
+
+MAIL_COMMENT_REPLY = getattr(settings, 'OBJECTAPP_MAIL_COMMENT_REPLY', False)
+
+MAIL_COMMENT_AUTHORS = getattr(settings, 'OBJECTAPP_MAIL_COMMENT_AUTHORS', True)
+
+MAIL_COMMENT_NOTIFICATION_RECIPIENTS = getattr(
+ settings, 'OBJECTAPP_MAIL_COMMENT_NOTIFICATION_RECIPIENTS',
+ [manager_tuple[1] for manager_tuple in settings.MANAGERS])
+
+UPLOAD_TO = getattr(settings, 'OBJECTAPP_UPLOAD_TO', 'uploads')
+
+PROTOCOL = getattr(settings, 'OBJECTAPP_PROTOCOL', 'http')
+
+FEEDS_FORMAT = getattr(settings, 'OBJECTAPP_FEEDS_FORMAT', 'rss')
+FEEDS_MAX_ITEMS = getattr(settings, 'OBJECTAPP_FEEDS_MAX_ITEMS', 15)
+
+PINGBACK_CONTENT_LENGTH = getattr(settings,
+ 'OBJECTAPP_PINGBACK_CONTENT_LENGTH', 300)
+
+F_MIN = getattr(settings, 'OBJECTAPP_F_MIN', 0.1)
+F_MAX = getattr(settings, 'OBJECTAPP_F_MAX', 1.0)
+
+SPAM_CHECKER_BACKENDS = getattr(settings, 'OBJECTAPP_SPAM_CHECKER_BACKENDS',
+ ())
+
+URL_SHORTENER_BACKEND = getattr(settings, 'OBJECTAPP_URL_SHORTENER_BACKEND',
+ 'objectapp.url_shortener.backends.default')
+
+STOP_WORDS = getattr(settings, 'OBJECTAPP_STOP_WORDS',
+ ('able', 'about', 'across', 'after', 'all', 'almost',
+ 'also', 'among', 'and', 'any', 'are', 'because', 'been',
+ 'but', 'can', 'cannot', 'could', 'dear', 'did', 'does',
+ 'either', 'else', 'ever', 'every', 'for', 'from', 'get',
+ 'got', 'had', 'has', 'have', 'her', 'hers', 'him', 'his',
+ 'how', 'however', 'into', 'its', 'just', 'least', 'let',
+ 'like', 'likely', 'may', 'might', 'most', 'must',
+ 'neither', 'nor', 'not', 'off', 'often', 'only', 'other',
+ 'our', 'own', 'rather', 'said', 'say', 'says', 'she',
+ 'should', 'since', 'some', 'than', 'that', 'the',
+ 'their', 'them', 'then', 'there', 'these', 'they',
+ 'this', 'tis', 'too', 'twas', 'wants', 'was', 'were',
+ 'what', 'when', 'where', 'which', 'while', 'who', 'whom',
+ 'why', 'will', 'with', 'would', 'yet', 'you', 'your'))
+
+TWITTER_CONSUMER_KEY = getattr(settings, 'TWITTER_CONSUMER_KEY', '')
+TWITTER_CONSUMER_SECRET = getattr(settings, 'TWITTER_CONSUMER_SECRET', '')
+TWITTER_ACCESS_KEY = getattr(settings, 'TWITTER_ACCESS_KEY', '')
+TWITTER_ACCESS_SECRET = getattr(settings, 'TWITTER_ACCESS_SECRET', '')
+
+USE_TWITTER = getattr(settings, 'OBJECTAPP_USE_TWITTER',
+ bool(TWITTER_ACCESS_KEY and TWITTER_ACCESS_SECRET and \
+ TWITTER_CONSUMER_KEY and TWITTER_CONSUMER_SECRET))
diff --git a/objectapp/signals.py b/objectapp/signals.py
new file mode 100644
index 0000000..ed3f81c
--- /dev/null
+++ b/objectapp/signals.py
@@ -0,0 +1,102 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Signal handlers of Objectapp"""
+import inspect
+from functools import wraps
+
+from django.db.models.signals import post_save
+
+from objectapp import settings
+
+
+def disable_for_loaddata(signal_handler):
+ """Decorator for disabling signals sent
+ by 'post_save' on loaddata command.
+ http://code.djangoproject.com/ticket/8399"""
+
+ @wraps(signal_handler)
+ def wrapper(*args, **kwargs):
+ for fr in inspect.stack():
+ if inspect.getmodulename(fr[1]) == 'loaddata':
+ return
+ signal_handler(*args, **kwargs)
+
+ return wrapper
+
+
+@disable_for_loaddata
+def ping_directories_handler(sender, **kwargs):
+ """Ping Directories when an gbobject is saved"""
+ gbobject = kwargs['instance']
+
+ if gbobject.is_visible and settings.SAVE_PING_DIRECTORIES:
+ from objectapp.ping import DirectoryPinger
+
+ for directory in settings.PING_DIRECTORIES:
+ DirectoryPinger(directory, [gbobject])
+
+
+@disable_for_loaddata
+def ping_external_urls_handler(sender, **kwargs):
+ """Ping Externals URLS when an gbobject is saved"""
+ gbobject = kwargs['instance']
+
+ if gbobject.is_visible and settings.SAVE_PING_EXTERNAL_URLS:
+ from objectapp.ping import ExternalUrlsPinger
+
+ ExternalUrlsPinger(gbobject)
+
+
+def disconnect_objectapp_signals():
+ """Disconnect all the signals provided by Objectapp"""
+ from objectapp.models import Gbobject
+
+ post_save.disconnect(
+ sender=Gbobject, dispatch_uid='objectapp.gbobject.post_save.ping_directories')
+ post_save.disconnect(
+ sender=Gbobject, dispatch_uid='objectapp.gbobject.post_save.ping_external_urls')
diff --git a/objectapp/sitemaps.py b/objectapp/sitemaps.py
new file mode 100644
index 0000000..3696622
--- /dev/null
+++ b/objectapp/sitemaps.py
@@ -0,0 +1,183 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Sitemaps for Objectapp"""
+from django.contrib.sitemaps import Sitemap
+from django.core.urlresolvers import reverse
+
+from tagging.models import TaggedItem
+
+from objectapp.models import Gbobject
+from objectapp.models import Author
+from objectapp.models import Objecttype
+from objectapp.managers import tags_published
+
+
+class GbobjectSitemap(Sitemap):
+ """Sitemap for gbobjects"""
+ priority = 0.5
+ changefreq = 'weekly'
+
+ def items(self):
+ """Return published gbobjects"""
+ return Gbobject.published.all()
+
+ def lastmod(self, obj):
+ """Return last modification of an gbobject"""
+ return obj.last_update
+
+
+class ObjecttypeSitemap(Sitemap):
+ """Sitemap for objecttypes"""
+ changefreq = 'monthly'
+
+ def cache(self, objecttypes):
+ """Cache categorie's gbobjects percent on total gbobjects"""
+ len_gbobjects = float(Gbobject.published.count())
+ self.cache_objecttypes = {}
+ for cat in objecttypes:
+ if len_gbobjects:
+ self.cache_objecttypes[cat.pk] = cat.gbobjects_published(
+ ).count() / len_gbobjects
+ else:
+ self.cache_objecttypes[cat.pk] = 0.0
+
+ def items(self):
+ """Return all objecttypes with coeff"""
+ objecttypes = Objecttype.objects.all()
+ self.cache(objecttypes)
+ return objecttypes
+
+ def lastmod(self, obj):
+ """Return last modification of a Objecttype"""
+ gbobjects = obj.gbobjects_published()
+ if not gbobjects:
+ return None
+ return gbobjects[0].creation_date
+
+ def priority(self, obj):
+ """Compute priority with cached coeffs"""
+ priority = 0.5 + self.cache_objecttypes[obj.pk]
+ if priority > 1.0:
+ priority = 1.0
+ return '%.1f' % priority
+
+
+class AuthorSitemap(Sitemap):
+ """Sitemap for authors"""
+ priority = 0.5
+ changefreq = 'monthly'
+
+ def items(self):
+ """Return published authors"""
+ return Author.published.all()
+
+ def lastmod(self, obj):
+ """Return last modification of an author"""
+ gbobjects = obj.gbobjects_published()
+ if not gbobjects:
+ return None
+ return gbobjects[0].creation_date
+
+ def location(self, obj):
+ """Return url of an author"""
+ return reverse('objectapp_author_detail', args=[obj.username])
+
+
+class TagSitemap(Sitemap):
+ """Sitemap for tags"""
+ changefreq = 'monthly'
+
+ def cache(self, tags):
+ """Cache tag's gbobjects percent on total gbobjects"""
+ len_gbobjects = float(Gbobject.published.count())
+ self.cache_tags = {}
+ for tag in tags:
+ gbobjects = TaggedItem.objects.get_by_model(
+ Gbobject.published.all(), tag)
+ self.cache_tags[tag.pk] = (gbobjects, gbobjects.count() / len_gbobjects)
+
+ def items(self):
+ """Return all tags with coeff"""
+ tags = tags_published()
+ self.cache(tags)
+ return tags
+
+ def lastmod(self, obj):
+ """Return last modification of a tag"""
+ gbobjects = self.cache_tags[obj.pk][0]
+ return gbobjects[0].creation_date
+
+ def priority(self, obj):
+ """Compute priority with cached coeffs"""
+ priority = 0.5 + self.cache_tags[obj.pk][1]
+ if priority > 1.0:
+ priority = 1.0
+ return '%.1f' % priority
+
+ def location(self, obj):
+ """Return url of a tag"""
+ return reverse('objectapp_tag_detail', args=[obj.name])
diff --git a/objectapp/spam_checker/__init__.py b/objectapp/spam_checker/__init__.py
new file mode 100644
index 0000000..7d1da4f
--- /dev/null
+++ b/objectapp/spam_checker/__init__.py
@@ -0,0 +1,82 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Spam checker for Objectapp"""
+import warnings
+
+from django.utils.importlib import import_module
+from django.core.exceptions import ImproperlyConfigured
+
+from objectapp.settings import SPAM_CHECKER_BACKENDS
+
+
+def get_spam_checker(backend_path):
+ """Return the selected spam checker backend"""
+ try:
+ backend_module = import_module(backend_path)
+ backend = getattr(backend_module, 'backend')
+ except (ImportError, AttributeError):
+ warnings.warn('%s backend cannot be imported' % backend_path,
+ RuntimeWarning)
+ backend = None
+ except ImproperlyConfigured, e:
+ warnings.warn(str(e), RuntimeWarning)
+ backend = None
+
+ return backend
+
+
+def check_is_spam(content, content_object, request,
+ backends=SPAM_CHECKER_BACKENDS):
+ """Return True if the content is a spam, else False"""
+ for backend_path in backends:
+ spam_checker = get_spam_checker(backend_path)
+ is_spam = spam_checker(content, content_object, request)
+ if is_spam:
+ return True
+
+ return False
diff --git a/objectapp/spam_checker/backends/__init__.py b/objectapp/spam_checker/backends/__init__.py
new file mode 100644
index 0000000..83a6d7e
--- /dev/null
+++ b/objectapp/spam_checker/backends/__init__.py
@@ -0,0 +1 @@
+"""Shortlink backends for Objectapp"""
diff --git a/objectapp/spam_checker/backends/all_is_spam.py b/objectapp/spam_checker/backends/all_is_spam.py
new file mode 100644
index 0000000..4aba03c
--- /dev/null
+++ b/objectapp/spam_checker/backends/all_is_spam.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""All is spam, spam checker backend for Objectapp"""
+
+
+def backend(comment, content_object, request):
+ """Backend for setting all comments to spam"""
+ return True
diff --git a/objectapp/spam_checker/backends/automattic.py b/objectapp/spam_checker/backends/automattic.py
new file mode 100644
index 0000000..46796e0
--- /dev/null
+++ b/objectapp/spam_checker/backends/automattic.py
@@ -0,0 +1,106 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Akismet spam checker backend for Objectapp"""
+from django.conf import settings
+from django.utils.encoding import smart_str
+from django.contrib.sites.models import Site
+from django.core.exceptions import ImproperlyConfigured
+
+from objectapp.settings import PROTOCOL
+
+try:
+ from akismet import Akismet
+ from akismet import APIKeyError
+except ImportError:
+ raise ImproperlyConfigured('akismet module is not available')
+
+if not getattr(settings, 'AKISMET_SECRET_API_KEY', ''):
+ raise ImproperlyConfigured('You have to set AKISMET_SECRET_API_KEY')
+
+AKISMET_API_KEY = settings.AKISMET_SECRET_API_KEY
+
+
+def backend(comment, content_object, request):
+ """Akismet spam checker backend for Objectapp"""
+ blog_url = '%s://%s/' % (PROTOCOL, Site.objects.get_current().domain)
+
+ akismet = Akismet(key=AKISMET_API_KEY, blog_url=blog_url)
+
+ if not akismet.verify_key():
+ raise APIKeyError('Your Akismet API key is invalid.')
+
+ akismet_data = {
+ 'user_ip': request.META.get('REMOTE_ADDR', ''),
+ 'user_agent': request.META.get('HTTP_USER_AGENT', ''),
+ 'referrer': request.META.get('HTTP_REFERER', 'unknown'),
+ 'permalink': content_object.get_absolute_url(),
+ 'comment_type': 'comment',
+ 'comment_author': smart_str(comment.userinfo.get('name', '')),
+ 'comment_author_email': smart_str(comment.userinfo.get('email', '')),
+ 'comment_author_url': smart_str(comment.userinfo.get('url', '')),
+ }
+ is_spam = akismet.comment_check(smart_str(comment.comment),
+ data=akismet_data, build_data=True)
+ return is_spam
diff --git a/objectapp/spam_checker/backends/mollom.py b/objectapp/spam_checker/backends/mollom.py
new file mode 100644
index 0000000..2f3fece
--- /dev/null
+++ b/objectapp/spam_checker/backends/mollom.py
@@ -0,0 +1,106 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Mollom spam checker backend for Objectapp"""
+from django.conf import settings
+from django.utils.encoding import smart_str
+from django.core.exceptions import ImproperlyConfigured
+
+try:
+ from pymollom import MollomAPI
+ from pymollom import MollomFault
+except ImportError:
+ raise ImproperlyConfigured('pymollom module is not available')
+
+if not getattr(settings, 'MOLLOM_PUBLIC_KEY', ''):
+ raise ImproperlyConfigured('You have to set a MOLLOM_PUBLIC_KEY setting')
+
+if not getattr(settings, 'MOLLOM_PRIVATE_KEY', ''):
+ raise ImproperlyConfigured('You have to set a MOLLOM_PRIVATE_KEY setting')
+
+MOLLOM_PUBLIC_KEY = settings.MOLLOM_PUBLIC_KEY
+MOLLOM_PRIVATE_KEY = settings.MOLLOM_PRIVATE_KEY
+
+
+def backend(comment, content_object, request):
+ """Mollom spam checker backend for Objectapp"""
+ mollom_api = MollomAPI(
+ publicKey=MOLLOM_PUBLIC_KEY,
+ privateKey=MOLLOM_PRIVATE_KEY)
+ if not mollom_api.verifyKey():
+ raise MollomFault('Your MOLLOM credentials are invalid.')
+
+ mollom_data = {'authorIP': request.META.get('REMOTE_ADDR', ''),
+ 'authorName': smart_str(comment.userinfo.get('name', '')),
+ 'authorMail': smart_str(comment.userinfo.get('email', '')),
+ 'authorURL': smart_str(comment.userinfo.get('url', ''))}
+
+ cc = mollom_api.checkContent(
+ postTitle=smart_str(content_object.title),
+ postBody=smart_str(comment.comment), **mollom_data)
+ # cc['spam']: 1 for ham, 2 for spam, 3 for unsure;
+ # http://mollom.com/blog/spam-vs-ham
+ if cc['spam'] == 2:
+ return True
+ return False
diff --git a/objectapp/spam_checker/backends/typepad.py b/objectapp/spam_checker/backends/typepad.py
new file mode 100644
index 0000000..f5b3415
--- /dev/null
+++ b/objectapp/spam_checker/backends/typepad.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""TypePad spam checker backend for Objectapp"""
+from django.conf import settings
+from django.utils.encoding import smart_str
+from django.contrib.sites.models import Site
+from django.core.exceptions import ImproperlyConfigured
+
+from objectapp.settings import PROTOCOL
+
+try:
+ from akismet import Akismet
+ from akismet import APIKeyError
+except ImportError:
+ raise ImproperlyConfigured('akismet module is not available')
+
+if not getattr(settings, 'TYPEPAD_SECRET_API_KEY', ''):
+ raise ImproperlyConfigured('You have to set TYPEPAD_SECRET_API_KEY')
+
+TYPEPAD_API_KEY = settings.TYPEPAD_SECRET_API_KEY
+
+
+class TypePad(Akismet):
+ """TypePad version of the Akismet module"""
+ baseurl = 'api.antispam.typepad.com/1.1/'
+
+
+def backend(comment, content_object, request):
+ """TypePad spam checker backend for Objectapp"""
+ blog_url = '%s://%s/' % (PROTOCOL, Site.objects.get_current().domain)
+
+ typepad = TypePad(key=TYPEPAD_API_KEY, blog_url=blog_url)
+
+ if not typepad.verify_key():
+ raise APIKeyError('Your Typepad API key is invalid.')
+
+ typepad_data = {
+ 'user_ip': request.META.get('REMOTE_ADDR', ''),
+ 'user_agent': request.META.get('HTTP_USER_AGENT', ''),
+ 'referrer': request.META.get('HTTP_REFERER', 'unknown'),
+ 'permalink': content_object.get_absolute_url(),
+ 'comment_type': 'comment',
+ 'comment_author': smart_str(comment.userinfo.get('name', '')),
+ 'comment_author_email': smart_str(comment.userinfo.get('email', '')),
+ 'comment_author_url': smart_str(comment.userinfo.get('url', '')),
+ }
+ is_spam = typepad.comment_check(smart_str(comment.comment),
+ data=typepad_data, build_data=True)
+ return is_spam
diff --git a/objectapp/static/objectapp/css/config.rb b/objectapp/static/objectapp/css/config.rb
new file mode 100644
index 0000000..accf354
--- /dev/null
+++ b/objectapp/static/objectapp/css/config.rb
@@ -0,0 +1,9 @@
+# Require any additional compass plugins here.
+project_type = :stand_alone
+# Set this to the root of your project when deployed:
+http_path = "/"
+css_dir = "."
+sass_dir = "src"
+images_dir = "../img"
+output_style = :compact
+line_comments = false
diff --git a/objectapp/static/objectapp/css/dashboard_objectapp.css b/objectapp/static/objectapp/css/dashboard_objectapp.css
new file mode 100644
index 0000000..d1f4a24
--- /dev/null
+++ b/objectapp/static/objectapp/css/dashboard_objectapp.css
@@ -0,0 +1,32 @@
+body.dashboard #content { width: 75%; }
+
+.module { width: 400px; margin-right: 5px; }
+.module table { width: 100%; }
+.module table th, .module table td { font-size: 12px; }
+
+.managelink { background: url("../img/manage.png") no-repeat scroll 0 0.2em transparent; padding-left: 12px; }
+
+.previewlink { background: url("../img/preview.png") no-repeat scroll 0 0.2em transparent; padding-left: 12px; }
+
+input, textarea, select { border: 1px solid #cccccc; }
+
+.vTextField, .vLargeTextField { width: 25em; }
+
+.required label, label.required { color: #333333 !important; font-weight: bold !important; }
+
+.button { float: left; margin-right: 5px; }
+
+#contents th { width: 50%; }
+#contents td { padding-left: 10px; }
+#contents td a { font-weight: bold; }
+
+#quickpost th { width: 20%; vertical-align: middle; }
+#quickpost td { width: 100%; }
+
+#comments th { width: 10%; }
+#comments td { width: 100%; white-space: normal; }
+#comments td a { display: inline; }
+
+#drafts th, #linkbacks th, #comments th { font-weight: normal; }
+#drafts th a, #linkbacks th a, #comments th a { font-weight: bold; }
+#drafts th abbr, #linkbacks th abbr, #comments th abbr { color: #666666; }
diff --git a/objectapp/static/objectapp/css/ie.css b/objectapp/static/objectapp/css/ie.css
new file mode 100644
index 0000000..1840905
--- /dev/null
+++ b/objectapp/static/objectapp/css/ie.css
@@ -0,0 +1,39 @@
+body { text-align: center; }
+* html body legend { margin: 0px -8px 16px 0; padding: 0; }
+html > body p code { *white-space: normal; }
+
+.container { text-align: left; }
+
+sup { vertical-align: text-top; }
+
+sub { vertical-align: text-bottom; }
+
+hr { margin: -8px auto 11px; }
+
+img { -ms-interpolation-mode: bicubic; }
+
+fieldset { padding-top: 0; }
+
+legend { margin-top: -0.2em; margin-bottom: 1em; margin-left: -0.5em; }
+
+fieldset, #IE8#HACK { padding-top: 1.4em; }
+
+legend, #IE8#HACK { margin-top: 0; margin-bottom: 0; }
+
+textarea { overflow: auto; }
+
+label { position: relative; top: -0.25em; }
+
+input.text { margin: 0.5em 0; background-color: white; border: 1px solid #bbbbbb; }
+input.text:focus { border: 1px solid #666666; }
+input.title { margin: 0.5em 0; background-color: white; border: 1px solid #bbbbbb; }
+input.title:focus { border: 1px solid #666666; }
+input.checkbox { position: relative; top: 0.25em; }
+input.radio { position: relative; top: 0.25em; }
+input.button { position: relative; top: 0.25em; }
+
+textarea { margin: 0.5em 0; }
+
+select { margin: 0.5em 0; }
+
+button { position: relative; top: 0.25em; }
diff --git a/objectapp/static/objectapp/css/jquery.autocomplete.css b/objectapp/static/objectapp/css/jquery.autocomplete.css
new file mode 100644
index 0000000..44747e2
--- /dev/null
+++ b/objectapp/static/objectapp/css/jquery.autocomplete.css
@@ -0,0 +1,48 @@
+.ac_results {
+ padding: 0px;
+ border: 1px solid #cccccc;
+ background-color: white;
+ overflow: hidden;
+ z-index: 99999;
+}
+
+.ac_results ul {
+ width: 100%;
+ list-style-position: outside;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.ac_results li {
+ margin: 0px;
+ padding: 2px 5px;
+ cursor: default;
+ display: block;
+ /*
+ if width will be 100% horizontal scrollbar will apear
+ when scroll mode will be used
+ */
+ /*width: 100%;*/
+ font: menu;
+ font-size: 12px;
+ /*
+ it is very important, if line-height not setted or setted
+ in relative units scroll will be broken in firefox
+ */
+ line-height: 16px;
+ overflow: hidden;
+}
+
+.ac_loading {
+ background: white url('indicator.gif') right center no-repeat;
+}
+
+.ac_odd {
+ background-color: #eee;
+}
+
+.ac_over {
+ background-color: #7CA0C7;
+ color: white;
+}
diff --git a/objectapp/static/objectapp/css/print.css b/objectapp/static/objectapp/css/print.css
new file mode 100644
index 0000000..f89d498
--- /dev/null
+++ b/objectapp/static/objectapp/css/print.css
@@ -0,0 +1,33 @@
+body { line-height: 1.5; font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; color: black; background: none; font-size: 10pt; }
+
+.container { background: none; }
+
+hr { background: #cccccc; color: #cccccc; width: 100%; height: 2px; margin: 2em 0; padding: 0; border: none; }
+hr.space { background: white; color: white; }
+
+h1, h2, h3, h4, h5, h6 { font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; }
+
+code { font-size: 0.9em; font-family: "andale mono", "lucida console", monospace; }
+
+a img { border: none; }
+a:link, a:visited { background: transparent; font-weight: 700; text-decoration: underline; }
+
+p img.top { margin-top: 0; }
+
+blockquote { margin: 1.5em; padding: 1em; font-style: italic; font-size: 0.9em; }
+
+.small { font-size: 0.9em; }
+
+.large { font-size: 1.1em; }
+
+.quiet { color: #999999; }
+
+.hide { display: none; }
+
+body { font-size: 2em; }
+body a, body a:link, body a:visited, body a:focus, body a:active, body a:hover { text-decoration: none; color: #006699; }
+
+#sidebar, .top-navigation, .commentForm { display: none; }
+
+#header h1 { margin-bottom: 0; }
+#header h1 a { font-size: 2em; font-weight: bold; }
diff --git a/objectapp/static/objectapp/css/screen.css b/objectapp/static/objectapp/css/screen.css
new file mode 100644
index 0000000..e337e26
--- /dev/null
+++ b/objectapp/static/objectapp/css/screen.css
@@ -0,0 +1,569 @@
+body { line-height: 1.5; font-family: Arial, Helvetica, sans-serif; color: #333333; font-size: 75%; height:1500px;width:1500px}
+
+h1, h2, h3, h4, h5, h6 { font-weight: normal; color: #176691; border-radius:15px; }
+h1 img, h2 img, h3 img, h4 img, h5 img, h6 img { margin: 0; }
+
+h1 { font-size: 3em; line-height: 1; margin-bottom: 0.50em; }
+
+h2 { font-size: 2em; margin-bottom: 0.05em; }
+
+h3 { font-size: 1em; line-height: 1; margin-bottom: 1.00em; }
+
+h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1.25em; }
+
+h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.50em; }
+
+h6 { font-size: 1em; font-weight: bold; }
+
+p { margin: 0 0 1.5em; }
+p .left { display: inline; float: left; margin: 1.5em 1.5em 1.5em 0; padding: 0; }
+p .right { display: inline; float: right; margin: 1.5em 0 1.5em 1.5em; padding: 0; }
+
+a { text-decoration:underline; color: #0888CC; }
+a:visited { color: #006699; }
+a:focus { color: #003366; }
+a:hover { color: #E83E65; }
+a:active { color: #006699; }
+
+blockquote { margin-bottom: 1em; margin-left: 1em; margin-right: 1em; margin-top: 0.5em; color: #666666; font-style: oblique; }
+
+
+strong, dfn { font-weight: bold; }
+
+em, dfn { font-style: italic; }
+
+sup, sub { line-height: 0; }
+
+abbr, acronym { border-bottom: 1px dotted #666666; }
+
+address { margin: 0 0 1.5em; font-style: italic; }
+
+del { color: #666666; }
+
+pre { margin: 1.5em 0; white-space: pre; }
+
+pre, code, tt { font: 1em "andale mono", "lucida console", monospace; line-height: 1.5; }
+
+li ul, li ol { margin: 0; }
+
+ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 1.5em; }
+
+ul { list-style-type: disc; }
+
+ol { list-style-type: decimal; }
+
+dl { margin: 0 0 1.5em 0; }
+dl dt { font-weight: bold; }
+
+dd { margin-left: 1.5em; }
+
+table { margin-bottom: 1.4em; width: 100%; }
+
+th { font-weight: bold; }
+
+thead th { background: #c3d9ff; }
+
+th, td, caption { padding: 4px 10px 4px 5px; }
+
+table.striped tr:nth-child(even) td, table tr.even td { background: #e5ecf9; }
+
+tfoot { font-style: italic; }
+
+caption { background: #eeeeee; }
+
+.quiet { color: #666666; }
+
+.loud { color: #111111; }
+
+.clear { clear: both; }
+
+.nowrap { white-space: nowrap; }
+
+.clearfix { overflow: hidden; *zoom: 1; }
+
+.small { font-size: 0.8em; margin-bottom: 1.875em; line-height: 1.875em; }
+
+.large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; }
+
+.first { margin-left: 0; padding-left: 0; }
+
+.last { margin-right: 0; padding-right: 0; }
+
+.top { margin-top: 0; padding-top: 0; }
+
+.bottom { margin-bottom: 0; padding-bottom: 0; }
+
+.container { width: 950px; margin: 0 auto; overflow: hidden; *zoom: 1; }
+
+.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 { display: inline; float: left; margin-right: 10px; }
+* html .column, * html .span-1, * html .span-2, * html .span-3, * html .span-4, * html .span-5, * html .span-6, * html .span-7, * html .span-8, * html .span-9, * html .span-10, * html .span-11, * html .span-12, * html .span-13, * html .span-14, * html .span-15, * html .span-16, * html .span-17, * html .span-18, * html .span-19, * html .span-20, * html .span-21, * html .span-22, * html .span-23, * html .span-24 { overflow-x: hidden; }
+
+.last { margin-right: 0; }
+
+.span-1 { width: 30px; }
+
+.span-2 { width: 70px; }
+
+.span-3 { width: 110px; }
+
+.span-4 { width: 150px; }
+
+.span-5 { width: 190px; }
+
+.span-6 { width: 230px; }
+
+.span-7 { width: 270px; }
+
+.span-8 { width: 310px; }
+
+.span-9 { width: 350px; }
+
+.span-10 { width: 390px; }
+
+.span-11 { width: 430px; }
+
+.span-12 { width: 470px; }
+
+.span-13 { width: 510px; }
+
+.span-14 { width: 550px; }
+
+.span-15 { width: 590px; }
+
+.span-16 { width: 500px; }
+
+.span-17 { width: 670px; }
+
+.span-18 { width: 710px; }
+
+.span-19 { width: 750px; }
+
+.span-20 { width: 790px; }
+
+.span-21 { width: 830px; }
+
+.span-22 { width: 870px; }
+
+.span-23 { width: 910px; }
+
+.span-24 { width: 950px; margin: 0; }
+
+input.span-1, textarea.span-1, select.span-1 { width: 30px; }
+input.span-2, textarea.span-2, select.span-2 { width: 70px; }
+input.span-3, textarea.span-3, select.span-3 { width: 110px; }
+input.span-4, textarea.span-4, select.span-4 { width: 150px; }
+input.span-5, textarea.span-5, select.span-5 { width: 190px; }
+input.span-6, textarea.span-6, select.span-6 { width: 230px; }
+input.span-7, textarea.span-7, select.span-7 { width: 270px; }
+input.span-8, textarea.span-8, select.span-8 { width: 310px; }
+input.span-9, textarea.span-9, select.span-9 { width: 350px; }
+input.span-10, textarea.span-10, select.span-10 { width: 390px; }
+input.span-11, textarea.span-11, select.span-11 { width: 430px; }
+input.span-12, textarea.span-12, select.span-12 { width: 470px; }
+input.span-13, textarea.span-13, select.span-13 { width: 510px; }
+input.span-14, textarea.span-14, select.span-14 { width: 550px; }
+input.span-15, textarea.span-15, select.span-15 { width: 590px; }
+input.span-16, textarea.span-16, select.span-16 { width: 630px; }
+input.span-17, textarea.span-17, select.span-17 { width: 670px; }
+input.span-18, textarea.span-18, select.span-18 { width: 710px; }
+input.span-19, textarea.span-19, select.span-19 { width: 750px; }
+input.span-20, textarea.span-20, select.span-20 { width: 790px; }
+input.span-21, textarea.span-21, select.span-21 { width: 830px; }
+input.span-22, textarea.span-22, select.span-22 { width: 870px; }
+input.span-23, textarea.span-23, select.span-23 { width: 910px; }
+input.span-24, textarea.span-24, select.span-24 { width: 950px; }
+
+.append-1 { padding-right: 40px; }
+
+.append-2 { padding-right: 80px; }
+
+.append-3 { padding-right: 120px; }
+
+.append-4 { padding-right: 160px; }
+
+.append-5 { padding-right: 200px; }
+
+.append-6 { padding-right: 240px; }
+
+.append-7 { padding-right: 280px; }
+
+.append-8 { padding-right: 320px; }
+
+.append-9 { padding-right: 360px; }
+
+.append-10 { padding-right: 400px; }
+
+.append-11 { padding-right: 440px; }
+
+.append-12 { padding-right: 480px; }
+
+.append-13 { padding-right: 520px; }
+
+.append-14 { padding-right: 560px; }
+
+.append-15 { padding-right: 600px; }
+
+.append-16 { padding-right: 640px; }
+
+.append-17 { padding-right: 680px; }
+
+.append-18 { padding-right: 720px; }
+
+.append-19 { padding-right: 760px; }
+
+.append-20 { padding-right: 800px; }
+
+.append-21 { padding-right: 840px; }
+
+.append-22 { padding-right: 880px; }
+
+.append-23 { padding-right: 920px; }
+
+.prepend-1 { padding-left: 40px; }
+
+.prepend-2 { padding-left: 80px; }
+
+.prepend-3 { padding-left: 120px; }
+
+.prepend-4 { padding-left: 160px; }
+
+.prepend-5 { padding-left: 200px; }
+
+.prepend-6 { padding-left: 240px; }
+
+.prepend-7 { padding-left: 280px; }
+
+.prepend-8 { padding-left: 320px; }
+
+.prepend-9 { padding-left: 360px; }
+
+.prepend-10 { padding-left: 400px; }
+
+.prepend-11 { padding-left: 440px; }
+
+.prepend-12 { padding-left: 480px; }
+
+.prepend-13 { padding-left: 520px; }
+
+.prepend-14 { padding-left: 560px; }
+
+.prepend-15 { padding-left: 600px; }
+
+.prepend-16 { padding-left: 640px; }
+
+.prepend-17 { padding-left: 680px; }
+
+.prepend-18 { padding-left: 720px; }
+
+.prepend-19 { padding-left: 760px; }
+
+.prepend-20 { padding-left: 800px; }
+
+.prepend-21 { padding-left: 840px; }
+
+.prepend-22 { padding-left: 880px; }
+
+.prepend-23 { padding-left: 920px; }
+
+.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 { display: inline; float: left; position: relative; }
+
+.pull-1 { margin-left: -40px; }
+
+.pull-2 { margin-left: -80px; }
+
+.pull-3 { margin-left: -120px; }
+
+.pull-4 { margin-left: -160px; }
+
+.pull-5 { margin-left: -200px; }
+
+.pull-6 { margin-left: -240px; }
+
+.pull-7 { margin-left: -280px; }
+
+.pull-8 { margin-left: -320px; }
+
+.pull-9 { margin-left: -360px; }
+
+.pull-10 { margin-left: -400px; }
+
+.pull-11 { margin-left: -440px; }
+
+.pull-12 { margin-left: -480px; }
+
+.pull-13 { margin-left: -520px; }
+
+.pull-14 { margin-left: -560px; }
+
+.pull-15 { margin-left: -600px; }
+
+.pull-16 { margin-left: -640px; }
+
+.pull-17 { margin-left: -680px; }
+
+.pull-18 { margin-left: -720px; }
+
+.pull-19 { margin-left: -760px; }
+
+.pull-20 { margin-left: -800px; }
+
+.pull-21 { margin-left: -840px; }
+
+.pull-22 { margin-left: -880px; }
+
+.pull-23 { margin-left: -920px; }
+
+.pull-24 { margin-left: -960px; }
+
+.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 { display: inline; float: left; position: relative; }
+
+.push-1 { margin: 0 -40px 1.5em 40px; }
+
+.push-2 { margin: 0 -80px 1.5em 80px; }
+
+.push-3 { margin: 0 -120px 1.5em 120px; }
+
+.push-4 { margin: 0 -160px 1.5em 160px; }
+
+.push-5 { margin: 0 -200px 1.5em 200px; }
+
+.push-6 { margin: 0 -240px 1.5em 240px; }
+
+.push-7 { margin: 0 -280px 1.5em 280px; }
+
+.push-8 { margin: 0 -320px 1.5em 320px; }
+
+.push-9 { margin: 0 -360px 1.5em 360px; }
+
+.push-10 { margin: 0 -400px 1.5em 400px; }
+
+.push-11 { margin: 0 -440px 1.5em 440px; }
+
+.push-12 { margin: 0 -480px 1.5em 480px; }
+
+.push-13 { margin: 0 -520px 1.5em 520px; }
+
+.push-14 { margin: 0 -560px 1.5em 560px; }
+
+.push-15 { margin: 0 -600px 1.5em 600px; }
+
+.push-16 { margin: 0 -640px 1.5em 640px; }
+
+.push-17 { margin: 0 -680px 1.5em 680px; }
+
+.push-18 { margin: 0 -720px 1.5em 720px; }
+
+.push-19 { margin: 0 -760px 1.5em 760px; }
+
+.push-20 { margin: 0 -800px 1.5em 800px; }
+
+.push-21 { margin: 0 -840px 1.5em 840px; }
+
+.push-22 { margin: 0 -880px 1.5em 880px; }
+
+.push-23 { margin: 0 -920px 1.5em 920px; }
+
+.push-24 { margin: 0 -960px 1.5em 960px; }
+
+.prepend-top { margin-top: 1.5em; }
+
+.append-bottom { margin-bottom: 1.5em; }
+
+.showgrid { background: url('/../img/grid.png?1317027277'); }
+
+.feedback, .error, .alert, .notice, .success, .info { padding: 0.8em; margin-bottom: 1em; border: 2px solid #dddddd; }
+
+.error, .alert { background: #fbe3e4; color: #8a1f11; border-color: #fbc2c4; }
+.error a, .alert a { color: #8a1f11; }
+
+.notice { background: #fff6bf; color: #514721; border-color: #ffd324; }
+.notice a { color: #514721; }
+
+.success { background: #e6efc2; color: #264409; border-color: #c6d880; }
+.success a { color: #264409; }
+
+.info { background: #d5edf8; color: #205791; border-color: #92cae4; }
+.info a { color: #205791; }
+
+.hide { display: none; }
+
+.highlight { background: yellow; }
+
+.added { background: #006600; color: white; }
+
+.removed { background: #990000; color: white; }
+
+label { font-weight: bold; }
+
+fieldset { padding: 1.4em; margin: 0 0 1.5em 0; }
+
+legend { font-weight: bold; font-size: 1.2em; }
+
+input.text, input.title, input[type=email], input[type=text], input[type=password] { margin: 0.5em 0; background-color: white; padding: 5px; }
+input.title { font-size: 1.5em; }
+
+textarea { margin: 0.5em 0; padding: 5px; }
+
+select { margin: 0.5em 0; }
+
+fieldset { border: 1px solid #cccccc; }
+
+input.text, input.title, input[type=email], input[type=text], input[type=password], textarea { background-color: #fff; border: 1px solid #bbbbbb; }
+input.text:focus, input.title:focus, input[type=email]:focus, input[type=text]:focus, input[type=password]:focus, textarea:focus { border: 1px solid #666666; }
+
+select { background-color: #fff; border-width: 1px; border-style: solid; }
+
+input.text, input.title, input[type=email], input[type=text], input[type=password] { width: 300px; }
+
+textarea { width: 390px; height: 250px; }
+
+body { background: transparent url(../img/background.gif) repeat-x scroll left top; }
+
+ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 3.333em; }
+
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+
+#header { margin-bottom: 1em; border-bottom: 1px solid #e9e9f3; }
+#header h1 { font-size: 2em; font-weight: bold; margin-bottom: 0; }
+#header .top-navigation { float: right; }
+#header .top-navigation img { border: 0; }
+#header .top-navigation .link { padding-left: 1.5em; }
+#header .top-navigation .sitemap { background: transparent url(../img/sitemap.png) no-repeat scroll left center; }
+#header .top-navigation .feeds { background: transparent url(../img/rss.png) no-repeat scroll left center; }
+
+#breadcrumbs { background-color:#0888CC; font-size: 1.2em; color:white; }
+#b1 {position:relative; center:5%; background-color:#43AEE7; color: white; border: 1px solid #000000; font-size:1em;}
+#b2 {position:relative; center:6%; background-color:#43AEE7; color: white; border: 1px solid #000000; font-size:1em;}
+
+#body { margin-bottom: 1em; border-bottom: 1px solid #e9e9f3; }
+
+#content h1, #content h2, #content h3 { margin-top: 10px; }
+
+/* copied from structure.css */
+#content {
+ position: relative;
+ float: left;
+ clear: both;
+ margin-bottom: 10px;
+ margin-left: 0;
+ margin-right: 60px;
+ margin-top: 10px;
+ padding-right: 30px;
+ width:250px;
+}
+
+#graph {
+ position: absolute;
+ float: left;
+ clear: both;
+ margin-bottom: 10px;
+ margin-left: 0;
+ margin-right: 60px;
+ margin-top: 10px;
+ padding-right: 30px;
+ width: 250px;
+}
+
+/* copied from tools.css */
+.tools {
+ position: relative;
+ float: left;
+ clear: both;
+ padding: 6px 10px;
+ font-size: 11px;
+ font-weight: bold;
+}
+#sidebar{position:absolute; margin-left:600px; margin-right:150px; margin-top:10px; width:300px;}
+#sidebar div:first-child h3 { margin-top:0px; }
+#sidebar h3 { background-color: #C4EBF4; padding: 0.5em; }
+#sidebar p { padding-left: 1em; }
+#sidebar ul, #sidebar ol { padding-left: 2.5em; list-style-image: url(../img/bullet.png); }
+#sidebar li ul, #sidebar li ol { margin: 0 0.0em 0 0; }
+#sidebar input { width: auto; }
+#button {background-color:#43AEE7; color: white;}
+#sidebar .calendar h3 { margin-bottom: 0; }
+#sidebar .calendar .month-navigation { padding-bottom: 1.5em; }
+#sidebar .calendar .month-navigation .month-previous { float: left; }
+#sidebar .calendar .month-navigation .month-next { float: right; }
+#sidebar .tags div { padding: 0 5px 0 18px; }
+#sidebar .tags div ul { list-style-type: none; list-style: none; font-size: 1.5em; line-height: 1.8em; }
+#sidebar .tags div ul, #sidebar .tags div ul li { margin: 0px; padding: 0px; display: inline; }
+#sidebar .tags div ul li { list-style-image: none; list-style-type: none; margin-left: 0px; }
+#sidebar .tags div ul .tag_1 { font-size: 0.75em; color: #001e2d; }
+#sidebar .tags div ul .tag_2 { font-size: 1em; color: #00334c; }
+#sidebar .tags div ul .tag_3 { font-size: 1.125em; color: #005b89; }
+#sidebar .tags div ul .tag_4 { font-size: 2em; color: #0070a8; }
+#sidebar .tags div ul .tag_5 { font-size: 2.25em; color: #0099e5; }
+#sidebar .tags div ul .tag_6 { font-size: 3em; color: #00adff; }
+
+th.month{padding:0cm 2.5cm;}
+table.month { margin-bottom: 0; }
+table.month th.month { text-transform: uppercase; color: #006699; background: none; }
+table.month th { text-transform: capitalize; color: #917E7E; background-color: #C4EBF4; border-radius:15px;}
+table.month td { text-align: center; font-weight: bold; }
+table.month td.objecttype { background-color: #C4EBF4; }
+
+div.paginator { margin-bottom: 1em; }
+div.paginator span { font-size: 1.2em; margin-left: 0.3em; padding: 0 0.5em 0 0.5em; float: left; border: 1px solid #e9e9f3; }
+div.paginator span.index { margin-left: 0; }
+div.paginator span.page:hover, div.paginator span.next:hover, div.paginator span.previous:hover { background-color: #006699; }
+div.paginator span.page:hover a, div.paginator span.next:hover a, div.paginator span.previous:hover a { color: white; }
+div.paginator span.current { border-color: #006699; background-color: #006699; color: white; }
+
+.hobjecttype { margin-bottom: 1em; }
+.hobjecttype .objecttype-header { margin-bottom: 1em; border-radius:15px;}
+.hobjecttype .objecttype-header .objecttype-title { margin: 0; padding-left: 0.25em; padding-right: 0.25em; background-color:#C4EBF4}
+.hobjecttype .objecttype-header .objecttype-info { margin: 0; padding-left: 2em; background: #C4EBF4 url(../img/metatype.png) no-repeat scroll 0.5em; border-radius:15px;}
+.hobjecttype .objecttype-body .objecttype-content { padding-left: 1em; padding-right: 1em; }
+.hobjecttype .objecttype-body .objecttype-image { margin-top: -1.5em; }
+.hobjecttype .objecttype-body .objecttype-image p img { border: 0; margin: 0 1.5em 1.5em 0; }
+.hobjecttype .objecttype-footer p { margin-bottom: 0; }
+.hobjecttype .objecttype-footer p strong { padding-left: 1.5em; }
+.hobjecttype .objecttype-footer .objecttype-tags strong { background: transparent url(../img/tags.png) no-repeat scroll; }
+.hobjecttype .objecttype-footer .objecttype-shorturl strong { background: transparent url(../img/shorturl.png) no-repeat scroll; }
+.hobjecttype .objecttype-footer .objecttype-comments strong { background: transparent url(../img/comments.png) no-repeat scroll; }
+.hobjecttype .objecttype-header { background-color: #C4EBF4; }
+.hobjecttype .objecttype-content p { font-size: 1.2em; }
+.hobjecttype .objecttype-content pre, .hobjecttype .objecttype-content blockquote { overflow: auto; padding: 1em; clear: none; float: none; margin: 0 0 1.5em 0; width: auto; }
+.hobjecttype .objecttype-content blockquote { margin: 0 2em 1.5em 2em; border: 1px dashed #94949e; background-color: #fafaff; }
+.hobjecttype .objecttype-content blockquote p { font-size: 1.5em; margin: 0; }
+.hobjecttype .objecttype-content pre { line-height: 110%; background-color: #e9e9ff; border-top: 1px solid #94949e; border-bottom: 1px solid #94949e; }
+.hobjecttype .objecttype-footer p strong { color: #94949e; }
+
+.objecttype-widgets ul, .objecttype-widgets ol { padding-left: 2.5em; list-style-image: url(../img/bullet.png); }
+
+.featured .objecttype-header { background-color: #c4d9c4; }
+
+.hnodetype { margin-bottom: 1em; }
+.hnodetype .nodetype-header { margin-bottom: 1em; }
+.hnodetype .nodetype-header .nodetype-title { margin: 0; padding-left: 0.25em; padding-right: 0.25em; }
+.hnodetype .nodetype-header .nodetype-info { margin: 0; padding-left: 2em; background: transparent url(../img/Objecttype.png) no-repeat scroll 0.5em; }
+.hnodetype .nodetype-body .nodetype-content { padding-left: 1em; padding-right: 1em; }
+.hnodetype .nodetype-body .nodetype-image { margin-top: -1.5em; }
+.hnodetype .nodetype-body .nodetype-image p img { border: 0; margin: 0 1.5em 1.5em 0; }
+.hnodetype .nodetype-footer p { margin-bottom: 0; }
+.hnodetype .nodetype-footer p strong { padding-left: 1.5em; }
+.hnodetype .nodetype-footer .nodetype-tags strong { background: transparent url(../img/tags.png) no-repeat scroll; }
+.hnodetype .nodetype-footer .nodetype-shorturl strong { background: transparent url(../img/shorturl.png) no-repeat scroll; }
+.hnodetype .nodetype-footer .nodetype-comments strong { background: transparent url(../img/comments.png) no-repeat scroll; }
+.hnodetype .nodetype-header { background-color: #ffffff; }
+.hnodetype .nodetype-content p { font-size: 1.2em; }
+.hnodetype .nodetype-content pre, .hnodetype .nodetype-content blockquote { overflow: auto; padding: 1em; clear: none; float: none; margin: 0 0 1.5em 0; width: auto; }
+.hnodetype .nodetype-content blockquote { margin: 0 2em 1.5em 2em; border: 1px dashed #94949e; background-color: #fafaff; }
+.hnodetype .nodetype-content blockquote p { font-size: 1.5em; margin: 0; }
+.hnodetype .nodetype-content pre { line-height: 110%; background-color: #e9e9ff; border-top: 1px solid #94949e; border-bottom: 1px solid #94949e; }
+.hnodetype .nodetype-footer p strong { color: #94949e; }
+
+.nodetype-widgets ul, .nodetype-widgets ol { padding-left: 2.5em; list-style-image: url(../img/bullet.png); }
+
+.featured .nodetype-header { background-color: #c4d9c4; }
+
+ol#comment-list, ol#pingback-list, ol#trackback-list { width: 100%; margin-bottom: 0; padding-left: 1em; }
+ol#comment-list li, ol#pingback-list li, ol#trackback-list li { margin-bottom: 1em; border: 1px solid #e9e9f3; }
+ol#comment-list li.box2, ol#pingback-list li.box2, ol#trackback-list li.box2 { border-color: #94949e; background-color: #e9e9f3; }
+ol#comment-list li.post-author, ol#pingback-list li.post-author, ol#trackback-list li.post-author { color: #94949e; }
+ol#comment-list li p, ol#pingback-list li p, ol#trackback-list li p { margin-bottom: 0.5em; }
+ol#comment-list li img, ol#pingback-list li img, ol#trackback-list li img { padding: 5px; }
+ol#comment-list li .pingback-body, ol#comment-list li .trackback-body, ol#pingback-list li .pingback-body, ol#pingback-list li .trackback-body, ol#trackback-list li .pingback-body, ol#trackback-list li .trackback-body { padding: 5px; }
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author, ol#comment-list li .trackback-author, ol#pingback-list li .comment-author, ol#pingback-list li .pingback-author, ol#pingback-list li .trackback-author, ol#trackback-list li .comment-author, ol#trackback-list li .pingback-author, ol#trackback-list li .trackback-author { font-size: 1.2em; font-weight: bold; color: #666666; }
diff --git a/objectapp/static/objectapp/css/screen1.css b/objectapp/static/objectapp/css/screen1.css
new file mode 100644
index 0000000..a34236d
--- /dev/null
+++ b/objectapp/static/objectapp/css/screen1.css
@@ -0,0 +1,505 @@
+body { line-height: 1.5; font-family: Arial, Helvetica, sans-serif; color: #333333; font-size: 75%; }
+
+h1, h2, h3, h4, h5, h6 { font-weight: normal; color: #3399cc; }
+h1 img, h2 img, h3 img, h4 img, h5 img, h6 img { margin: 0; }
+
+h1 { font-size: 3em; line-height: 1; margin-bottom: 0.50em; }
+
+h2 { font-size: 2em; margin-bottom: 0.75em; }
+
+h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1.00em; }
+
+h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1.25em; }
+
+h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.50em; }
+
+h6 { font-size: 1em; font-weight: bold; }
+
+p { margin: 0 0 1.5em; }
+p .left { display: inline; float: left; margin: 1.5em 1.5em 1.5em 0; padding: 0; }
+p .right { display: inline; float: right; margin: 1.5em 0 1.5em 1.5em; padding: 0; }
+
+a { text-decoration: underline; color: #006699; }
+a:visited { color: #006699; }
+a:focus { color: #003366; }
+a:hover { color: #003366; }
+a:active { color: #006699; }
+
+blockquote { margin: 1.5em; color: #666666; font-style: italic; }
+
+strong, dfn { font-weight: bold; }
+
+em, dfn { font-style: italic; }
+
+sup, sub { line-height: 0; }
+
+abbr, acronym { border-bottom: 1px dotted #666666; }
+
+address { margin: 0 0 1.5em; font-style: italic; }
+
+del { color: #666666; }
+
+pre { margin: 1.5em 0; white-space: pre; }
+
+pre, code, tt { font: 1em "andale mono", "lucida console", monospace; line-height: 1.5; }
+
+li ul, li ol { margin: 0; }
+
+ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 1.5em; }
+
+ul { list-style-type: disc; }
+
+ol { list-style-type: decimal; }
+
+dl { margin: 0 0 1.5em 0; }
+dl dt { font-weight: bold; }
+
+dd { margin-left: 1.5em; }
+
+table { margin-bottom: 1.4em; width: 100%; }
+
+th { font-weight: bold; }
+
+thead th { background: #c3d9ff; }
+
+th, td, caption { padding: 4px 10px 4px 5px; }
+
+table.striped tr:nth-child(even) td, table tr.even td { background: #e5ecf9; }
+
+tfoot { font-style: italic; }
+
+caption { background: #eeeeee; }
+
+.quiet { color: #666666; }
+
+.loud { color: #111111; }
+
+.clear { clear: both; }
+
+.nowrap { white-space: nowrap; }
+
+.clearfix { overflow: hidden; *zoom: 1; }
+
+.small { font-size: 0.8em; margin-bottom: 1.875em; line-height: 1.875em; }
+
+.large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; }
+
+.first { margin-left: 0; padding-left: 0; }
+
+.last { margin-right: 0; padding-right: 0; }
+
+.top { margin-top: 0; padding-top: 0; }
+
+.bottom { margin-bottom: 0; padding-bottom: 0; }
+
+.container { width: 950px; margin: 0 auto; overflow: hidden; *zoom: 1; }
+
+.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 { display: inline; float: left; margin-right: 10px; }
+* html .column, * html .span-1, * html .span-2, * html .span-3, * html .span-4, * html .span-5, * html .span-6, * html .span-7, * html .span-8, * html .span-9, * html .span-10, * html .span-11, * html .span-12, * html .span-13, * html .span-14, * html .span-15, * html .span-16, * html .span-17, * html .span-18, * html .span-19, * html .span-20, * html .span-21, * html .span-22, * html .span-23, * html .span-24 { overflow-x: hidden; }
+
+.last { margin-right: 0; }
+
+.span-1 { width: 30px; }
+
+.span-2 { width: 70px; }
+
+.span-3 { width: 110px; }
+
+.span-4 { width: 150px; }
+
+.span-5 { width: 190px; }
+
+.span-6 { width: 230px; }
+
+.span-7 { width: 270px; }
+
+.span-8 { width: 310px; }
+
+.span-9 { width: 350px; }
+
+.span-10 { width: 390px; }
+
+.span-11 { width: 430px; }
+
+.span-12 { width: 470px; }
+
+.span-13 { width: 510px; }
+
+.span-14 { width: 550px; }
+
+.span-15 { width: 590px; }
+
+.span-16 { width: 630px; }
+
+.span-17 { width: 670px; }
+
+.span-18 { width: 710px; }
+
+.span-19 { width: 750px; }
+
+.span-20 { width: 790px; }
+
+.span-21 { width: 830px; }
+
+.span-22 { width: 870px; }
+
+.span-23 { width: 910px; }
+
+.span-24 { width: 950px; margin: 0; }
+
+input.span-1, textarea.span-1, select.span-1 { width: 30px; }
+input.span-2, textarea.span-2, select.span-2 { width: 70px; }
+input.span-3, textarea.span-3, select.span-3 { width: 110px; }
+input.span-4, textarea.span-4, select.span-4 { width: 150px; }
+input.span-5, textarea.span-5, select.span-5 { width: 190px; }
+input.span-6, textarea.span-6, select.span-6 { width: 230px; }
+input.span-7, textarea.span-7, select.span-7 { width: 270px; }
+input.span-8, textarea.span-8, select.span-8 { width: 310px; }
+input.span-9, textarea.span-9, select.span-9 { width: 350px; }
+input.span-10, textarea.span-10, select.span-10 { width: 390px; }
+input.span-11, textarea.span-11, select.span-11 { width: 430px; }
+input.span-12, textarea.span-12, select.span-12 { width: 470px; }
+input.span-13, textarea.span-13, select.span-13 { width: 510px; }
+input.span-14, textarea.span-14, select.span-14 { width: 550px; }
+input.span-15, textarea.span-15, select.span-15 { width: 590px; }
+input.span-16, textarea.span-16, select.span-16 { width: 630px; }
+input.span-17, textarea.span-17, select.span-17 { width: 670px; }
+input.span-18, textarea.span-18, select.span-18 { width: 710px; }
+input.span-19, textarea.span-19, select.span-19 { width: 750px; }
+input.span-20, textarea.span-20, select.span-20 { width: 790px; }
+input.span-21, textarea.span-21, select.span-21 { width: 830px; }
+input.span-22, textarea.span-22, select.span-22 { width: 870px; }
+input.span-23, textarea.span-23, select.span-23 { width: 910px; }
+input.span-24, textarea.span-24, select.span-24 { width: 950px; }
+
+.append-1 { padding-right: 40px; }
+
+.append-2 { padding-right: 80px; }
+
+.append-3 { padding-right: 120px; }
+
+.append-4 { padding-right: 160px; }
+
+.append-5 { padding-right: 200px; }
+
+.append-6 { padding-right: 240px; }
+
+.append-7 { padding-right: 280px; }
+
+.append-8 { padding-right: 320px; }
+
+.append-9 { padding-right: 360px; }
+
+.append-10 { padding-right: 400px; }
+
+.append-11 { padding-right: 440px; }
+
+.append-12 { padding-right: 480px; }
+
+.append-13 { padding-right: 520px; }
+
+.append-14 { padding-right: 560px; }
+
+.append-15 { padding-right: 600px; }
+
+.append-16 { padding-right: 640px; }
+
+.append-17 { padding-right: 680px; }
+
+.append-18 { padding-right: 720px; }
+
+.append-19 { padding-right: 760px; }
+
+.append-20 { padding-right: 800px; }
+
+.append-21 { padding-right: 840px; }
+
+.append-22 { padding-right: 880px; }
+
+.append-23 { padding-right: 920px; }
+
+.prepend-1 { padding-left: 40px; }
+
+.prepend-2 { padding-left: 80px; }
+
+.prepend-3 { padding-left: 120px; }
+
+.prepend-4 { padding-left: 160px; }
+
+.prepend-5 { padding-left: 200px; }
+
+.prepend-6 { padding-left: 240px; }
+
+.prepend-7 { padding-left: 280px; }
+
+.prepend-8 { padding-left: 320px; }
+
+.prepend-9 { padding-left: 360px; }
+
+.prepend-10 { padding-left: 400px; }
+
+.prepend-11 { padding-left: 440px; }
+
+.prepend-12 { padding-left: 480px; }
+
+.prepend-13 { padding-left: 520px; }
+
+.prepend-14 { padding-left: 560px; }
+
+.prepend-15 { padding-left: 600px; }
+
+.prepend-16 { padding-left: 640px; }
+
+.prepend-17 { padding-left: 680px; }
+
+.prepend-18 { padding-left: 720px; }
+
+.prepend-19 { padding-left: 760px; }
+
+.prepend-20 { padding-left: 800px; }
+
+.prepend-21 { padding-left: 840px; }
+
+.prepend-22 { padding-left: 880px; }
+
+.prepend-23 { padding-left: 920px; }
+
+.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 { display: inline; float: left; position: relative; }
+
+.pull-1 { margin-left: -40px; }
+
+.pull-2 { margin-left: -80px; }
+
+.pull-3 { margin-left: -120px; }
+
+.pull-4 { margin-left: -160px; }
+
+.pull-5 { margin-left: -200px; }
+
+.pull-6 { margin-left: -240px; }
+
+.pull-7 { margin-left: -280px; }
+
+.pull-8 { margin-left: -320px; }
+
+.pull-9 { margin-left: -360px; }
+
+.pull-10 { margin-left: -400px; }
+
+.pull-11 { margin-left: -440px; }
+
+.pull-12 { margin-left: -480px; }
+
+.pull-13 { margin-left: -520px; }
+
+.pull-14 { margin-left: -560px; }
+
+.pull-15 { margin-left: -600px; }
+
+.pull-16 { margin-left: -640px; }
+
+.pull-17 { margin-left: -680px; }
+
+.pull-18 { margin-left: -720px; }
+
+.pull-19 { margin-left: -760px; }
+
+.pull-20 { margin-left: -800px; }
+
+.pull-21 { margin-left: -840px; }
+
+.pull-22 { margin-left: -880px; }
+
+.pull-23 { margin-left: -920px; }
+
+.pull-24 { margin-left: -960px; }
+
+.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 { display: inline; float: left; position: relative; }
+
+.push-1 { margin: 0 -40px 1.5em 40px; }
+
+.push-2 { margin: 0 -80px 1.5em 80px; }
+
+.push-3 { margin: 0 -120px 1.5em 120px; }
+
+.push-4 { margin: 0 -160px 1.5em 160px; }
+
+.push-5 { margin: 0 -200px 1.5em 200px; }
+
+.push-6 { margin: 0 -240px 1.5em 240px; }
+
+.push-7 { margin: 0 -280px 1.5em 280px; }
+
+.push-8 { margin: 0 -320px 1.5em 320px; }
+
+.push-9 { margin: 0 -360px 1.5em 360px; }
+
+.push-10 { margin: 0 -400px 1.5em 400px; }
+
+.push-11 { margin: 0 -440px 1.5em 440px; }
+
+.push-12 { margin: 0 -480px 1.5em 480px; }
+
+.push-13 { margin: 0 -520px 1.5em 520px; }
+
+.push-14 { margin: 0 -560px 1.5em 560px; }
+
+.push-15 { margin: 0 -600px 1.5em 600px; }
+
+.push-16 { margin: 0 -640px 1.5em 640px; }
+
+.push-17 { margin: 0 -680px 1.5em 680px; }
+
+.push-18 { margin: 0 -720px 1.5em 720px; }
+
+.push-19 { margin: 0 -760px 1.5em 760px; }
+
+.push-20 { margin: 0 -800px 1.5em 800px; }
+
+.push-21 { margin: 0 -840px 1.5em 840px; }
+
+.push-22 { margin: 0 -880px 1.5em 880px; }
+
+.push-23 { margin: 0 -920px 1.5em 920px; }
+
+.push-24 { margin: 0 -960px 1.5em 960px; }
+
+.prepend-top { margin-top: 1.5em; }
+
+.append-bottom { margin-bottom: 1.5em; }
+
+.showgrid { background: url('/../img/grid.png?1317027277'); }
+
+.feedback, .error, .alert, .notice, .success, .info { padding: 0.8em; margin-bottom: 1em; border: 2px solid #dddddd; }
+
+.error, .alert { background: #fbe3e4; color: #8a1f11; border-color: #fbc2c4; }
+.error a, .alert a { color: #8a1f11; }
+
+.notice { background: #fff6bf; color: #514721; border-color: #ffd324; }
+.notice a { color: #514721; }
+
+.success { background: #e6efc2; color: #264409; border-color: #c6d880; }
+.success a { color: #264409; }
+
+.info { background: #d5edf8; color: #205791; border-color: #92cae4; }
+.info a { color: #205791; }
+
+.hide { display: none; }
+
+.highlight { background: yellow; }
+
+.added { background: #006600; color: white; }
+
+.removed { background: #990000; color: white; }
+
+label { font-weight: bold; }
+
+fieldset { padding: 1.4em; margin: 0 0 1.5em 0; }
+
+legend { font-weight: bold; font-size: 1.2em; }
+
+input.text, input.title, input[type=email], input[type=text], input[type=password] { margin: 0.5em 0; background-color: white; padding: 5px; }
+input.title { font-size: 1.5em; }
+
+textarea { margin: 0.5em 0; padding: 5px; }
+
+select { margin: 0.5em 0; }
+
+fieldset { border: 1px solid #cccccc; }
+
+input.text, input.title, input[type=email], input[type=text], input[type=password], textarea { background-color: #fff; border: 1px solid #bbbbbb; }
+input.text:focus, input.title:focus, input[type=email]:focus, input[type=text]:focus, input[type=password]:focus, textarea:focus { border: 1px solid #666666; }
+
+select { background-color: #fff; border-width: 1px; border-style: solid; }
+
+input.text, input.title, input[type=email], input[type=text], input[type=password] { width: 300px; }
+
+textarea { width: 390px; height: 250px; }
+
+body { background: transparent url(../img/background.gif) repeat-x scroll left top; }
+
+ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 3.333em; }
+
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+
+#header { margin-bottom: 1em; border-bottom: 1px solid #e9e9f3; }
+#header h1 { font-size: 4em; font-weight: bold; margin-bottom: 0; }
+#header .top-navigation { float: right; }
+#header .top-navigation img { border: 0; }
+#header .top-navigation .link { padding-left: 1.5em; }
+#header .top-navigation .sitemap { background: transparent url(../img/sitemap.png) no-repeat scroll left center; }
+#header .top-navigation .feeds { background: transparent url(../img/rss.png) no-repeat scroll left center; }
+
+#breadcrumbs { font-size: 1.2em; }
+
+#body { margin-bottom: 1em; border-bottom: 1px solid #e9e9f3; }
+
+#content h1, #content h2, #content h3 { margin-top: 0; }
+
+#sidebar div:first-child h3 { margin-top: 0; }
+#sidebar h3 { background-color: #e9e9f3; padding: 0.5em; }
+#sidebar p { padding-left: 1em; }
+#sidebar ul, #sidebar ol { padding-left: 2.5em; list-style-image: url(../img/bullet.png); }
+#sidebar li ul, #sidebar li ol { margin: 0 1.5em 0 0; }
+#sidebar input { width: auto; }
+#sidebar .calendar h3 { margin-bottom: 0; }
+#sidebar .calendar .month-navigation { padding-bottom: 1.5em; }
+#sidebar .calendar .month-navigation .month-previous { float: left; }
+#sidebar .calendar .month-navigation .month-next { float: right; }
+#sidebar .tags div { padding: 0 5px 0 18px; }
+#sidebar .tags div ul { list-style-type: none; list-style: none; font-size: 1.5em; line-height: 1.8em; }
+#sidebar .tags div ul, #sidebar .tags div ul li { margin: 0px; padding: 0px; display: inline; }
+#sidebar .tags div ul li { list-style-image: none; list-style-type: none; margin-left: 0px; }
+#sidebar .tags div ul .tag_1 { font-size: 0.75em; color: #001e2d; }
+#sidebar .tags div ul .tag_2 { font-size: 1em; color: #00334c; }
+#sidebar .tags div ul .tag_3 { font-size: 1.125em; color: #005b89; }
+#sidebar .tags div ul .tag_4 { font-size: 2em; color: #0070a8; }
+#sidebar .tags div ul .tag_5 { font-size: 2.25em; color: #0099e5; }
+#sidebar .tags div ul .tag_6 { font-size: 3em; color: #00adff; }
+
+table.month { margin-bottom: 0; }
+table.month th.month { text-transform: uppercase; color: #006699; background: none; }
+table.month th { text-transform: capitalize; color: #94949e; background-color: #e9e9f3; }
+table.month td { text-align: center; font-weight: bold; }
+table.month td.gbobject { background-color: #e9e9f3; }
+
+div.paginator { margin-bottom: 1em; }
+div.paginator span { font-size: 1.2em; margin-left: 0.3em; padding: 0 0.5em 0 0.5em; float: left; border: 1px solid #e9e9f3; }
+div.paginator span.index { margin-left: 0; }
+div.paginator span.page:hover, div.paginator span.next:hover, div.paginator span.previous:hover { background-color: #006699; }
+div.paginator span.page:hover a, div.paginator span.next:hover a, div.paginator span.previous:hover a { color: white; }
+div.paginator span.current { border-color: #006699; background-color: #006699; color: white; }
+
+.hgbobject { margin-bottom: 1em; }
+.hgbobject .gbobject-header { margin-bottom: 1em; }
+.hgbobject .gbobject-header .gbobject-title { margin: 0; padding-left: 0.25em; padding-right: 0.25em; }
+.hgbobject .gbobject-header .gbobject-info { margin: 0; padding-left: 2em; background: transparent url(../img/Objecttype.png) no-repeat scroll 0.5em; }
+.hgbobject .gbobject-body .gbobject-content { padding-left: 1em; padding-right: 1em; }
+.hgbobject .gbobject-body .gbobject-image { margin-top: -1.5em; }
+.hgbobject .gbobject-body .gbobject-image p img { border: 0; margin: 0 1.5em 1.5em 0; }
+.hgbobject .gbobject-footer p { margin-bottom: 0; }
+.hgbobject .gbobject-footer p strong { padding-left: 1.5em; }
+.hgbobject .gbobject-footer .gbobject-tags strong { background: transparent url(../img/tags.png) no-repeat scroll; }
+.hgbobject .gbobject-footer .gbobject-shorturl strong { background: transparent url(../img/shorturl.png) no-repeat scroll; }
+.hgbobject .gbobject-footer .gbobject-comments strong { background: transparent url(../img/comments.png) no-repeat scroll; }
+.hgbobject .gbobject-header { background-color: #e9e9f3; }
+.hgbobject .gbobject-content p { font-size: 1.2em; }
+.hgbobject .gbobject-content pre, .hgbobject .gbobject-content blockquote { overflow: auto; padding: 1em; clear: none; float: none; margin: 0 0 1.5em 0; width: auto; }
+.hgbobject .gbobject-content blockquote { margin: 0 2em 1.5em 2em; border: 1px dashed #94949e; background-color: #fafaff; }
+.hgbobject .gbobject-content blockquote p { font-size: 1.5em; margin: 0; }
+.hgbobject .gbobject-content pre { line-height: 110%; background-color: #e9e9ff; border-top: 1px solid #94949e; border-bottom: 1px solid #94949e; }
+.hgbobject .gbobject-footer p strong { color: #94949e; }
+
+.gbobject-widgets ul, .gbobject-widgets ol { padding-left: 2.5em; list-style-image: url(../img/bullet.png); }
+
+.featured .gbobject-header { background-color: #c4d9c4; }
+
+ol#comment-list, ol#pingback-list, ol#trackback-list { width: 100%; margin-bottom: 0; padding-left: 1em; }
+ol#comment-list li, ol#pingback-list li, ol#trackback-list li { margin-bottom: 1em; border: 1px solid #e9e9f3; }
+ol#comment-list li.box2, ol#pingback-list li.box2, ol#trackback-list li.box2 { border-color: #94949e; background-color: #e9e9f3; }
+ol#comment-list li.post-author, ol#pingback-list li.post-author, ol#trackback-list li.post-author { color: #94949e; }
+ol#comment-list li p, ol#pingback-list li p, ol#trackback-list li p { margin-bottom: 0.5em; }
+ol#comment-list li img, ol#pingback-list li img, ol#trackback-list li img { padding: 5px; }
+ol#comment-list li .pingback-body, ol#comment-list li .trackback-body, ol#pingback-list li .pingback-body, ol#pingback-list li .trackback-body, ol#trackback-list li .pingback-body, ol#trackback-list li .trackback-body { padding: 5px; }
+ol#comment-list li .comment-author, ol#comment-list li .pingback-author, ol#comment-list li .trackback-author, ol#pingback-list li .comment-author, ol#pingback-list li .pingback-author, ol#pingback-list li .trackback-author, ol#trackback-list li .comment-author, ol#trackback-list li .pingback-author, ol#trackback-list li .trackback-author { font-size: 1.2em; font-weight: bold; color: #666666; }
diff --git a/objectapp/static/objectapp/css/slider.css b/objectapp/static/objectapp/css/slider.css
new file mode 100644
index 0000000..d3bae53
--- /dev/null
+++ b/objectapp/static/objectapp/css/slider.css
@@ -0,0 +1,12 @@
+#slider { width: 400px; padding-right: 230px; position: relative; height: 250px; overflow: hidden; margin-bottom: 5px; }
+#slider ul.ui-tabs-nav { list-style: none; padding: 0; margin: 0; top: 0; left: 400px; width: 230px; position: absolute; overflow: hidden; }
+#slider ul.ui-tabs-nav li a { display: block; height: 49px; border-bottom: 1px solid #e9e9f3; }
+#slider ul.ui-tabs-nav li a img { float: left; border: 0; padding: 3px; width: 78.4px; height: 43px; }
+#slider ul.ui-tabs-nav li a:hover { text-decoration: none; background: #e9e9f3; }
+#slider ul.ui-tabs-nav li.ui-tabs-selected a { background: #44aadd; color: white; }
+#slider div.ui-tabs-panel { position: relative; width: 400px; height: 250px; }
+#slider div.ui-tabs-panel .panel_info { left: 0; bottom: 0; position: absolute; height: 70px; padding: 5px; color: white; background: url(../img/trans.png); width: 390px; }
+#slider div.ui-tabs-panel a { color: #44aadd; }
+#slider div.ui-tabs-panel h2 { margin: 0; overflow: hidden; }
+#slider div.ui-tabs-panel img { width: 400px; height: 250px; }
+#slider .ui-tabs-hide { display: none; }
diff --git a/objectapp/static/objectapp/css/src/_base.sass b/objectapp/static/objectapp/css/src/_base.sass
new file mode 100644
index 0000000..9b9f570
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/_base.sass
@@ -0,0 +1,32 @@
+// Here is where you can define your constants for your application
+// and to configure the blueprint framework.
+
+$blueprint_grid_columns: 24
+$blueprint_container_size: 950px
+$blueprint_grid_margin: 10px
+
+// Use this to calculate the width based on the total width.
+// Or you can set !blueprint_grid_width to a fixed value
+// and unset !blueprint_container_size -- it will be calculated for you.
+
+$blueprint_font_size: 12px
+$blueprint_font_family: unquote("Arial, Helvetica, sans-serif")
+$blueprint_grid_width: ($blueprint_container_size + $blueprint_grid_margin) / $blueprint_grid_columns - $blueprint_grid_margin
+
+// Colors
+$font_color: #333333
+$link_color: #006699
+$box_color: #e9e9f3
+
+// Derived colors
+$box_font_color: $box_color - #555555
+$link_hover_color: $link_color - #333333
+$link_active_color: $link_color
+$link_visited_color: $link_color
+$header_color: $link_color + #333333
+
+// Mixins
+@import gbobject
+@import calendar
+@import paginator
+@import tag-cloud
diff --git a/objectapp/static/objectapp/css/src/_calendar.sass b/objectapp/static/objectapp/css/src/_calendar.sass
new file mode 100644
index 0000000..d3c18d5
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/_calendar.sass
@@ -0,0 +1,15 @@
+=calendar($head_color, $box_color, $box_font_color)
+ margin-bottom: 0
+ th.month
+ text-transform: uppercase
+ color: $head_color
+ background: none
+ th
+ text-transform: capitalize
+ color: $box_font_color
+ background-color: $box_color
+ td
+ text-align: center
+ font-weight: bold
+ &.gbobject
+ background-color: $box_color
diff --git a/objectapp/static/objectapp/css/src/_gbobject.sass b/objectapp/static/objectapp/css/src/_gbobject.sass
new file mode 100644
index 0000000..5805303
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/_gbobject.sass
@@ -0,0 +1,35 @@
+=gbobject
+ margin-bottom: 1em
+ .gbobject-header
+ margin-bottom: 1em
+ .gbobject-title
+ margin: 0
+ padding-left: 0.25em
+ padding-right: 0.25em
+ .gbobject-info
+ margin: 0
+ padding-left: 2em
+ background: transparent url(../img/Objecttype.png) no-repeat scroll 0.5em
+ .gbobject-body
+ .gbobject-content
+ padding-left: 1em
+ padding-right: 1em
+ .gbobject-image
+ margin-top: -1.5em
+ p img
+ border: 0
+ margin: 0 1.5em 1.5em 0
+ .gbobject-footer
+ p
+ margin-bottom: 0
+ strong
+ padding-left: 1.5em
+ .gbobject-tags
+ strong
+ background: transparent url(../img/tags.png) no-repeat scroll
+ .gbobject-shorturl
+ strong
+ background: transparent url(../img/shorturl.png) no-repeat scroll
+ .gbobject-comments
+ strong
+ background: transparent url(../img/comments.png) no-repeat scroll
diff --git a/objectapp/static/objectapp/css/src/_paginator.sass b/objectapp/static/objectapp/css/src/_paginator.sass
new file mode 100644
index 0000000..b1258fc
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/_paginator.sass
@@ -0,0 +1,19 @@
+=paginator($link_color, $box_color)
+ margin-bottom: 1em
+ span
+ font-size: 1.2em
+ margin-left: 0.3em
+ padding: 0 0.5em 0 0.5em
+ float: left
+ border: 1px solid $box_color
+ &.index
+ margin-left: 0
+ &.page, &.next, &.previous
+ &:hover
+ background-color: $link_color
+ a
+ color: white
+ &.current
+ border-color: $link_color
+ background-color: $link_color
+ color: white
diff --git a/objectapp/static/objectapp/css/src/_tag-cloud.sass b/objectapp/static/objectapp/css/src/_tag-cloud.sass
new file mode 100644
index 0000000..6d859b6
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/_tag-cloud.sass
@@ -0,0 +1,21 @@
+=tag-cloud($link_color, $base_size: 1em)
+ font-size: $base_size
+ line-height: 1.2 * $base_size
+ .tag_1
+ font-size: $base_size / 2
+ color: $link_color * 0.3
+ .tag_2
+ font-size: 2 * $base_size / 3
+ color: $link_color * 0.5
+ .tag_3
+ font-size: 3 * $base_size / 4
+ color: $link_color * 0.9
+ .tag_4
+ font-size: 4 * $base_size / 3
+ color: $link_color * 1.1
+ .tag_5
+ font-size: 3 * $base_size / 2
+ color: $link_color * 1.5
+ .tag_6
+ font-size: 2 * $base_size
+ color: $link_color * 1.7 \ No newline at end of file
diff --git a/objectapp/static/objectapp/css/src/dashboard_objectapp.sass b/objectapp/static/objectapp/css/src/dashboard_objectapp.sass
new file mode 100644
index 0000000..1aa979d
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/dashboard_objectapp.sass
@@ -0,0 +1,66 @@
+body
+ &.dashboard
+ #content
+ width: 75%
+
+.module
+ width: 400px
+ margin-right: 5px
+ table
+ width: 100%
+ th, td
+ font-size: 12px
+
+.managelink
+ background: url("../img/manage.png") no-repeat scroll 0 0.2em transparent
+ padding-left: 12px
+
+.previewlink
+ background: url("../img/preview.png") no-repeat scroll 0 0.2em transparent
+ padding-left: 12px
+
+input, textarea, select
+ border: 1px solid #cccccc
+
+.vTextField, .vLargeTextField
+ width: 25em
+
+.required label, label.required
+ color: #333333 !important
+ font-weight: bold !important
+
+.button
+ float: left
+ margin-right: 5px
+
+#contents
+ th
+ width: 50%
+ td
+ padding-left: 10px
+ a
+ font-weight: bold
+
+#quickpost
+ th
+ width: 20%
+ vertical-align: middle
+ td
+ width: 100%
+
+#comments
+ th
+ width: 10%
+ td
+ width: 100%
+ white-space: normal
+ a
+ display: inline
+
+#drafts, #linkbacks, #comments
+ th
+ font-weight: normal
+ a
+ font-weight: bold
+ abbr
+ color: #666666
diff --git a/objectapp/static/objectapp/css/src/ie.sass b/objectapp/static/objectapp/css/src/ie.sass
new file mode 100644
index 0000000..5bad66b
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/ie.sass
@@ -0,0 +1,3 @@
+@import blueprint
+
++blueprint-ie
diff --git a/objectapp/static/objectapp/css/src/print.sass b/objectapp/static/objectapp/css/src/print.sass
new file mode 100644
index 0000000..6453761
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/print.sass
@@ -0,0 +1,22 @@
+@import blueprint
+@import compass/utilities/links/link-colors
+
+$link_color: #006699
+
++blueprint-print
+
+body
+ font-size: 2em
+ a, a:link, a:visited, a:focus, a:active, a:hover
+ text-decoration: none
+ color: $link_color
+
+#sidebar, .top-navigation, .commentForm
+ display: none
+
+#header
+ h1
+ margin-bottom: 0
+ a
+ font-size: 2em
+ font-weight: bold \ No newline at end of file
diff --git a/objectapp/static/objectapp/css/src/screen.sass b/objectapp/static/objectapp/css/src/screen.sass
new file mode 100644
index 0000000..f4d7765
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/screen.sass
@@ -0,0 +1,160 @@
+@import base
+@import blueprint
+@import compass/utilities/lists/bullets
+@import compass/utilities/lists/inline-list
+@import compass/utilities/links/hover-link
+
++blueprint
+
+// Global
+body
+ background: transparent url(../img/background.gif) repeat-x scroll left top
+
+ul, ol
+ margin: 0 1.5em 1.5em 0
+ padding-left: 3.333em
+a
+ +hover-link
+
+// Header
+#header
+ margin-bottom: 1em
+ border-bottom: 1px solid $box_color
+ h1
+ font-size: 4em
+ font-weight: bold
+ margin-bottom: 0
+ .top-navigation
+ float: right
+ img
+ border: 0
+ .link
+ padding-left: 1.5em
+ .sitemap
+ background: transparent url(../img/sitemap.png) no-repeat scroll left center
+ .feeds
+ background: transparent url(../img/rss.png) no-repeat scroll left center
+
+// Breadcrumbs
+#breadcrumbs
+ font-size: 1.2em
+
+// Body
+#body
+ margin-bottom: 1em
+ border-bottom: 1px solid $box_color
+
+// Content
+#content
+ h1, h2, h3
+ margin-top: 0
+
+// Sidebar
+#sidebar
+ div:first-child
+ h3
+ margin-top: 0
+ h3
+ background-color: $box_color
+ padding: 0.5em
+ p
+ padding-left: 1em
+ ul, ol
+ padding-left: 2.5em
+ list-style-image: url(../img/bullet.png)
+ li ul, li ol
+ margin: 0 1.5em 0 0
+ input
+ width: auto
+ .calendar
+ h3
+ margin-bottom: 0
+ .month-navigation
+ padding-bottom: 1.5em
+ .month-previous
+ float: left
+ .month-next
+ float: right
+ .tags
+ div
+ padding: 0 5px 0 18px
+ ul
+ +inline-list
+ +no-bullets
+ +tag-cloud($link_color, 1.5em)
+
+// Calendar
+table.month
+ +calendar($link_color, $box_color, $box_font_color)
+
+// Pagination
+div.paginator
+ +paginator($link_color, $box_color)
+
+// Gbobject
+.hgbobject
+ +gbobject
+ .gbobject-header
+ background-color: $box_color
+ .gbobject-content
+ p
+ font-size: 1.2em
+ pre, blockquote
+ overflow: auto
+ padding: 1em
+ clear: none
+ float: none
+ margin: 0 0 1.5em 0
+ width: auto
+
+ blockquote
+ margin: 0 2em 1.5em 2em
+ border: 1px dashed $box_font_color
+ background-color: $box_color + #111
+ p
+ font-size: 1.5em
+ margin: 0
+ pre
+ line-height: 110%
+ background-color: $box_color + #002
+ border-top: 1px solid $box_font_color
+ border-bottom: 1px solid $box_font_color
+
+ .gbobject-footer
+ p
+ strong
+ color: $box_font_color
+
+.gbobject-widgets
+ ul, ol
+ padding-left: 2.5em
+ list-style-image: url(../img/bullet.png)
+
+.featured
+ .gbobject-header
+ background-color: #C4D9C4
+
+// Discussions
+ol#comment-list, ol#pingback-list, ol#trackback-list
+ width: 100%
+ margin-bottom: 0
+ padding-left: 1em
+ li
+ margin-bottom: 1em
+ border: 1px solid $box_color
+ &.box2
+ border-color: $box_font_color
+ background-color: $box_color
+ &.post-author
+ color: $box_font_color
+ p
+ margin-bottom: 0.5em
+ img
+ padding: 5px
+ .pingback-body, .trackback-body
+ padding: 5px
+ .comment-author, .pingback-author, .trackback-author,
+ font-size: 1.2em
+ font-weight: bold
+ color: #666
+
diff --git a/objectapp/static/objectapp/css/src/slider.sass b/objectapp/static/objectapp/css/src/slider.sass
new file mode 100644
index 0000000..7172745
--- /dev/null
+++ b/objectapp/static/objectapp/css/src/slider.sass
@@ -0,0 +1,74 @@
+@import base
+
+$max_width: 630px
+$number_of_items: 5
+
+$slider_width: 400px
+$slider_height: 250px
+$slider_color: $link_color + #444444
+
+$nav_padding: 3px
+$info_padding: 5px
+
+$tab_nav_width: $max_width - $slider_width
+$tab_nav_height: $slider_height / $number_of_items - 1
+$tab_nav_img_height: $tab_nav_height - $nav_padding * 2
+$tab_nav_img_width: $tab_nav_height * $slider_width / $slider_height
+
+#slider
+ width: $slider_width
+ padding-right: $tab_nav_width
+ position: relative
+ height: $slider_height
+ overflow: hidden
+ margin-bottom: 5px
+ ul.ui-tabs-nav
+ list-style: none
+ padding: 0
+ margin: 0
+ top: 0
+ left: $slider_width
+ width: $tab_nav_width
+ position: absolute
+ overflow: hidden
+ li
+ a
+ display: block
+ height: $tab_nav_height
+ border-bottom: 1px solid $box_color
+ img
+ float: left
+ border: 0
+ padding: $nav_padding
+ width: $tab_nav_img_width
+ height: $tab_nav_img_height
+ a:hover
+ text-decoration: none
+ background: $box_color
+ &.ui-tabs-selected
+ a
+ background: $slider_color
+ color: white
+ div.ui-tabs-panel
+ position: relative
+ width: $slider_width
+ height: $slider_height
+ .panel_info
+ left: 0
+ bottom: 0
+ position: absolute
+ height: 70px
+ padding: $info_padding
+ color: white
+ background: url(../img/trans.png)
+ width: $slider_width - $info_padding * 2
+ a
+ color: $slider_color
+ h2
+ margin: 0
+ overflow: hidden
+ img
+ width: $slider_width
+ height: $slider_height
+ .ui-tabs-hide
+ display: none
diff --git a/objectapp/static/objectapp/css/structures.css b/objectapp/static/objectapp/css/structures.css
new file mode 100644
index 0000000..fd44c5a
--- /dev/null
+++ b/objectapp/static/objectapp/css/structures.css
@@ -0,0 +1,662 @@
+
+
+
+/* Body
+------------------------------------------------------------------------------------------------------ */
+
+body {
+ padding: 58px 20px 0;
+ font-family: Arial, sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+body.popup {
+ padding-top: 0;
+}
+
+
+
+/* Container
+------------------------------------------------------------------------------------------------------ */
+
+#container {
+ z-index: 0;
+ position: relative;
+ float: left;
+ clear: both;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+}
+
+
+
+/* Header
+------------------------------------------------------------------------------------------------------ */
+
+#header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1100;
+ padding: 0 20px;
+ width: 100%;
+ height: 30px;
+ font-size: 11px;
+ line-height: 14px;
+ font-weight: bold;
+}
+body.filebrowser.popup #header {
+ display: none;
+}
+
+
+
+/* Branding, Bookmarks & User-Tools
+------------------------------------------------------------------------------------------------------ */
+
+.branding, .admin-title,
+#bookmarks li, #user-tools li {
+ margin: 0;
+ padding: 8px 10px;
+}
+.branding {
+ display: none;
+ position: relative;
+ float: right;
+ width: 10px;
+ background: url('../img/grappelli-icon.png') 50% 50% no-repeat;
+}
+.admin-title {
+ position: relative;
+ float: left;
+ margin: 0 0 0 -20px;
+ padding-left: 20px;
+ padding-right: 20px;
+}
+#header ul li {
+ position: relative;
+ float: left;
+}
+
+
+/* Navigation Menu (UL Navigation-Menu of Admin-Tools) ................................................... */
+
+ul.navigation-menu {
+ position: relative;
+ float: left;
+}
+ul.navigation-menu li {
+ float: none !important;
+}
+ul.navigation-menu>li {
+ position: relative;
+ float: none !important;
+ display: block;
+ margin: 0;
+}
+ul.navigation-menu>li>a {
+ display: block;
+ padding: 8px 10px;
+ font-size: 11px !important;
+}
+ul.navigation-menu li.bookmark,
+ul.navigation-menu li.actions {
+ float: left !important;
+}
+ul.navigation-menu li ul {
+ position: absolute;
+ z-index: 1 !important;
+ float: none !important;
+ margin-top: -1px;
+ padding: 0;
+ min-width: 220px;
+ white-space: nowrap;
+
+/* box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;*/
+}
+ul.navigation-menu>li>a+ul {
+ overflow-x: hidden !important;
+/* padding-right: 20px;*/
+/* width: 500px;*/
+ box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;
+ box-shadow: 0 0 20px #333; -moz-box-shadow: 0 0 20px #333; -webkit-box-shadow: 0 0 20px #333;
+}
+ul.navigation-menu>li>ul>li.parent {
+ overflow-x: hidden !important;
+}
+
+ul.navigation-menu li ul ul {
+ position: relative;
+ float: none;
+ margin-top: 0;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ overflow: inherit;
+}
+
+ul.navigation-menu li li.item-collapse.item-open {
+ background: #3a3a3a;
+}
+ul.navigation-menu li li li.item-collapse.item-open {
+/* border: 1px solid #383838;*/
+ -moz-border-radius: 4px;
+ background: #424242;
+}
+ul.navigation-menu li li li.item-collapse.item-open + li {
+/* border: 0 !important;*/
+}
+ul.navigation-menu li li li li.item-collapse.item-open {
+/* border: 1px solid #404040;*/
+ background: #4a4a4a;
+}
+ul.navigation-menu li li li li.item-collapse.oitem-pen + li {
+/* border: 0 !important;*/
+}
+ul.navigation-menu li li li li li.item-collapse.item-open {
+/* border: 1px solid #484848;*/
+ background: #525252;
+}
+
+
+ul.navigation-menu li li {
+/* padding: 0 10px;*/
+}
+ul.navigation-menu li li li {
+/* margin: 0 -20px 0 -10px;*/
+/* padding: 0 10px 0 20px;*/
+ box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;
+}
+ul.navigation-menu li li li li {
+/* margin: 0 -10px 0 -20px;*/
+/* padding: 0 10px 0 30px;*/
+/* box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;*/
+}
+ul.navigation-menu li li li li li {
+/* margin: 0 -10px 0 -30px;*/
+/* padding: 0 10px 0 40px;*/
+ overflow: hidden;
+/* box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;*/
+}
+ul.navigation-menu li li li li li li {
+/* margin: 0 0 0 -40px;*/
+/* padding: 0 10px 0 50px;*/
+ overflow: hidden;
+/* box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;*/
+}
+
+
+ul.navigation-menu li li.last {
+ border-bottom: 0 !important;
+}
+
+ul.navigation-menu li ul ul>li:first-child a {
+/* margin-left: -10px;*/
+/* padding-left: 10px;*/
+}
+ul.navigation-menu li li a {
+ display: block;
+ padding: 8px 10px;
+ font-size: 11px;
+}
+ul.navigation-menu li li li a {
+ padding: 4px 10px 4px 20px;
+ font-size: 11px;
+ white-space: normal;
+}
+ul.navigation-menu li li li li a {
+ padding-left: 30px;
+}
+ul.navigation-menu li li li li li a {
+ padding-left: 40px;
+}
+ul.navigation-menu li li li li li li a {
+ padding-left: 50px;
+}
+ul.navigation-menu li.parent>a {
+ font-size: 11px;
+}
+ul.navigation-menu li li.parent>a {
+ font-size: 11px;
+}
+
+ul.navigation-menu li.item-collapse.item-closed>* {
+ display: none !important;
+}
+ul.navigation-menu li.item-collapse.item-open>* {
+ display: block !important;
+}
+
+ul.navigation-menu li.item-collapse a.item-collapse-handler-container {
+ display: block !important;
+/* padding: 10px 0 !important;*/
+}
+
+form#bookmark-form {
+ position: relative;
+ float: left;
+ padding: 3px 10px 1px 0;
+ height: 26px;
+}
+form#bookmark-form button {
+ position: relative; display: block;
+ margin: 3px 0 0;
+ width: 20px;
+ height: 20px;
+}
+
+ul.navigation-menu li.item-collapse a.item-collapse-handler {
+ position: relative;
+ float: right;
+ display: inline-block !important;
+ right: 0;
+ margin: -30px 0 -30px 0;
+ padding: 0;
+ width: 28px;
+ height: 30px;
+ cursor: pointer;
+}
+ul.navigation-menu li li li.item-collapse a.item-collapse-handler {
+ margin: -22px 0;
+ width: 28px;
+ height: 22px;
+}
+a.item-collapse-handler-container {
+ padding-right: 38px !important;
+}
+ul.navigation-menu li li.item-collapse.item-closed>a+a.item-collapse-handler:link,
+ul.navigation-menu li li.item-collapse.item-closed>a+a.item-collapse-handler:visited {
+ background: transparent url("../img/icons/icon-admin_tools-dropdown.png") no-repeat scroll 50% 50%;
+}
+ul.navigation-menu li li.item-collapse.item-closed>a+a.item-collapse-handler:hover,
+ul.navigation-menu li li.item-collapse.item-closed>a+a.item-collapse-handler:active {
+ background: transparent url("../img/icons/icon-admin_tools-dropdown-hover.png") no-repeat scroll 50% 50%;
+}
+ul.navigation-menu li li.item-collapse.item-open>a+a.item-collapse-handler:link,
+ul.navigation-menu li li.item-collapse.item-open>a+a.item-collapse-handler:visited {
+ background: transparent url("../img/icons/icon-admin_tools-dropdown-active.png") no-repeat scroll 50% 50%;
+}
+ul.navigation-menu li li.item-collapse.item-open>a+a.item-collapse-handler:hover,
+ul.navigation-menu li li.item-collapse.item-open>a+a.item-collapse-handler:active {
+ background: transparent url("../img/icons/icon-admin_tools-dropdown-active-hover.png") no-repeat scroll 50% 50%;
+}
+
+
+/* User Tools ................................................... */
+
+#user-tools {
+ position: absolute;
+ right: 40px;
+}
+#user-tools>li:last-child {
+ padding-right: 20px;
+}
+
+#user-tools li.user-options-container {
+ position: relative;
+ width: 200px;
+}
+
+li.user-options-container.open a.user-options-handler {
+ display: block;
+}
+ul.user-options {
+ display: none;
+}
+li.user-options-container.open ul.user-options {
+ display: block;
+ position: absolute;
+ float: none;
+ clear: both;
+ z-index: 1000;
+ margin: 7px -10px 0;
+ width: 221px;
+}
+ul.user-options li {
+ float: none !important;
+ clear: both;
+}
+ul.user-options li a {
+ display: block;
+}
+
+
+
+/* Breadcrumbs
+------------------------------------------------------------------------------------------------------ */
+
+div#breadcrumbs {
+ position: fixed;
+ top: 30px;
+ left: 0;
+ z-index: 1000;
+ padding: 5px 10px 5px 20px;
+ width: 100%;
+ font-size: 11px;
+/* font-weight: bold;*/
+ text-align: left;
+}
+div#breadcrumbs > a {
+ padding: 10px 2px;
+}
+body.popup div#breadcrumbs {
+ top: 0;
+}
+
+
+
+/* Messages
+------------------------------------------------------------------------------------------------------ */
+
+ul.messagelist {
+ position: relative;
+ top: 0;
+ z-index: 990;
+ margin: 0 -20px;
+}
+ul.messagelist li {
+ display: block;
+ padding: 5px 10px 5px 20px;
+ font-size: 11px;
+ font-weight: bold;
+}
+body.popup .breadcrumbs + ul.messagelist {
+ top: 24px;
+}
+body.filebrowser.popup ul.messagelist {
+ top: 28px;
+}
+body.login ul.messagelist {
+ top: -28px;
+}
+
+
+/* Masthead
+------------------------------------------------------------------------------------------------------ */
+
+#masthead {
+ position: relative;
+ float: left;
+ clear: both;
+ z-index: 900;
+ padding: 60px 0 10px;
+ width: 100%;
+}
+
+
+
+/* Login Form
+------------------------------------------------------------------------------------------------------ */
+
+div.login {
+ top: -30px;
+}
+#login-form {
+ margin: 0 auto;
+}
+
+
+
+/* Content
+------------------------------------------------------------------------------------------------------ */
+
+#content {
+ position: relative;
+ float: left;
+ clear: both;
+ margin:0 0 80px;
+
+ padding-right: 10px;
+ width: 50%;
+}
+#content.content-flexible {
+ width: 100%;
+}
+body.filebrowser.popup #content {
+ top: 28px;
+}
+
+
+
+/* Container
+------------------------------------------------------------------------------------------------------ */
+
+.container,
+.container-grid {
+ position: relative;
+ float: left;
+ clear: both;
+ width: 940px;
+}
+.container-flexible {
+ position: relative;
+ float: none;
+ clear: both;
+ width: auto;
+ height: 100%;
+}
+
+
+
+/* Blueprint Grid Columns & Spans
+------------------------------------------------------------------------------------------------------ */
+
+.column {
+ position: relative;
+ float: left;
+}
+.column.centered {
+ position: relative;
+ float: none !important;
+ margin: 0 auto !important;
+}
+.span-flexible {
+ position: relative;
+ width: 100%;
+}
+.container-flexible.layout-flexible-grid .span-flexible {
+ float: left;
+ margin-right: 20px;
+ width: 100%;
+}
+.container-flexible.layout-flexible-grid .span-flexible + .column {
+ float: left !important;
+}
+.container-flexible.layout-grid-flexible .column {
+ float: left;
+}
+.container-flexible.layout-grid-flexible .span-flexible {
+ float: left;
+ width: 100%;
+}
+fieldset.module .row .column:first-child {
+ margin-left: 0 !important;
+}
+fieldset.module .row .column:last-child {
+ margin-right: -20px !important;
+}
+fieldset.module .row .column.span-flexible:last-child {
+ margin-right: 0 !important;
+}
+.row .span-flexible,
+.row .span-flexible:last-child {
+ float: none;
+ width: auto;
+ margin-right: 0 !important;
+}
+
+
+/* Basic Float & Margin ......................................... */
+
+.span-1, .span-2, .span-3, .span-4, .span-5, .span-6,
+.span-7, .span-8, .span-9, .span-10, .span-11, .span-12,
+.span-13, .span-14, .span-15, .span-16, .span-17, .span-18,
+.span-19, .span-20, .span-21, .span-22, .span-23, .span-24 {
+/* float: left;*/
+ margin-right: 20px;
+}
+.column.last { margin-right: 0; }
+
+
+/* Column Widths ......................................... */
+
+.span-1 { width: 20px; }
+.span-2 { width: 60px; }
+.span-3 { width: 100px; }
+.span-4 { width: 140px; }
+.span-5 { width: 180px; }
+.span-6 { width: 220px; }
+.span-7 { width: 260px; }
+.span-8 { width: 300px; }
+.span-9 { width: 340px; }
+.span-10 { width: 380px; }
+.span-11 { width: 420px; }
+.span-12 { width: 460px; }
+.span-13 { width: 500px; }
+.span-14 { width: 540px; }
+.span-15 { width: 580px; }
+.span-16 { width: 620px; }
+.span-17 { width: 660px; }
+.span-18 { width: 700px; }
+.span-19 { width: 740px; }
+.span-20 { width: 780px; }
+.span-21 { width: 820px; }
+.span-22 { width: 860px; }
+.span-23 { width: 900px; }
+.span-24 { width: 940px; margin: 0; }
+
+
+/* Append empty columns ......................................... */
+
+.append-1 { padding-right: 40px; }
+.append-2 { padding-right: 80px; }
+.append-3 { padding-right: 120px; }
+.append-4 { padding-right: 160px; }
+.append-5 { padding-right: 200px; }
+.append-6 { padding-right: 240px; }
+.append-7 { padding-right: 280px; }
+.append-8 { padding-right: 320px; }
+.append-9 { padding-right: 360px; }
+.append-10 { padding-right: 400px; }
+.append-11 { padding-right: 440px; }
+.append-12 { padding-right: 480px; }
+.append-13 { padding-right: 520px; }
+.append-14 { padding-right: 560px; }
+.append-15 { padding-right: 600px; }
+.append-16 { padding-right: 640px; }
+.append-17 { padding-right: 680px; }
+.append-18 { padding-right: 720px; }
+.append-19 { padding-right: 760px; }
+.append-20 { padding-right: 800px; }
+.append-21 { padding-right: 840px; }
+.append-22 { padding-right: 880px; }
+.append-23 { padding-right: 920px; }
+
+
+/* Prepend empty columns ......................................... */
+
+.prepend-1 { padding-left: 40px; }
+.prepend-2 { padding-left: 80px; }
+.prepend-3 { padding-left: 120px; }
+.prepend-4 { padding-left: 160px; }
+.prepend-5 { padding-left: 200px; }
+.prepend-6 { padding-left: 240px; }
+.prepend-7 { padding-left: 280px; }
+.prepend-8 { padding-left: 320px; }
+.prepend-9 { padding-left: 360px; }
+.prepend-10 { padding-left: 400px; }
+.prepend-11 { padding-left: 440px; }
+.prepend-12 { padding-left: 480px; }
+.prepend-13 { padding-left: 520px; }
+.prepend-14 { padding-left: 560px; }
+.prepend-15 { padding-left: 600px; }
+.prepend-16 { padding-left: 640px; }
+.prepend-17 { padding-left: 680px; }
+.prepend-18 { padding-left: 720px; }
+.prepend-19 { padding-left: 760px; }
+.prepend-20 { padding-left: 800px; }
+.prepend-21 { padding-left: 840px; }
+.prepend-22 { padding-left: 880px; }
+.prepend-23 { padding-left: 920px; }
+
+
+/* Span-X + Span-Flexible ......................................... */
+
+.span-1 + .span-flexible { margin-left: 40px; }
+.span-2 + .span-flexible { margin-left: 80px; }
+.span-3 + .span-flexible { margin-left: 120px; }
+.span-4 + .span-flexible { margin-left: 160px; Xmin-width: 758px; }
+.span-5 + .span-flexible { margin-left: 200px; }
+.span-6 + .span-flexible { margin-left: 240px; }
+.span-7 + .span-flexible { margin-left: 280px; }
+.span-8 + .span-flexible { margin-left: 320px; }
+.span-9 + .span-flexible { margin-left: 360px; }
+.span-10 + .span-flexible { margin-left: 400px; }
+.span-11 + .span-flexible { margin-left: 440px; }
+.span-12 + .span-flexible { margin-left: 480px; }
+.span-13 + .span-flexible { margin-left: 520px; }
+.span-14 + .span-flexible { margin-left: 560px; }
+.span-15 + .span-flexible { margin-left: 600px; }
+.span-16 + .span-flexible { margin-left: 640px; }
+.span-17 + .span-flexible { margin-left: 680px; }
+.span-18 + .span-flexible { margin-left: 720px; }
+.span-19 + .span-flexible { margin-left: 760px; }
+.span-20 + .span-flexible { margin-left: 800px; }
+.span-21 + .span-flexible { margin-left: 840px; }
+.span-22 + .span-flexible { margin-left: 880px; }
+.span-23 + .span-flexible { margin-left: 920px; }
+.span-24 + .span-flexible { margin-left: 960px; }
+
+
+/* Columns in Cells ......................................... */
+
+.cell.span-1 { width: 0px; }
+.cell.span-2 { width: 40px; }
+.cell.span-3 { width: 80px; }
+.cell.span-4 { width: 120px; }
+.cell.span-5 { width: 160px; }
+.cell.span-6 { width: 200px; }
+.cell.span-7 { width: 240px; }
+.cell.span-8 { width: 280px; }
+.cell.span-9 { width: 330px; }
+.cell.span-10 { width: 360px; }
+.cell.span-11 { width: 400px; }
+.cell.span-12 { width: 440px; }
+.cell.span-13 { width: 480px; }
+.cell.span-14 { width: 520px; }
+.cell.span-15 { width: 560px; }
+.cell.span-16 { width: 600px; }
+.cell.span-17 { width: 640px; }
+.cell.span-18 { width: 680px; }
+.cell.span-19 { width: 720px; }
+.cell.span-20 { width: 760px; }
+.cell.span-21 { width: 800px; }
+.cell.span-22 { width: 840px; }
+.cell.span-23 { width: 880px; }
+.cell.span-24 { width: 920px; margin: 0; }
+
+
+/* Clearing floats without extra markup
+ Based on How To Clear Floats Without Structural Markup by PiE
+ [http://www.positioniseverything.net/easyclearing.html] */
+
+.clearfix:after, .container:after {
+ content: ".";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+.clearfix, .container { display: inline-block; }
+* html .clearfix,
+* html .container { height: 1%; }
+.clearfix, .container { display: block; }
+
+/* Regular clearing
+ apply to column that should drop below previous ones. */
+
+.clear { clear: both; }
+
+
+
diff --git a/objectapp/static/objectapp/css/wymeditor_styles.css b/objectapp/static/objectapp/css/wymeditor_styles.css
new file mode 100644
index 0000000..fb2f569
--- /dev/null
+++ b/objectapp/static/objectapp/css/wymeditor_styles.css
@@ -0,0 +1,45 @@
+/* EDITOR RELATED STYLES - CUSTOMIZE FOR YOUR NEEDS */
+/*
+ The classes panel, the visual feedback and the preview
+ will be affected by these values.
+
+ - Commented styles inside style definitions are used for visual
+ feedback when using the editor.
+ - Comments before opening the style are used as caption.
+ - Comments after the class name and before the style declaration ({)
+ define the jQuery expression that decides whether this
+ style should be applied or not.
+
+ Note: the WYMeditor and /WYMeditor comments below are required
+ for the CSS parser to work properly.
+*/
+
+/* WYMeditor */
+
+/* PARA: Highlight */
+p.highlight {
+ background: yellow;
+ /* background: yellow; border: 2px solid orange; */
+}
+
+/* PARA: Hidden note */
+p.hide {
+ display: none;
+ /* color: #999; border: 2px solid #ccc; */
+}
+
+/* IMG: floating left */
+img.left /* img[@class!="right"] */ {
+ display: inline;
+ float: left;
+ /* float: left; */
+}
+
+/* IMG: floating right */
+img.right /* img[@class!="left"] */ {
+ display: inline;
+ float: right;
+ /* float: right; */
+}
+
+/* /WYMeditor */
diff --git a/objectapp/static/objectapp/img/background.gif b/objectapp/static/objectapp/img/background.gif
new file mode 100644
index 0000000..0f892b1
--- /dev/null
+++ b/objectapp/static/objectapp/img/background.gif
Binary files differ
diff --git a/objectapp/static/objectapp/img/bullet.png b/objectapp/static/objectapp/img/bullet.png
new file mode 100644
index 0000000..3e9c121
--- /dev/null
+++ b/objectapp/static/objectapp/img/bullet.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/comments.png b/objectapp/static/objectapp/img/comments.png
new file mode 100644
index 0000000..64d81a5
--- /dev/null
+++ b/objectapp/static/objectapp/img/comments.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/community.png b/objectapp/static/objectapp/img/community.png
new file mode 100644
index 0000000..bb8ca4a
--- /dev/null
+++ b/objectapp/static/objectapp/img/community.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/favicon.ico b/objectapp/static/objectapp/img/favicon.ico
new file mode 100644
index 0000000..6baafb3
--- /dev/null
+++ b/objectapp/static/objectapp/img/favicon.ico
Binary files differ
diff --git a/objectapp/static/objectapp/img/favicon.png b/objectapp/static/objectapp/img/favicon.png
new file mode 100644
index 0000000..d3269bf
--- /dev/null
+++ b/objectapp/static/objectapp/img/favicon.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/grid.png b/objectapp/static/objectapp/img/grid.png
new file mode 100644
index 0000000..129d4a2
--- /dev/null
+++ b/objectapp/static/objectapp/img/grid.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/help.png b/objectapp/static/objectapp/img/help.png
new file mode 100644
index 0000000..8e49281
--- /dev/null
+++ b/objectapp/static/objectapp/img/help.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/logo.png b/objectapp/static/objectapp/img/logo.png
new file mode 100644
index 0000000..668cbfd
--- /dev/null
+++ b/objectapp/static/objectapp/img/logo.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/manage.png b/objectapp/static/objectapp/img/manage.png
new file mode 100644
index 0000000..807b82d
--- /dev/null
+++ b/objectapp/static/objectapp/img/manage.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/objecttype.png b/objectapp/static/objectapp/img/objecttype.png
new file mode 100644
index 0000000..6647a4b
--- /dev/null
+++ b/objectapp/static/objectapp/img/objecttype.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/plugin.png b/objectapp/static/objectapp/img/plugin.png
new file mode 100644
index 0000000..0b26234
--- /dev/null
+++ b/objectapp/static/objectapp/img/plugin.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/preview.png b/objectapp/static/objectapp/img/preview.png
new file mode 100644
index 0000000..292c578
--- /dev/null
+++ b/objectapp/static/objectapp/img/preview.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/rss.png b/objectapp/static/objectapp/img/rss.png
new file mode 100644
index 0000000..9006ffe
--- /dev/null
+++ b/objectapp/static/objectapp/img/rss.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/shorturl.png b/objectapp/static/objectapp/img/shorturl.png
new file mode 100644
index 0000000..25eacb7
--- /dev/null
+++ b/objectapp/static/objectapp/img/shorturl.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/sitemap.png b/objectapp/static/objectapp/img/sitemap.png
new file mode 100644
index 0000000..092c959
--- /dev/null
+++ b/objectapp/static/objectapp/img/sitemap.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/tags.png b/objectapp/static/objectapp/img/tags.png
new file mode 100644
index 0000000..1478033
--- /dev/null
+++ b/objectapp/static/objectapp/img/tags.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/trans.png b/objectapp/static/objectapp/img/trans.png
new file mode 100644
index 0000000..cd1db6c
--- /dev/null
+++ b/objectapp/static/objectapp/img/trans.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/wlw/comments.png b/objectapp/static/objectapp/img/wlw/comments.png
new file mode 100644
index 0000000..981b1af
--- /dev/null
+++ b/objectapp/static/objectapp/img/wlw/comments.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/wlw/objectapp.png b/objectapp/static/objectapp/img/wlw/objectapp.png
new file mode 100644
index 0000000..d3269bf
--- /dev/null
+++ b/objectapp/static/objectapp/img/wlw/objectapp.png
Binary files differ
diff --git a/objectapp/static/objectapp/img/wlw/watermark.png b/objectapp/static/objectapp/img/wlw/watermark.png
new file mode 100644
index 0000000..09223aa
--- /dev/null
+++ b/objectapp/static/objectapp/img/wlw/watermark.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/jquery.autocomplete.js b/objectapp/static/objectapp/js/jquery.autocomplete.js
new file mode 100644
index 0000000..4b021c1
--- /dev/null
+++ b/objectapp/static/objectapp/js/jquery.autocomplete.js
@@ -0,0 +1,13 @@
+/*
+ * jQuery Autocomplete plugin 1.1
+ *
+ * Copyright (c) 2009 Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
+ */;(function($){$.fn.extend({autocomplete:function(urlOrData,options){var isUrl=typeof urlOrData=="string";options=$.extend({},$.Autocompleter.defaults,{url:isUrl?urlOrData:null,data:isUrl?null:urlOrData,delay:isUrl?$.Autocompleter.defaults.delay:10,max:options&&!options.scroll?10:150},options);options.highlight=options.highlight||function(value){return value;};options.formatMatch=options.formatMatch||options.formatItem;return this.each(function(){new $.Autocompleter(this,options);});},result:function(handler){return this.bind("result",handler);},search:function(handler){return this.trigger("search",[handler]);},flushCache:function(){return this.trigger("flushCache");},setOptions:function(options){return this.trigger("setOptions",[options]);},unautocomplete:function(){return this.trigger("unautocomplete");}});$.Autocompleter=function(input,options){var KEY={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var $input=$(input).attr("autocomplete","off").addClass(options.inputClass);var timeout;var previousValue="";var cache=$.Autocompleter.Cache(options);var hasFocus=0;var lastKeyPressCode;var config={mouseDownOnSelect:false};var select=$.Autocompleter.Select(options,input,selectCurrent,config);var blockSubmit;$.browser.opera&&$(input.form).bind("submit.autocomplete",function(){if(blockSubmit){blockSubmit=false;return false;}});$input.bind(($.browser.opera?"keypress":"keydown")+".autocomplete",function(event){hasFocus=1;lastKeyPressCode=event.keyCode;switch(event.keyCode){case KEY.UP:event.preventDefault();if(select.visible()){select.prev();}else{onChange(0,true);}break;case KEY.DOWN:event.preventDefault();if(select.visible()){select.next();}else{onChange(0,true);}break;case KEY.PAGEUP:event.preventDefault();if(select.visible()){select.pageUp();}else{onChange(0,true);}break;case KEY.PAGEDOWN:event.preventDefault();if(select.visible()){select.pageDown();}else{onChange(0,true);}break;case options.multiple&&$.trim(options.multipleSeparator)==","&&KEY.COMMA:case KEY.TAB:case KEY.RETURN:if(selectCurrent()){event.preventDefault();blockSubmit=true;return false;}break;case KEY.ESC:select.hide();break;default:clearTimeout(timeout);timeout=setTimeout(onChange,options.delay);break;}}).focus(function(){hasFocus++;}).blur(function(){hasFocus=0;if(!config.mouseDownOnSelect){hideResults();}}).click(function(){if(hasFocus++>1&&!select.visible()){onChange(0,true);}}).bind("search",function(){var fn=(arguments.length>1)?arguments[1]:null;function findValueCallback(q,data){var result;if(data&&data.length){for(var i=0;i<data.length;i++){if(data[i].result.toLowerCase()==q.toLowerCase()){result=data[i];break;}}}if(typeof fn=="function")fn(result);else $input.trigger("result",result&&[result.data,result.value]);}$.each(trimWords($input.val()),function(i,value){request(value,findValueCallback,findValueCallback);});}).bind("flushCache",function(){cache.flush();}).bind("setOptions",function(){$.extend(options,arguments[1]);if("data"in arguments[1])cache.populate();}).bind("unautocomplete",function(){select.unbind();$input.unbind();$(input.form).unbind(".autocomplete");});function selectCurrent(){var selected=select.selected();if(!selected)return false;var v=selected.result;previousValue=v;if(options.multiple){var words=trimWords($input.val());if(words.length>1){var seperator=options.multipleSeparator.length;var cursorAt=$(input).selection().start;var wordAt,progress=0;$.each(words,function(i,word){progress+=word.length;if(cursorAt<=progress){wordAt=i;return false;}progress+=seperator;});words[wordAt]=v;v=words.join(options.multipleSeparator);}v+=options.multipleSeparator;}$input.val(v);hideResultsNow();$input.trigger("result",[selected.data,selected.value]);return true;}function onChange(crap,skipPrevCheck){if(lastKeyPressCode==KEY.DEL){select.hide();return;}var currentValue=$input.val();if(!skipPrevCheck&&currentValue==previousValue)return;previousValue=currentValue;currentValue=lastWord(currentValue);if(currentValue.length>=options.minChars){$input.addClass(options.loadingClass);if(!options.matchCase)currentValue=currentValue.toLowerCase();request(currentValue,receiveData,hideResultsNow);}else{stopLoading();select.hide();}};function trimWords(value){if(!value)return[""];if(!options.multiple)return[$.trim(value)];return $.map(value.split(options.multipleSeparator),function(word){return $.trim(value).length?$.trim(word):null;});}function lastWord(value){if(!options.multiple)return value;var words=trimWords(value);if(words.length==1)return words[0];var cursorAt=$(input).selection().start;if(cursorAt==value.length){words=trimWords(value)}else{words=trimWords(value.replace(value.substring(cursorAt),""));}return words[words.length-1];}function autoFill(q,sValue){if(options.autoFill&&(lastWord($input.val()).toLowerCase()==q.toLowerCase())&&lastKeyPressCode!=KEY.BACKSPACE){$input.val($input.val()+sValue.substring(lastWord(previousValue).length));$(input).selection(previousValue.length,previousValue.length+sValue.length);}};function hideResults(){clearTimeout(timeout);timeout=setTimeout(hideResultsNow,200);};function hideResultsNow(){var wasVisible=select.visible();select.hide();clearTimeout(timeout);stopLoading();if(options.mustMatch){$input.search(function(result){if(!result){if(options.multiple){var words=trimWords($input.val()).slice(0,-1);$input.val(words.join(options.multipleSeparator)+(words.length?options.multipleSeparator:""));}else{$input.val("");$input.trigger("result",null);}}});}};function receiveData(q,data){if(data&&data.length&&hasFocus){stopLoading();select.display(data,q);autoFill(q,data[0].value);select.show();}else{hideResultsNow();}};function request(term,success,failure){if(!options.matchCase)term=term.toLowerCase();var data=cache.load(term);if(data&&data.length){success(term,data);}else if((typeof options.url=="string")&&(options.url.length>0)){var extraParams={timestamp:+new Date()};$.each(options.extraParams,function(key,param){extraParams[key]=typeof param=="function"?param():param;});$.ajax({mode:"abort",port:"autocomplete"+input.name,dataType:options.dataType,url:options.url,data:$.extend({q:lastWord(term),limit:options.max},extraParams),success:function(data){var parsed=options.parse&&options.parse(data)||parse(data);cache.add(term,parsed);success(term,parsed);}});}else{select.emptyList();failure(term);}};function parse(data){var parsed=[];var rows=data.split("\n");for(var i=0;i<rows.length;i++){var row=$.trim(rows[i]);if(row){row=row.split("|");parsed[parsed.length]={data:row,value:row[0],result:options.formatResult&&options.formatResult(row,row[0])||row[0]};}}return parsed;};function stopLoading(){$input.removeClass(options.loadingClass);};};$.Autocompleter.defaults={inputClass:"ac_input",resultsClass:"ac_results",loadingClass:"ac_loading",minChars:1,delay:400,matchCase:false,matchSubset:true,matchContains:false,cacheLength:10,max:100,mustMatch:false,extraParams:{},selectFirst:true,formatItem:function(row){return row[0];},formatMatch:null,autoFill:false,width:0,multiple:false,multipleSeparator:", ",highlight:function(value,term){return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"<strong>$1</strong>");},scroll:true,scrollHeight:180};$.Autocompleter.Cache=function(options){var data={};var length=0;function matchSubset(s,sub){if(!options.matchCase)s=s.toLowerCase();var i=s.indexOf(sub);if(options.matchContains=="word"){i=s.toLowerCase().search("\\b"+sub.toLowerCase());}if(i==-1)return false;return i==0||options.matchContains;};function add(q,value){if(length>options.cacheLength){flush();}if(!data[q]){length++;}data[q]=value;}function populate(){if(!options.data)return false;var stMatchSets={},nullData=0;if(!options.url)options.cacheLength=1;stMatchSets[""]=[];for(var i=0,ol=options.data.length;i<ol;i++){var rawValue=options.data[i];rawValue=(typeof rawValue=="string")?[rawValue]:rawValue;var value=options.formatMatch(rawValue,i+1,options.data.length);if(value===false)continue;var firstChar=value.charAt(0).toLowerCase();if(!stMatchSets[firstChar])stMatchSets[firstChar]=[];var row={value:value,data:rawValue,result:options.formatResult&&options.formatResult(rawValue)||value};stMatchSets[firstChar].push(row);if(nullData++<options.max){stMatchSets[""].push(row);}};$.each(stMatchSets,function(i,value){options.cacheLength++;add(i,value);});}setTimeout(populate,25);function flush(){data={};length=0;}return{flush:flush,add:add,populate:populate,load:function(q){if(!options.cacheLength||!length)return null;if(!options.url&&options.matchContains){var csub=[];for(var k in data){if(k.length>0){var c=data[k];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub.push(x);}});}}return csub;}else
+if(data[q]){return data[q];}else
+if(options.matchSubset){for(var i=q.length-1;i>=options.minChars;i--){var c=data[q.substr(0,i)];if(c){var csub=[];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub[csub.length]=x;}});return csub;}}}return null;}};};$.Autocompleter.Select=function(options,input,select,config){var CLASSES={ACTIVE:"ac_over"};var listItems,active=-1,data,term="",needsInit=true,element,list;function init(){if(!needsInit)return;element=$("<div/>").hide().addClass(options.resultsClass).css("position","absolute").appendTo(document.body);list=$("<ul/>").appendTo(element).mouseover(function(event){if(target(event).nodeName&&target(event).nodeName.toUpperCase()=='LI'){active=$("li",list).removeClass(CLASSES.ACTIVE).index(target(event));$(target(event)).addClass(CLASSES.ACTIVE);}}).click(function(event){$(target(event)).addClass(CLASSES.ACTIVE);select();input.focus();return false;}).mousedown(function(){config.mouseDownOnSelect=true;}).mouseup(function(){config.mouseDownOnSelect=false;});if(options.width>0)element.css("width",options.width);needsInit=false;}function target(event){var element=event.target;while(element&&element.tagName!="LI")element=element.parentNode;if(!element)return[];return element;}function moveSelect(step){listItems.slice(active,active+1).removeClass(CLASSES.ACTIVE);movePosition(step);var activeItem=listItems.slice(active,active+1).addClass(CLASSES.ACTIVE);if(options.scroll){var offset=0;listItems.slice(0,active).each(function(){offset+=this.offsetHeight;});if((offset+activeItem[0].offsetHeight-list.scrollTop())>list[0].clientHeight){list.scrollTop(offset+activeItem[0].offsetHeight-list.innerHeight());}else if(offset<list.scrollTop()){list.scrollTop(offset);}}};function movePosition(step){active+=step;if(active<0){active=listItems.size()-1;}else if(active>=listItems.size()){active=0;}}function limitNumberOfItems(available){return options.max&&options.max<available?options.max:available;}function fillList(){list.empty();var max=limitNumberOfItems(data.length);for(var i=0;i<max;i++){if(!data[i])continue;var formatted=options.formatItem(data[i].data,i+1,max,data[i].value,term);if(formatted===false)continue;var li=$("<li/>").html(options.highlight(formatted,term)).addClass(i%2==0?"ac_even":"ac_odd").appendTo(list)[0];$.data(li,"ac_data",data[i]);}listItems=list.find("li");if(options.selectFirst){listItems.slice(0,1).addClass(CLASSES.ACTIVE);active=0;}if($.fn.bgiframe)list.bgiframe();}return{display:function(d,q){init();data=d;term=q;fillList();},next:function(){moveSelect(1);},prev:function(){moveSelect(-1);},pageUp:function(){if(active!=0&&active-8<0){moveSelect(-active);}else{moveSelect(-8);}},pageDown:function(){if(active!=listItems.size()-1&&active+8>listItems.size()){moveSelect(listItems.size()-1-active);}else{moveSelect(8);}},hide:function(){element&&element.hide();listItems&&listItems.removeClass(CLASSES.ACTIVE);active=-1;},visible:function(){return element&&element.is(":visible");},current:function(){return this.visible()&&(listItems.filter("."+CLASSES.ACTIVE)[0]||options.selectFirst&&listItems[0]);},show:function(){var offset=$(input).offset();element.css({width:typeof options.width=="string"||options.width>0?options.width:$(input).width(),top:offset.top+input.offsetHeight,left:offset.left}).show();if(options.scroll){list.scrollTop(0);list.css({maxHeight:options.scrollHeight,overflow:'auto'});if($.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var listHeight=0;listItems.each(function(){listHeight+=this.offsetHeight;});var scrollbarsVisible=listHeight>options.scrollHeight;list.css('height',scrollbarsVisible?options.scrollHeight:listHeight);if(!scrollbarsVisible){listItems.width(list.width()-parseInt(listItems.css("padding-left"))-parseInt(listItems.css("padding-right")));}}}},selected:function(){var selected=listItems&&listItems.filter("."+CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);return selected&&selected.length&&$.data(selected[0],"ac_data");},emptyList:function(){list&&list.empty();},unbind:function(){element&&element.remove();}};};$.fn.selection=function(start,end){if(start!==undefined){return this.each(function(){if(this.createTextRange){var selRange=this.createTextRange();if(end===undefined||start==end){selRange.move("character",start);selRange.select();}else{selRange.collapse(true);selRange.moveStart("character",start);selRange.moveEnd("character",end);selRange.select();}}else if(this.setSelectionRange){this.setSelectionRange(start,end);}else if(this.selectionStart){this.selectionStart=start;this.selectionEnd=end;}});}var field=this[0];if(field.createTextRange){var range=document.selection.createRange(),orig=field.value,teststring="<->",textLength=range.text.length;range.text=teststring;var caretAt=field.value.indexOf(teststring);field.value=orig;this.selection(caretAt,caretAt+textLength);return{start:caretAt,end:caretAt+textLength}}else if(field.selectionStart!==undefined){return{start:field.selectionStart,end:field.selectionEnd}}};})(jQuery); \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/jquery.bgiframe.js b/objectapp/static/objectapp/js/jquery.bgiframe.js
new file mode 100644
index 0000000..7faef4b
--- /dev/null
+++ b/objectapp/static/objectapp/js/jquery.bgiframe.js
@@ -0,0 +1,10 @@
+/* Copyright (c) 2006 Brandon Aaron (http://brandonaaron.net)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * $LastChangedDate: 2007-07-22 01:45:56 +0200 (Son, 22 Jul 2007) $
+ * $Rev: 2447 $
+ *
+ * Version 2.1.1
+ */
+(function($){$.fn.bgIframe=$.fn.bgiframe=function(s){if($.browser.msie&&/6.0/.test(navigator.userAgent)){s=$.extend({top:'auto',left:'auto',width:'auto',height:'auto',opacity:true,src:'javascript:false;'},s||{});var prop=function(n){return n&&n.constructor==Number?n+'px':n;},html='<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+'style="display:block;position:absolute;z-index:-1;'+(s.opacity!==false?'filter:Alpha(Opacity=\'0\');':'')+'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+'"/>';return this.each(function(){if($('> iframe.bgiframe',this).length==0)this.insertBefore(document.createElement(html),this.firstChild);});}return this;};})(jQuery); \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/jquery.js b/objectapp/static/objectapp/js/jquery.js
new file mode 100644
index 0000000..8f3ca2e
--- /dev/null
+++ b/objectapp/static/objectapp/js/jquery.js
@@ -0,0 +1,167 @@
+/*!
+ * jQuery JavaScript Library v1.4.4
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Nov 11 19:04:53 2010 -0500
+ */
+(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
+h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;k<J.length;k++){h=J[k];h.origType.replace(X,"")===a.type?f.push(h.selector):J.splice(k--,1)}f=c(a.target).closest(f,a.currentTarget);o=0;for(x=f.length;o<x;o++){r=f[o];for(k=0;k<J.length;k++){h=J[k];if(r.selector===h.selector&&(!A||A.test(h.namespace))){l=r.elem;e=null;if(h.preType==="mouseenter"||
+h.preType==="mouseleave"){a.type=h.preType;e=c(a.relatedTarget).closest(h.selector)[0]}if(!e||e!==l)C.push({elem:l,handleObj:h,level:r.level})}}}o=0;for(x=C.length;o<x;o++){f=C[o];if(d&&f.level>d)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
+"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
+e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
+"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
+s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
+j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
+toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
+-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
+if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K<Q;K++)if((j=arguments[K])!=null)for(s in j){v=G[s];z=j[s];if(G!==z)if(ga&&z&&(b.isPlainObject(z)||(H=b.isArray(z)))){if(H){H=false;v=v&&b.isArray(v)?v:[]}else v=v&&b.isPlainObject(v)?v:{};G[s]=b.extend(ga,v,z)}else if(z!==B)G[s]=z}return G};b.extend({noConflict:function(j){E.$=e;if(j)E.jQuery=d;return b},isReady:false,readyWait:1,ready:function(j){j===true&&b.readyWait--;
+if(!b.readyWait||j!==true&&!b.isReady){if(!t.body)return setTimeout(b.ready,1);b.isReady=true;if(!(j!==true&&--b.readyWait>0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
+b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
+!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
+l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H<G;){if(s.apply(j[H++],v)===false)break}else if(K)for(z in j){if(s.call(j[z],
+z,j[z])===false)break}else for(v=j[0];H<G&&s.call(v,H,v)!==false;v=j[++H]);return j},trim:O?function(j){return j==null?"":O.call(j)}:function(j){return j==null?"":j.toString().replace(k,"").replace(o,"")},makeArray:function(j,s){var v=s||[];if(j!=null){var z=b.type(j);j.length==null||z==="string"||z==="function"||z==="regexp"||b.isWindow(j)?M.call(v,j):b.merge(v,j)}return v},inArray:function(j,s){if(s.indexOf)return s.indexOf(j);for(var v=0,z=s.length;v<z;v++)if(s[v]===j)return v;return-1},merge:function(j,
+s){var v=j.length,z=0;if(typeof s.length==="number")for(var H=s.length;z<H;z++)j[v++]=s[z];else for(;s[z]!==B;)j[v++]=s[z++];j.length=v;return j},grep:function(j,s,v){var z=[],H;v=!!v;for(var G=0,K=j.length;G<K;G++){H=!!s(j[G],G);v!==H&&z.push(j[G])}return z},map:function(j,s,v){for(var z=[],H,G=0,K=j.length;G<K;G++){H=s(j[G],G,v);if(H!=null)z[z.length]=H}return z.concat.apply([],z)},guid:1,proxy:function(j,s,v){if(arguments.length===2)if(typeof s==="string"){v=j;j=v[s];s=B}else if(s&&!b.isFunction(s)){v=
+s;s=B}if(!s&&j)s=function(){return j.apply(v||this,arguments)};if(j)s.guid=j.guid=j.guid||s.guid||b.guid++;return s},access:function(j,s,v,z,H,G){var K=j.length;if(typeof s==="object"){for(var Q in s)b.access(j,Q,s[Q],z,H,v);return j}if(v!==B){z=!G&&z&&b.isFunction(v);for(Q=0;Q<K;Q++)H(j[Q],s,z?v.call(j[Q],Q,H(j[Q],s)):v,G);return j}return K?H(j[0],s):B},now:function(){return(new Date).getTime()},uaMatch:function(j){j=j.toLowerCase();j=L.exec(j)||g.exec(j)||i.exec(j)||j.indexOf("compatible")<0&&n.exec(j)||
+[];return{browser:j[1]||"",version:j[2]||"0"}},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(j,s){R["[object "+s+"]"]=s.toLowerCase()});m=b.uaMatch(m);if(m.browser){b.browser[m.browser]=true;b.browser.version=m.version}if(b.browser.webkit)b.browser.safari=true;if(D)b.inArray=function(j,s){return D.call(s,j)};if(!/\s/.test("\u00a0")){k=/^[\s\xA0]+/;o=/[\s\xA0]+$/}f=b(t);if(t.addEventListener)u=function(){t.removeEventListener("DOMContentLoaded",u,
+false);b.ready()};else if(t.attachEvent)u=function(){if(t.readyState==="complete"){t.detachEvent("onreadystatechange",u);b.ready()}};return E.jQuery=E.$=b}();(function(){c.support={};var a=t.documentElement,b=t.createElement("script"),d=t.createElement("div"),e="script"+c.now();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
+k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
+scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
+1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="<div style='width:4px;'></div>";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
+"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
+c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
+else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h<l;h++){f=e[h].name;if(f.indexOf("data-")===0){f=f.substr(5);ka(this[0],f,d[f])}}}return d}else if(typeof a==="object")return this.each(function(){c.data(this,
+a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(b===B){d=this.triggerHandler("getData"+k[1]+"!",[k[0]]);if(d===B&&this.length){d=c.data(this[0],a);d=ka(this[0],a,d)}return d===B&&k[1]?this.data(k[0]):d}else return this.each(function(){var o=c(this),x=[k[0],b];o.triggerHandler("setData"+k[1]+"!",x);c.data(this,a,b);o.triggerHandler("changeData"+k[1]+"!",x)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var e=
+c.data(a,b);if(!d)return e||[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===B)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,
+a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var sa=/[\n\t]/g,ha=/\s+/,Sa=/\r/g,Ta=/^(?:href|src|style)$/,Ua=/^(?:button|input)$/i,Va=/^(?:button|input|object|select|textarea)$/i,Wa=/^a(?:rea)?$/i,ta=/^(?:radio|checkbox)$/i;c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",
+colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};c.fn.extend({attr:function(a,b){return c.access(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(x){var r=c(this);r.addClass(a.call(this,x,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===
+1)if(f.className){for(var h=" "+f.className+" ",l=f.className,k=0,o=b.length;k<o;k++)if(h.indexOf(" "+b[k]+" ")<0)l+=" "+b[k];f.className=c.trim(l)}else f.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var x=c(this);x.removeClass(a.call(this,o,x.attr("class")))});if(a&&typeof a==="string"||a===B)for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1&&f.className)if(a){for(var h=(" "+f.className+" ").replace(sa," "),
+l=0,k=b.length;l<k;l++)h=h.replace(" "+b[l]+" "," ");f.className=c.trim(h)}else f.className=""}return this},toggleClass:function(a,b){var d=typeof a,e=typeof b==="boolean";if(c.isFunction(a))return this.each(function(f){var h=c(this);h.toggleClass(a.call(this,f,h.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var f,h=0,l=c(this),k=b,o=a.split(ha);f=o[h++];){k=e?k:!l.hasClass(f);l[k?"addClass":"removeClass"](f)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,
+"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(sa," ").indexOf(a)>-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";
+if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h<e;h++){var l=f[h];if(l.selected&&(c.support.optDisabled?!l.disabled:l.getAttribute("disabled")===null)&&(!l.parentNode.disabled||!c.nodeName(l.parentNode,"optgroup"))){a=c(l).val();if(b)return a;d.push(a)}}return d}if(ta.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Sa,"")}return B}var k=c.isFunction(a);return this.each(function(o){var x=c(this),r=a;if(this.nodeType===1){if(k)r=
+a.call(this,o,x.val());if(r==null)r="";else if(typeof r==="number")r+="";else if(c.isArray(r))r=c.map(r,function(C){return C==null?"":C+""});if(c.isArray(r)&&ta.test(this.type))this.checked=c.inArray(x.val(),r)>=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},
+attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&
+b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0};
+c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,
+arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid=
+d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+
+c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h<A.length;h++){C=A[h];if(d.guid===C.guid){if(k||x.test(C.namespace)){e==null&&A.splice(h--,1);r.remove&&r.remove.call(a,C)}if(e!=null)break}}if(A.length===0||e!=null&&A.length===1){if(!r.teardown||r.teardown.call(a,o)===false)c.removeEvent(a,f,w.handle);delete I[f]}}else for(h=0;h<A.length;h++){C=A[h];if(k||x.test(C.namespace)){c.event.remove(a,r,C.handler,h);A.splice(h--,1)}}}if(c.isEmptyObject(I)){if(b=
+w.handle)b.elem=null;delete w.events;delete w.handle;if(typeof w==="function")c.removeData(a,J);else c.isEmptyObject(w)&&c.removeData(a)}}}}},trigger:function(a,b,d,e){var f=a.type||a;if(!e){a=typeof a==="object"?a[c.expando]?a:c.extend(c.Event(f),a):c.Event(f);if(f.indexOf("!")>=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
+8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k===
+"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+
+d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f<l;f++){var k=d[f];if(b||e.test(k.namespace)){a.handler=k.handler;a.data=k.data;a.handleObj=k;k=k.handler.apply(this,h);if(k!==B){a.result=k;if(k===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[c.expando])return a;var b=a;a=c.Event(b);for(var d=this.props.length,e;d;){e=this.props[--d];a[e]=b[e]}if(!a.target)a.target=a.srcElement||t;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=t.documentElement;d=t.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(a.which==null&&(a.charCode!=null||a.keyCode!=null))a.which=a.charCode!=null?a.charCode:a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==B)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,Y(a.origType,a.selector),c.extend({},a,{handler:Ka,guid:a.handler.guid}))},remove:function(a){c.event.remove(this,
+Y(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,d){if(c.isWindow(this))this.onbeforeunload=d},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.removeEvent=t.removeEventListener?function(a,b,d){a.removeEventListener&&a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent&&a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=
+c.now();this[c.expando]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ca;var a=this.originalEvent;if(a)if(a.preventDefault)a.preventDefault();else a.returnValue=false},stopPropagation:function(){this.isPropagationStopped=ca;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ca;this.stopPropagation()},isDefaultPrevented:U,isPropagationStopped:U,isImmediatePropagationStopped:U};
+var va=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},wa=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?wa:va,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?wa:va)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(){if(this.nodeName.toLowerCase()!==
+"form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length){a.liveFired=B;return la("submit",this,arguments)}});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13){a.liveFired=B;return la("submit",this,arguments)}})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var V,
+xa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired=
+B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type===
+"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]===
+0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h<k;h++)c.event.add(this[h],d,l,e)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault)for(var d in a)this.unbind(d,
+a[d]);else{d=0;for(var e=this.length;d<e;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,e){return this.live(b,d,e,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var d=c.Event(a);d.preventDefault();d.stopPropagation();c.event.trigger(d,b,this[0]);return d.result}},toggle:function(a){for(var b=arguments,d=
+1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(e){var f=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,f+1);e.preventDefault();return b[f].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var ya={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,e,f,h){var l,k=0,o,x,r=h||this.selector;h=h?this:c(this.context);if(typeof d===
+"object"&&!d.preventDefault){for(l in d)h[b](l,e,d[l],r);return this}if(c.isFunction(e)){f=e;e=B}for(d=(d||"").split(" ");(l=d[k++])!=null;){o=X.exec(l);x="";if(o){x=o[0];l=l.replace(X,"")}if(l==="hover")d.push("mouseenter"+x,"mouseleave"+x);else{o=l;if(l==="focus"||l==="blur"){d.push(ya[l]+x);l+=x}else l=(ya[l]||l)+x;if(b==="live"){x=0;for(var A=h.length;x<A;x++)c.event.add(h[x],"live."+Y(l,r),{data:e,selector:r,handler:f,origType:l,origHandler:f,preType:o})}else h.unbind("live."+Y(l,r),f)}}return this}});
+c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d,e){if(e==null){e=d;d=null}return arguments.length>0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
+(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1&&!q){y.sizcache=n;y.sizset=p}if(y.nodeName.toLowerCase()===i){F=y;break}y=y[g]}m[p]=F}}}function b(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1){if(!q){y.sizcache=n;y.sizset=p}if(typeof i!=="string"){if(y===i){F=true;break}}else if(k.filter(i,
+[y]).length>0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3];
+break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr,
+q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h=
+l;g.sort(w);if(h)for(var i=1;i<g.length;i++)g[i]===g[i-1]&&g.splice(i--,1)}return g};k.matches=function(g,i){return k(g,null,null,i)};k.matchesSelector=function(g,i){return k(i,null,null,[g]).length>0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p<q;p++){var u,y=o.order[p];if(u=o.leftMatch[y].exec(g)){var F=u[1];u.splice(1,1);if(F.substr(F.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");m=o.find[y](u,i,n);if(m!=null){g=g.replace(o.match[y],"");break}}}}m||(m=i.getElementsByTagName("*"));
+return{set:m,expr:g}};k.filter=function(g,i,n,m){for(var p,q,u=g,y=[],F=i,M=i&&i[0]&&k.isXML(i[0]);g&&i.length;){for(var N in o.filter)if((p=o.leftMatch[N].exec(g))!=null&&p[2]){var O,D,R=o.filter[N];D=p[1];q=false;p.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(F===y)y=[];if(o.preFilter[N])if(p=o.preFilter[N](p,F,n,y,m,M)){if(p===true)continue}else q=O=true;if(p)for(var j=0;(D=F[j])!=null;j++)if(D){O=R(D,p,j,F);var s=m^!!O;if(n&&O!=null)if(s)q=true;else F[j]=false;else if(s){y.push(D);q=true}}if(O!==
+B){n||(F=y);g=g.replace(o.match[N],"");if(!q)return[];break}}}if(g===u)if(q==null)k.error(g);else break;u=g}return F};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var o=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
+POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,i){var n=typeof i==="string",m=n&&!/\W/.test(i);n=n&&!m;if(m)i=i.toLowerCase();m=0;for(var p=g.length,q;m<p;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=n||q&&q.nodeName.toLowerCase()===
+i?q||false:q===i}n&&k.filter(i,g,true)},">":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p<q;p++){if(n=g[p]){n=n.parentNode;g[p]=n.nodeName.toLowerCase()===i?n:false}}else{for(;p<q;p++)if(n=g[p])g[p]=m?n.parentNode:n.parentNode===i;m&&k.filter(i,g,true)}},"":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=i=i.toLowerCase();q=a}q("parentNode",i,p,g,m,n)},"~":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=
+i=i.toLowerCase();q=a}q("previousSibling",i,p,g,m,n)}},find:{ID:function(g,i,n){if(typeof i.getElementById!=="undefined"&&!n)return(g=i.getElementById(g[1]))&&g.parentNode?[g]:[]},NAME:function(g,i){if(typeof i.getElementsByName!=="undefined"){for(var n=[],m=i.getElementsByName(g[1]),p=0,q=m.length;p<q;p++)m[p].getAttribute("name")===g[1]&&n.push(m[p]);return n.length===0?null:n}},TAG:function(g,i){return i.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,i,n,m,p,q){g=" "+g[1].replace(/\\/g,
+"")+" ";if(q)return g;q=0;for(var u;(u=i[q])!=null;q++)if(u)if(p^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
+m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
+true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
+g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return i<n[3]-0},gt:function(g,i,n){return i>n[3]-0},nth:function(g,i,n){return n[3]-
+0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n<m;n++)if(i[n]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+p)},CHILD:function(g,i){var n=i[1],m=g;switch(n){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(n===
+"first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":n=i[2];var p=i[3];if(n===1&&p===0)return true;var q=i[0],u=g.parentNode;if(u&&(u.sizcache!==q||!g.nodeIndex)){var y=0;for(m=u.firstChild;m;m=m.nextSibling)if(m.nodeType===1)m.nodeIndex=++y;u.sizcache=q}m=g.nodeIndex-p;return n===0?m===0:m%n===0&&m/n>=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
+i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]];
+if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m,
+g);else if(typeof g.length==="number")for(var p=g.length;n<p;n++)m.push(g[n]);else for(;g[n];n++)m.push(g[n]);return m}}var w,I;if(t.documentElement.compareDocumentPosition)w=function(g,i){if(g===i){h=true;return 0}if(!g.compareDocumentPosition||!i.compareDocumentPosition)return g.compareDocumentPosition?-1:1;return g.compareDocumentPosition(i)&4?-1:1};else{w=function(g,i){var n,m,p=[],q=[];n=g.parentNode;m=i.parentNode;var u=n;if(g===i){h=true;return 0}else if(n===m)return I(g,i);else if(n){if(!m)return 1}else return-1;
+for(;u;){p.unshift(u);u=u.parentNode}for(u=m;u;){q.unshift(u);u=u.parentNode}n=p.length;m=q.length;for(u=0;u<n&&u<m;u++)if(p[u]!==q[u])return I(p[u],q[u]);return u===n?I(g,q[u],-1):I(p[u],i,1)};I=function(g,i,n){if(g===i)return n;for(g=g.nextSibling;g;){if(g===i)return-1;g=g.nextSibling}return 1}}k.getText=function(g){for(var i="",n,m=0;g[m];m++){n=g[m];if(n.nodeType===3||n.nodeType===4)i+=n.nodeValue;else if(n.nodeType!==8)i+=k.getText(n.childNodes)}return i};(function(){var g=t.createElement("div"),
+i="script"+(new Date).getTime(),n=t.documentElement;g.innerHTML="<a name='"+i+"'/>";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g);
+n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&&
+function(){var g=k,i=t.createElement("div");i.innerHTML="<p class='TEST'></p>";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F||
+p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g=
+t.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition?
+function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n<u;n++)k(g,q[n],m);return k.filter(p,m)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=k.getText;c.isXMLDoc=k.isXML;
+c.contains=k.contains})();var Za=/Until$/,$a=/^(?:parents|prevUntil|prevAll)/,ab=/,/,Na=/^.[^:#\[\.,]*$/,bb=Array.prototype.slice,cb=c.expr.match.POS;c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,e=0,f=this.length;e<f;e++){d=b.length;c.find(a,this[e],b);if(e>0)for(var h=d;h<b.length;h++)for(var l=0;l<d;l++)if(b[l]===b[h]){b.splice(h--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,e=b.length;d<e;d++)if(c.contains(this,b[d]))return true})},
+not:function(a){return this.pushStack(ma(this,a,false),"not",a)},filter:function(a){return this.pushStack(ma(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e<f;e++){l=a[e];k[l]||(k[l]=c.expr.match.POS.test(l)?c(l,b||this.context):l)}for(;h&&h.ownerDocument&&h!==b;){for(l in k){e=k[l];if(e.jquery?e.index(h)>-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h=
+h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e<f;e++)for(h=this[e];h;)if(l?l.index(h)>-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):
+c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,
+2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,
+b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&
+e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/<tbody/i,eb=/<|&#?\w+;/,Ca=/<(?:script|object|embed|option|style)/i,Da=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/\=([^="'>\s]+\/)>/g,P={option:[1,
+"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null;
+else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1></$2>");try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(e){this.empty().append(a)}}else c.isFunction(a)?this.each(function(f){var h=c(this);h.html(a.call(this,f,h.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=
+c(this),e=d.html();d.replaceWith(a.call(this,b,e))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){var e,f,h,l=a[0],k=[];if(!c.support.checkClone&&arguments.length===3&&typeof l==="string"&&Da.test(l))return this.each(function(){c(this).domManip(a,
+b,d,true)});if(c.isFunction(l))return this.each(function(x){var r=c(this);a[0]=l.call(this,x,b?r.html():B);r.domManip(a,b,d)});if(this[0]){e=l&&l.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:c.buildFragment(a,this,k);h=e.fragment;if(f=h.childNodes.length===1?h=h.firstChild:h.firstChild){b=b&&c.nodeName(f,"tr");f=0;for(var o=this.length;f<o;f++)d.call(b?c.nodeName(this[f],"table")?this[f].getElementsByTagName("tbody")[0]||this[f].appendChild(this[f].ownerDocument.createElement("tbody")):
+this[f]:this[f],f>0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",
+prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f<h;f++){var l=(f>0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument||
+b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1></$2>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]==="<table>"&&!x?r.childNodes:[];for(o=k.length-
+1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
+d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
+jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
+zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
+h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
+if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
+d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
+e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b===
+"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("<div>").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&
+!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})},
+getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html",
+script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data||
+!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache=
+false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset;
+A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type",
+b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&&
+c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d||
+c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]=
+encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",
+[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),
+e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});
+if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",
+3),a,b,d);else{d=0;for(var e=this.length;d<e;d++){a=this[d];b=a.style.display;if(!c.data(a,"olddisplay")&&b==="none")b=a.style.display="";b===""&&c.css(a,"display")==="none"&&c.data(a,"olddisplay",qa(a.nodeName))}for(d=0;d<e;d++){a=this[d];b=a.style.display;if(b===""||b==="none")a.style.display=c.data(a,"olddisplay")||""}return this}},hide:function(a,b,d){if(a||a===0)return this.animate(S("hide",3),a,b,d);else{a=0;for(b=this.length;a<b;a++){d=c.css(this[a],"display");d!=="none"&&c.data(this[a],"olddisplay",
+d)}for(a=0;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b,d){var e=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||e?this.each(function(){var f=e?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(S("toggle",3),a,b,d);return this},fadeTo:function(a,b,d,e){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d,e)},animate:function(a,b,d,e){var f=c.speed(b,
+d,e);if(c.isEmptyObject(a))return this.each(f.complete);return this[f.queue===false?"each":"queue"](function(){var h=c.extend({},f),l,k=this.nodeType===1,o=k&&c(this).is(":hidden"),x=this;for(l in a){var r=c.camelCase(l);if(l!==r){a[r]=a[l];delete a[l];l=r}if(a[l]==="hide"&&o||a[l]==="show"&&!o)return h.complete.call(this);if(k&&(l==="height"||l==="width")){h.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(c.css(this,"display")==="inline"&&c.css(this,"float")==="none")if(c.support.inlineBlockNeedsLayout)if(qa(this.nodeName)===
+"inline")this.style.display="inline-block";else{this.style.display="inline";this.style.zoom=1}else this.style.display="inline-block"}if(c.isArray(a[l])){(h.specialEasing=h.specialEasing||{})[l]=a[l][1];a[l]=a[l][0]}}if(h.overflow!=null)this.style.overflow="hidden";h.curAnim=c.extend({},a);c.each(a,function(A,C){var J=new c.fx(x,h,A);if(vb.test(C))J[C==="toggle"?o?"show":"hide":C](a);else{var w=wb.exec(C),I=J.cur()||0;if(w){var L=parseFloat(w[2]),g=w[3]||"px";if(g!=="px"){c.style(x,A,(L||1)+g);I=(L||
+1)/J.cur()*I;c.style(x,A,I+g)}if(w[1])L=(w[1]==="-="?-1:1)*L+I;J.custom(I,L,g)}else J.custom(I,C,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var e=d.length-1;e>=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b,
+d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a*
+Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)}
+var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;
+this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide||
+this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=
+c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},interval:13,stop:function(){clearInterval(ba);ba=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===
+b.elem}).length};var xb=/^t(?:able|d|h)$/i,Ia=/^(?:body|html)$/i;c.fn.offset="getBoundingClientRect"in t.documentElement?function(a){var b=this[0],d;if(a)return this.each(function(l){c.offset.setOffset(this,a,l)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);try{d=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,h=f.documentElement;if(!d||!c.contains(h,b))return d||{top:0,left:0};b=f.body;f=fa(f);return{top:d.top+(f.pageYOffset||c.support.boxModel&&
+h.scrollTop||b.scrollTop)-(h.clientTop||b.clientTop||0),left:d.left+(f.pageXOffset||c.support.boxModel&&h.scrollLeft||b.scrollLeft)-(h.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(x){c.offset.setOffset(this,a,x)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d,e=b.offsetParent,f=b.ownerDocument,h=f.documentElement,l=f.body;d=(f=f.defaultView)?f.getComputedStyle(b,null):b.currentStyle;
+for(var k=b.offsetTop,o=b.offsetLeft;(b=b.parentNode)&&b!==l&&b!==h;){if(c.offset.supportsFixedPosition&&d.position==="fixed")break;d=f?f.getComputedStyle(b,null):b.currentStyle;k-=b.scrollTop;o-=b.scrollLeft;if(b===e){k+=b.offsetTop;o+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&xb.test(b.nodeName))){k+=parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}e=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"){k+=
+parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}d=d}if(d.position==="relative"||d.position==="static"){k+=l.offsetTop;o+=l.offsetLeft}if(c.offset.supportsFixedPosition&&d.position==="fixed"){k+=Math.max(h.scrollTop,l.scrollTop);o+=Math.max(h.scrollLeft,l.scrollLeft)}return{top:k,left:o}};c.offset={initialize:function(){var a=t.body,b=t.createElement("div"),d,e,f,h=parseFloat(c.css(a,"marginTop"))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",
+height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);d=b.firstChild;e=d.firstChild;f=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=e.offsetTop!==5;this.doesAddBorderForTableAndCells=
+f.offsetTop===5;e.style.position="fixed";e.style.top="20px";this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15;e.style.position=e.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==h;a.removeChild(b);c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.css(a,
+"marginTop"))||0;d+=parseFloat(c.css(a,"marginLeft"))||0}return{top:b,left:d}},setOffset:function(a,b,d){var e=c.css(a,"position");if(e==="static")a.style.position="relative";var f=c(a),h=f.offset(),l=c.css(a,"top"),k=c.css(a,"left"),o=e==="absolute"&&c.inArray("auto",[l,k])>-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a,
+e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&&
+c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();
+c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+
+b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window);
diff --git a/objectapp/static/objectapp/js/jquery.masonry.js b/objectapp/static/objectapp/js/jquery.masonry.js
new file mode 100644
index 0000000..f6e8a82
--- /dev/null
+++ b/objectapp/static/objectapp/js/jquery.masonry.js
@@ -0,0 +1,12 @@
+/*************************************************
+** jQuery Masonry version 1.3.2
+** Copyright David DeSandro, licensed MIT
+** http://desandro.com/resources/jquery-masonry
+**************************************************/
+(function(e){var n=e.event,o;n.special.smartresize={setup:function(){e(this).bind("resize",n.special.smartresize.handler)},teardown:function(){e(this).unbind("resize",n.special.smartresize.handler)},handler:function(j,l){var g=this,d=arguments;j.type="smartresize";o&&clearTimeout(o);o=setTimeout(function(){jQuery.event.handle.apply(g,d)},l==="execAsap"?0:100)}};e.fn.smartresize=function(j){return j?this.bind("smartresize",j):this.trigger("smartresize",["execAsap"])};e.fn.masonry=function(j,l){var g=
+{getBricks:function(d,b,a){var c=a.itemSelector===undefined;b.$bricks=a.appendedContent===undefined?c?d.children():d.find(a.itemSelector):c?a.appendedContent:a.appendedContent.filter(a.itemSelector)},placeBrick:function(d,b,a,c,h){b=Math.min.apply(Math,a);for(var i=b+d.outerHeight(true),f=a.length,k=f,m=c.colCount+1-f;f--;)if(a[f]==b)k=f;d.applyStyle({left:c.colW*k+c.posLeft,top:b},e.extend(true,{},h.animationOptions));for(f=0;f<m;f++)c.colY[k+f]=i},setup:function(d,b,a){g.getBricks(d,a,b);if(a.masoned)a.previousData=
+d.data("masonry");a.colW=b.columnWidth===undefined?a.masoned?a.previousData.colW:a.$bricks.outerWidth(true):b.columnWidth;a.colCount=Math.floor(d.width()/a.colW);a.colCount=Math.max(a.colCount,1)},arrange:function(d,b,a){var c;if(!a.masoned||b.appendedContent!==undefined)a.$bricks.css("position","absolute");if(a.masoned){a.posTop=a.previousData.posTop;a.posLeft=a.previousData.posLeft}else{d.css("position","relative");var h=e(document.createElement("div"));d.prepend(h);a.posTop=Math.round(h.position().top);
+a.posLeft=Math.round(h.position().left);h.remove()}if(a.masoned&&b.appendedContent!==undefined){a.colY=a.previousData.colY;for(c=a.previousData.colCount;c<a.colCount;c++)a.colY[c]=a.posTop}else{a.colY=[];for(c=a.colCount;c--;)a.colY.push(a.posTop)}e.fn.applyStyle=a.masoned&&b.animate?e.fn.animate:e.fn.css;b.singleMode?a.$bricks.each(function(){var i=e(this);g.placeBrick(i,a.colCount,a.colY,a,b)}):a.$bricks.each(function(){var i=e(this),f=Math.ceil(i.outerWidth(true)/a.colW);f=Math.min(f,a.colCount);
+if(f===1)g.placeBrick(i,a.colCount,a.colY,a,b);else{var k=a.colCount+1-f,m=[];for(c=0;c<k;c++){var p=a.colY.slice(c,c+f);m[c]=Math.max.apply(Math,p)}g.placeBrick(i,k,m,a,b)}});a.wallH=Math.max.apply(Math,a.colY);d.applyStyle({height:a.wallH-a.posTop},e.extend(true,[],b.animationOptions));a.masoned||setTimeout(function(){d.addClass("masoned")},1);l.call(a.$bricks);d.data("masonry",a)},resize:function(d,b,a){a.masoned=!!d.data("masonry");var c=d.data("masonry").colCount;g.setup(d,b,a);a.colCount!=c&&
+g.arrange(d,b,a)}};return this.each(function(){var d=e(this),b={};b.masoned=!!d.data("masonry");var a=b.masoned?d.data("masonry").options:{},c=e.extend({},e.fn.masonry.defaults,a,j),h=a.resizeable;b.options=c.saveOptions?c:a;l=l||function(){};g.getBricks(d,b,c);if(!b.$bricks.length)return this;g.setup(d,c,b);g.arrange(d,c,b);!h&&c.resizeable&&e(window).bind("smartresize.masonry",function(){g.resize(d,c,b)});h&&!c.resizeable&&e(window).unbind("smartresize.masonry")})};e.fn.masonry.defaults={singleMode:false,
+columnWidth:undefined,itemSelector:undefined,appendedContent:undefined,saveOptions:true,resizeable:true,animate:false,animationOptions:{}}})(jQuery); \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/jquery.ui.js b/objectapp/static/objectapp/js/jquery.ui.js
new file mode 100644
index 0000000..044d307
--- /dev/null
+++ b/objectapp/static/objectapp/js/jquery.ui.js
@@ -0,0 +1,68 @@
+/*!
+ * jQuery UI 1.8.9
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.9",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,
+NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,
+"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");
+if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f,
+"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,
+d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}});
+c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&
+b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
+;/*!
+ * jQuery UI Widget 1.8.9
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
+a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
+e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
+this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
+widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
+enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
+;/*
+ * jQuery UI Tabs 1.8.9
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&&
+e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=
+d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]||
+(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
+this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected=
+this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active");
+if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));
+this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+
+g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",
+function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};
+this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=
+-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";
+d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=
+d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b,
+e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);
+j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove();
+if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1<this.anchors.length?1:-1));e.disabled=d.map(d.grep(e.disabled,function(h){return h!=b}),function(h){return h>=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null,
+this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this},
+load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c,
+"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},
+url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.9"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&&
+a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow",h);this.anchors.bind(c.event+".tabs",e);h()}else{clearTimeout(a.rotation);this.element.unbind("tabsshow",h);this.anchors.unbind(c.event+".tabs",e);delete this._rotate;delete this._unrotate}return this}})})(jQuery);
+; \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/markitup/jquery.markitup.js b/objectapp/static/objectapp/js/markitup/jquery.markitup.js
new file mode 100644
index 0000000..8c69b78
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/jquery.markitup.js
@@ -0,0 +1,574 @@
+// ----------------------------------------------------------------------------
+// markItUp! Universal MarkUp Engine, JQuery plugin
+// v 1.1.x
+// Dual licensed under the MIT and GPL licenses.
+// ----------------------------------------------------------------------------
+// Copyright (C) 2007-2010 Jay Salvat
+// http://markitup.jaysalvat.com/
+// ----------------------------------------------------------------------------
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ----------------------------------------------------------------------------
+(function($) {
+ $.fn.markItUp = function(settings, extraSettings) {
+ var options, ctrlKey, shiftKey, altKey;
+ ctrlKey = shiftKey = altKey = false;
+
+ options = { id: '',
+ nameSpace: '',
+ root: '',
+ previewInWindow: '', // 'width=800, height=600, resizable=yes, scrollbars=yes'
+ previewAutoRefresh: true,
+ previewPosition: 'after',
+ previewTemplatePath: '~/templates/preview.html',
+ previewParserPath: '',
+ previewParserVar: 'data',
+ resizeHandle: true,
+ beforeInsert: '',
+ afterInsert: '',
+ onEnter: {},
+ onShiftEnter: {},
+ onCtrlEnter: {},
+ onTab: {},
+ markupSet: [ { /* set */ } ]
+ };
+ $.extend(options, settings, extraSettings);
+
+ // compute markItUp! path
+ if (!options.root) {
+ $('script').each(function(a, tag) {
+ miuScript = $(tag).get(0).src.match(/(.*)jquery\.markitup(\.pack)?\.js$/);
+ if (miuScript !== null) {
+ options.root = miuScript[1];
+ }
+ });
+ }
+
+ return this.each(function() {
+ var $$, textarea, levels, scrollPosition, caretPosition, caretOffset,
+ clicked, hash, header, footer, previewWindow, template, iFrame, abort;
+ $$ = $(this);
+ textarea = this;
+ levels = [];
+ abort = false;
+ scrollPosition = caretPosition = 0;
+ caretOffset = -1;
+
+ options.previewParserPath = localize(options.previewParserPath);
+ options.previewTemplatePath = localize(options.previewTemplatePath);
+
+ // apply the computed path to ~/
+ function localize(data, inText) {
+ if (inText) {
+ return data.replace(/("|')~\//g, "$1"+options.root);
+ }
+ return data.replace(/^~\//, options.root);
+ }
+
+ // init and build editor
+ function init() {
+ id = ''; nameSpace = '';
+ if (options.id) {
+ id = 'id="'+options.id+'"';
+ } else if ($$.attr("id")) {
+ id = 'id="markItUp'+($$.attr("id").substr(0, 1).toUpperCase())+($$.attr("id").substr(1))+'"';
+
+ }
+ if (options.nameSpace) {
+ nameSpace = 'class="'+options.nameSpace+'"';
+ }
+ $$.wrap('<div '+nameSpace+'></div>');
+ $$.wrap('<div '+id+' class="markItUp"></div>');
+ $$.wrap('<div class="markItUpContainer"></div>');
+ $$.addClass("markItUpEditor");
+
+ // add the header before the textarea
+ header = $('<div class="markItUpHeader"></div>').insertBefore($$);
+ $(dropMenus(options.markupSet)).appendTo(header);
+
+ // add the footer after the textarea
+ footer = $('<div class="markItUpFooter"></div>').insertAfter($$);
+
+ // add the resize handle after textarea
+ if (options.resizeHandle === true && $.browser.safari !== true) {
+ resizeHandle = $('<div class="markItUpResizeHandle"></div>')
+ .insertAfter($$)
+ .bind("mousedown", function(e) {
+ var h = $$.height(), y = e.clientY, mouseMove, mouseUp;
+ mouseMove = function(e) {
+ $$.css("height", Math.max(20, e.clientY+h-y)+"px");
+ return false;
+ };
+ mouseUp = function(e) {
+ $("html").unbind("mousemove", mouseMove).unbind("mouseup", mouseUp);
+ return false;
+ };
+ $("html").bind("mousemove", mouseMove).bind("mouseup", mouseUp);
+ });
+ footer.append(resizeHandle);
+ }
+
+ // listen key events
+ $$.keydown(keyPressed).keyup(keyPressed);
+
+ // bind an event to catch external calls
+ $$.bind("insertion", function(e, settings) {
+ if (settings.target !== false) {
+ get();
+ }
+ if (textarea === $.markItUp.focused) {
+ markup(settings);
+ }
+ });
+
+ // remember the last focus
+ $$.focus(function() {
+ $.markItUp.focused = this;
+ });
+ }
+
+ // recursively build header with dropMenus from markupset
+ function dropMenus(markupSet) {
+ var ul = $('<ul></ul>'), i = 0;
+ $('li:hover > ul', ul).css('display', 'block');
+ $.each(markupSet, function() {
+ var button = this, t = '', title, li, j;
+ title = (button.key) ? (button.name||'')+' [Ctrl+'+button.key+']' : (button.name||'');
+ key = (button.key) ? 'accesskey="'+button.key+'"' : '';
+ if (button.separator) {
+ li = $('<li class="markItUpSeparator">'+(button.separator||'')+'</li>').appendTo(ul);
+ } else {
+ i++;
+ for (j = levels.length -1; j >= 0; j--) {
+ t += levels[j]+"-";
+ }
+ li = $('<li class="markItUpButton markItUpButton'+t+(i)+' '+(button.className||'')+'"><a href="" '+key+' title="'+title+'">'+(button.name||'')+'</a></li>')
+ .bind("contextmenu", function() { // prevent contextmenu on mac and allow ctrl+click
+ return false;
+ }).click(function() {
+ return false;
+ }).bind("focusin", function(){
+ $$.focus();
+ }).mousedown(function() {
+ if (button.call) {
+ eval(button.call)();
+ }
+ setTimeout(function() { markup(button) },1);
+ return false;
+ }).hover(function() {
+ $('> ul', this).show();
+ $(document).one('click', function() { // close dropmenu if click outside
+ $('ul ul', header).hide();
+ }
+ );
+ }, function() {
+ $('> ul', this).hide();
+ }
+ ).appendTo(ul);
+ if (button.dropMenu) {
+ levels.push(i);
+ $(li).addClass('markItUpDropMenu').append(dropMenus(button.dropMenu));
+ }
+ }
+ });
+ levels.pop();
+ return ul;
+ }
+
+ // markItUp! markups
+ function magicMarkups(string) {
+ if (string) {
+ string = string.toString();
+ string = string.replace(/\(\!\(([\s\S]*?)\)\!\)/g,
+ function(x, a) {
+ var b = a.split('|!|');
+ if (altKey === true) {
+ return (b[1] !== undefined) ? b[1] : b[0];
+ } else {
+ return (b[1] === undefined) ? "" : b[0];
+ }
+ }
+ );
+ // [![prompt]!], [![prompt:!:value]!]
+ string = string.replace(/\[\!\[([\s\S]*?)\]\!\]/g,
+ function(x, a) {
+ var b = a.split(':!:');
+ if (abort === true) {
+ return false;
+ }
+ value = prompt(b[0], (b[1]) ? b[1] : '');
+ if (value === null) {
+ abort = true;
+ }
+ return value;
+ }
+ );
+ return string;
+ }
+ return "";
+ }
+
+ // prepare action
+ function prepare(action) {
+ if ($.isFunction(action)) {
+ action = action(hash);
+ }
+ return magicMarkups(action);
+ }
+
+ // build block to insert
+ function build(string) {
+ var openWith = prepare(clicked.openWith);
+ var placeHolder = prepare(clicked.placeHolder);
+ var replaceWith = prepare(clicked.replaceWith);
+ var closeWith = prepare(clicked.closeWith);
+ if (replaceWith !== "") {
+ block = openWith + replaceWith + closeWith;
+ } else if (selection === '' && placeHolder !== '') {
+ block = openWith + placeHolder + closeWith;
+ } else {
+ string = string || selection;
+ if (string.match(/ $/)) {
+ block = openWith + string.replace(/ $/, '') + closeWith + ' ';
+ } else {
+ block = openWith + string + closeWith;
+ }
+ }
+ return { block:block,
+ openWith:openWith,
+ replaceWith:replaceWith,
+ placeHolder:placeHolder,
+ closeWith:closeWith
+ };
+ }
+
+ // define markup to insert
+ function markup(button) {
+ var len, j, n, i;
+ hash = clicked = button;
+ get();
+
+ $.extend(hash, { line:"",
+ root:options.root,
+ textarea:textarea,
+ selection:(selection||''),
+ caretPosition:caretPosition,
+ ctrlKey:ctrlKey,
+ shiftKey:shiftKey,
+ altKey:altKey
+ }
+ );
+ // callbacks before insertion
+ prepare(options.beforeInsert);
+ prepare(clicked.beforeInsert);
+ if (ctrlKey === true && shiftKey === true) {
+ prepare(clicked.beforeMultiInsert);
+ }
+ $.extend(hash, { line:1 });
+
+ if (ctrlKey === true && shiftKey === true) {
+ lines = selection.split(/\r?\n/);
+ for (j = 0, n = lines.length, i = 0; i < n; i++) {
+ if ($.trim(lines[i]) !== '') {
+ $.extend(hash, { line:++j, selection:lines[i] } );
+ lines[i] = build(lines[i]).block;
+ } else {
+ lines[i] = "";
+ }
+ }
+ string = { block:lines.join('\n')};
+ start = caretPosition;
+ len = string.block.length + (($.browser.opera) ? n-1 : 0);
+ } else if (ctrlKey === true) {
+ string = build(selection);
+ start = caretPosition + string.openWith.length;
+ len = string.block.length - string.openWith.length - string.closeWith.length;
+ len = len - (string.block.match(/ $/) ? 1 : 0);
+ len -= fixIeBug(string.block);
+ } else if (shiftKey === true) {
+ string = build(selection);
+ start = caretPosition;
+ len = string.block.length;
+ len -= fixIeBug(string.block);
+ } else {
+ string = build(selection);
+ start = caretPosition + string.block.length ;
+ len = 0;
+ start -= fixIeBug(string.block);
+ }
+ if ((selection === '' && string.replaceWith === '')) {
+ caretOffset += fixOperaBug(string.block);
+
+ start = caretPosition + string.openWith.length;
+ len = string.block.length - string.openWith.length - string.closeWith.length;
+
+ caretOffset = $$.val().substring(caretPosition, $$.val().length).length;
+ caretOffset -= fixOperaBug($$.val().substring(0, caretPosition));
+ }
+ $.extend(hash, { caretPosition:caretPosition, scrollPosition:scrollPosition } );
+
+ if (string.block !== selection && abort === false) {
+ insert(string.block);
+ set(start, len);
+ } else {
+ caretOffset = -1;
+ }
+ get();
+
+ $.extend(hash, { line:'', selection:selection });
+
+ // callbacks after insertion
+ if (ctrlKey === true && shiftKey === true) {
+ prepare(clicked.afterMultiInsert);
+ }
+ prepare(clicked.afterInsert);
+ prepare(options.afterInsert);
+
+ // refresh preview if opened
+ if (previewWindow && options.previewAutoRefresh) {
+ refreshPreview();
+ }
+
+ // reinit keyevent
+ shiftKey = altKey = ctrlKey = abort = false;
+ }
+
+ // Substract linefeed in Opera
+ function fixOperaBug(string) {
+ if ($.browser.opera) {
+ return string.length - string.replace(/\n*/g, '').length;
+ }
+ return 0;
+ }
+ // Substract linefeed in IE
+ function fixIeBug(string) {
+ if ($.browser.msie) {
+ return string.length - string.replace(/\r/g, '').length;
+ }
+ return 0;
+ }
+
+ // add markup
+ function insert(block) {
+ if (document.selection) {
+ var newSelection = document.selection.createRange();
+ newSelection.text = block;
+ } else {
+ textarea.value = textarea.value.substring(0, caretPosition) + block + textarea.value.substring(caretPosition + selection.length, textarea.value.length);
+ }
+ }
+
+ // set a selection
+ function set(start, len) {
+ if (textarea.createTextRange){
+ // quick fix to make it work on Opera 9.5
+ if ($.browser.opera && $.browser.version >= 9.5 && len == 0) {
+ return false;
+ }
+ range = textarea.createTextRange();
+ range.collapse(true);
+ range.moveStart('character', start);
+ range.moveEnd('character', len);
+ range.select();
+ } else if (textarea.setSelectionRange ){
+ textarea.setSelectionRange(start, start + len);
+ }
+ textarea.scrollTop = scrollPosition;
+ textarea.focus();
+ }
+
+ // get the selection
+ function get() {
+ textarea.focus();
+
+ scrollPosition = textarea.scrollTop;
+ if (document.selection) {
+ selection = document.selection;
+ if ($.browser.msie) { // ie
+ var range = selection.createRange();
+ var stored_range = range.duplicate();
+ stored_range.moveToElementText(textarea);
+ stored_range.setEndPoint('EndToEnd', range);
+ var s = stored_range.text.length - range.text.length;
+
+ caretPosition = s - (textarea.value.substr(0, s).length - textarea.value.substr(0, s).replace(/\r/g, '').length);
+ selection = range.text;
+ } else { // opera
+ caretPosition = textarea.selectionStart;
+ }
+ } else { // gecko & webkit
+ caretPosition = textarea.selectionStart;
+ selection = textarea.value.substring(caretPosition, textarea.selectionEnd);
+ }
+ return selection;
+ }
+
+ // open preview window
+ function preview() {
+ if (!previewWindow || previewWindow.closed) {
+ if (options.previewInWindow) {
+ previewWindow = window.open('', 'preview', options.previewInWindow);
+ $(window).unload(function() {
+ previewWindow.close();
+ });
+ } else {
+ iFrame = $('<iframe class="markItUpPreviewFrame"></iframe>');
+ if (options.previewPosition == 'after') {
+ iFrame.insertAfter(footer);
+ } else {
+ iFrame.insertBefore(header);
+ }
+ previewWindow = iFrame[iFrame.length - 1].contentWindow || frame[iFrame.length - 1];
+ }
+ } else if (altKey === true) {
+ if (iFrame) {
+ iFrame.remove();
+ } else {
+ previewWindow.close();
+ }
+ previewWindow = iFrame = false;
+ }
+ if (!options.previewAutoRefresh) {
+ refreshPreview();
+ }
+ if (options.previewInWindow) {
+ previewWindow.focus();
+ }
+ }
+
+ // refresh Preview window
+ function refreshPreview() {
+ renderPreview();
+ }
+
+ function renderPreview() {
+ var phtml;
+ if (options.previewParserPath !== '') {
+ $.ajax({
+ type: 'POST',
+ dataType: 'text',
+ global: false,
+ url: options.previewParserPath,
+ data: options.previewParserVar+'='+encodeURIComponent($$.val()),
+ success: function(data) {
+ writeInPreview( localize(data, 1) );
+ }
+ });
+ } else {
+ if (!template) {
+ $.ajax({
+ url: options.previewTemplatePath,
+ dataType: 'text',
+ global: false,
+ success: function(data) {
+ writeInPreview( localize(data, 1).replace(/<!-- content -->/g, $$.val()) );
+ }
+ });
+ }
+ }
+ return false;
+ }
+
+ function writeInPreview(data) {
+ if (previewWindow.document) {
+ try {
+ sp = previewWindow.document.documentElement.scrollTop
+ } catch(e) {
+ sp = 0;
+ }
+ previewWindow.document.open();
+ previewWindow.document.write(data);
+ previewWindow.document.close();
+ previewWindow.document.documentElement.scrollTop = sp;
+ }
+ }
+
+ // set keys pressed
+ function keyPressed(e) {
+ shiftKey = e.shiftKey;
+ altKey = e.altKey;
+ ctrlKey = (!(e.altKey && e.ctrlKey)) ? e.ctrlKey : false;
+
+ if (e.type === 'keydown') {
+ if (ctrlKey === true) {
+ li = $("a[accesskey="+String.fromCharCode(e.keyCode)+"]", header).parent('li');
+ if (li.length !== 0) {
+ ctrlKey = false;
+ setTimeout(function() {
+ li.triggerHandler('mousedown');
+ },1);
+ return false;
+ }
+ }
+ if (e.keyCode === 13 || e.keyCode === 10) { // Enter key
+ if (ctrlKey === true) { // Enter + Ctrl
+ ctrlKey = false;
+ markup(options.onCtrlEnter);
+ return options.onCtrlEnter.keepDefault;
+ } else if (shiftKey === true) { // Enter + Shift
+ shiftKey = false;
+ markup(options.onShiftEnter);
+ return options.onShiftEnter.keepDefault;
+ } else { // only Enter
+ markup(options.onEnter);
+ return options.onEnter.keepDefault;
+ }
+ }
+ if (e.keyCode === 9) { // Tab key
+ if (shiftKey == true || ctrlKey == true || altKey == true) {
+ return false;
+ }
+ if (caretOffset !== -1) {
+ get();
+ caretOffset = $$.val().length - caretOffset;
+ set(caretOffset, 0);
+ caretOffset = -1;
+ return false;
+ } else {
+ markup(options.onTab);
+ return options.onTab.keepDefault;
+ }
+ }
+ }
+ }
+
+ init();
+ });
+ };
+
+ $.fn.markItUpRemove = function() {
+ return this.each(function() {
+ var $$ = $(this).unbind().removeClass('markItUpEditor');
+ $$.parent('div').parent('div.markItUp').parent('div').replaceWith($$);
+ }
+ );
+ };
+
+ $.markItUp = function(settings) {
+ var options = { target:false };
+ $.extend(options, settings);
+ if (options.target) {
+ return $(options.target).each(function() {
+ $(this).focus();
+ $(this).trigger('insertion', [options]);
+ });
+ } else {
+ $('textarea').trigger('insertion', [options]);
+ }
+ };
+})(jQuery);
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/bold.png b/objectapp/static/objectapp/js/markitup/sets/html/images/bold.png
new file mode 100644
index 0000000..889ae80
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/bold.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/clean.png b/objectapp/static/objectapp/js/markitup/sets/html/images/clean.png
new file mode 100644
index 0000000..7e7cefb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/clean.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/h1.png b/objectapp/static/objectapp/js/markitup/sets/html/images/h1.png
new file mode 100644
index 0000000..9c122e9
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/h1.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/h2.png b/objectapp/static/objectapp/js/markitup/sets/html/images/h2.png
new file mode 100644
index 0000000..fbd8765
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/h2.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/h3.png b/objectapp/static/objectapp/js/markitup/sets/html/images/h3.png
new file mode 100644
index 0000000..c7836cf
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/h3.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/h4.png b/objectapp/static/objectapp/js/markitup/sets/html/images/h4.png
new file mode 100644
index 0000000..4e929ea
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/h4.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/h5.png b/objectapp/static/objectapp/js/markitup/sets/html/images/h5.png
new file mode 100644
index 0000000..30cabeb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/h5.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/h6.png b/objectapp/static/objectapp/js/markitup/sets/html/images/h6.png
new file mode 100644
index 0000000..058170a
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/h6.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/image.png b/objectapp/static/objectapp/js/markitup/sets/html/images/image.png
new file mode 100644
index 0000000..fc3c393
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/image.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/italic.png b/objectapp/static/objectapp/js/markitup/sets/html/images/italic.png
new file mode 100644
index 0000000..8482ac8
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/italic.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/link.png b/objectapp/static/objectapp/js/markitup/sets/html/images/link.png
new file mode 100644
index 0000000..25eacb7
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/link.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/list-bullet.png b/objectapp/static/objectapp/js/markitup/sets/html/images/list-bullet.png
new file mode 100644
index 0000000..4a8672b
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/list-bullet.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/list-item.png b/objectapp/static/objectapp/js/markitup/sets/html/images/list-item.png
new file mode 100644
index 0000000..8cb4d69
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/list-item.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/list-numeric.png b/objectapp/static/objectapp/js/markitup/sets/html/images/list-numeric.png
new file mode 100644
index 0000000..33b0b8d
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/list-numeric.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/paragraph.png b/objectapp/static/objectapp/js/markitup/sets/html/images/paragraph.png
new file mode 100644
index 0000000..95704fb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/paragraph.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/picture.png b/objectapp/static/objectapp/js/markitup/sets/html/images/picture.png
new file mode 100644
index 0000000..4a158fe
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/picture.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/preview.png b/objectapp/static/objectapp/js/markitup/sets/html/images/preview.png
new file mode 100644
index 0000000..a9925a0
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/preview.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/images/stroke.png b/objectapp/static/objectapp/js/markitup/sets/html/images/stroke.png
new file mode 100644
index 0000000..612058a
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/images/stroke.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/set.js b/objectapp/static/objectapp/js/markitup/sets/html/set.js
new file mode 100644
index 0000000..1474286
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/set.js
@@ -0,0 +1,38 @@
+// ----------------------------------------------------------------------------
+// markItUp!
+// ----------------------------------------------------------------------------
+// Copyright (C) 2008 Jay Salvat
+// http://markitup.jaysalvat.com/
+// ----------------------------------------------------------------------------
+// Html tags
+// http://en.wikipedia.org/wiki/html
+// ----------------------------------------------------------------------------
+// Basic set. Feel free to add more tags
+// ----------------------------------------------------------------------------
+mySettings = {
+ onShiftEnter: {keepDefault:false, replaceWith:'<br />\n'},
+ onCtrlEnter: {keepDefault:false, openWith:'\n<p>', closeWith:'</p>\n'},
+ onTab: {keepDefault:false, openWith:' '},
+ markupSet: [
+ {name:'Heading 1', key:'1', openWith:'<h1(!( class="[![Class]!]")!)>', closeWith:'</h1>', placeHolder:'Your title here...' },
+ {name:'Heading 2', key:'2', openWith:'<h2(!( class="[![Class]!]")!)>', closeWith:'</h2>', placeHolder:'Your title here...' },
+ {name:'Heading 3', key:'3', openWith:'<h3(!( class="[![Class]!]")!)>', closeWith:'</h3>', placeHolder:'Your title here...' },
+ {name:'Heading 4', key:'4', openWith:'<h4(!( class="[![Class]!]")!)>', closeWith:'</h4>', placeHolder:'Your title here...' },
+ {name:'Heading 5', key:'5', openWith:'<h5(!( class="[![Class]!]")!)>', closeWith:'</h5>', placeHolder:'Your title here...' },
+ {name:'Heading 6', key:'6', openWith:'<h6(!( class="[![Class]!]")!)>', closeWith:'</h6>', placeHolder:'Your title here...' },
+ {name:'Paragraph', openWith:'<p(!( class="[![Class]!]")!)>', closeWith:'</p>' },
+ {separator:'---------------' },
+ {name:'Bold', key:'B', openWith:'(!(<strong>|!|<b>)!)', closeWith:'(!(</strong>|!|</b>)!)' },
+ {name:'Italic', key:'I', openWith:'(!(<em>|!|<i>)!)', closeWith:'(!(</em>|!|</i>)!)' },
+ {name:'Stroke through', key:'S', openWith:'<del>', closeWith:'</del>' },
+ {separator:'---------------' },
+ {name:'Ul', openWith:'<ul>\n', closeWith:'</ul>\n' },
+ {name:'Ol', openWith:'<ol>\n', closeWith:'</ol>\n' },
+ {name:'Li', openWith:'<li>', closeWith:'</li>' },
+ {separator:'---------------' },
+ {name:'Picture', key:'P', replaceWith:'<img src="[![Source:!:http://]!]" alt="[![Alternative text]!]" />' },
+ {name:'Link', key:'L', openWith:'<a href="[![Link:!:http://]!]"(!( title="[![Title]!]")!)>', closeWith:'</a>', placeHolder:'Your text to link...' },
+ {separator:'---------------' },
+ {name:'Clean', className:'clean', replaceWith:function(markitup) { return markitup.selection.replace(/<(.*?)>/g, "") } }
+ ]
+} \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/markitup/sets/html/style.css b/objectapp/static/objectapp/js/markitup/sets/html/style.css
new file mode 100644
index 0000000..817e503
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/html/style.css
@@ -0,0 +1,59 @@
+/* -------------------------------------------------------------------
+// markItUp!
+// By Jay Salvat - http://markitup.jaysalvat.com/
+// ------------------------------------------------------------------*/
+.markItUp .markItUpButton1 a {
+ background-image:url(images/h1.png);
+}
+.markItUp .markItUpButton2 a {
+ background-image:url(images/h2.png);
+}
+.markItUp .markItUpButton3 a {
+ background-image:url(images/h3.png);
+}
+.markItUp .markItUpButton4 a {
+ background-image:url(images/h4.png);
+}
+.markItUp .markItUpButton5 a {
+ background-image:url(images/h5.png);
+}
+.markItUp .markItUpButton6 a {
+ background-image:url(images/h6.png);
+}
+.markItUp .markItUpButton7 a {
+ background-image:url(images/paragraph.png);
+}
+
+.markItUp .markItUpButton8 a {
+ background-image:url(images/bold.png);
+}
+.markItUp .markItUpButton9 a {
+ background-image:url(images/italic.png);
+}
+.markItUp .markItUpButton10 a {
+ background-image:url(images/stroke.png);
+}
+
+.markItUp .markItUpButton11 a {
+ background-image:url(images/list-bullet.png);
+}
+.markItUp .markItUpButton12 a {
+ background-image:url(images/list-numeric.png);
+}
+.markItUp .markItUpButton13 a {
+ background-image:url(images/list-item.png);
+}
+
+.markItUp .markItUpButton14 a {
+ background-image:url(images/picture.png);
+}
+.markItUp .markItUpButton15 a {
+ background-image:url(images/link.png);
+}
+
+.markItUp .clean a {
+ background-image:url(images/clean.png);
+}
+.markItUp .preview a {
+ background-image:url(images/preview.png);
+} \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/bold.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/bold.png
new file mode 100644
index 0000000..889ae80
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/bold.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/code.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/code.png
new file mode 100644
index 0000000..63fe6ce
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/code.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/h1.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h1.png
new file mode 100644
index 0000000..9c122e9
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h1.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/h2.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h2.png
new file mode 100644
index 0000000..fbd8765
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h2.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/h3.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h3.png
new file mode 100644
index 0000000..c7836cf
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h3.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/h4.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h4.png
new file mode 100644
index 0000000..4e929ea
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h4.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/h5.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h5.png
new file mode 100644
index 0000000..30cabeb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h5.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/h6.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h6.png
new file mode 100644
index 0000000..058170a
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/h6.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/italic.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/italic.png
new file mode 100644
index 0000000..8482ac8
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/italic.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/link.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/link.png
new file mode 100644
index 0000000..25eacb7
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/link.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/list-bullet.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/list-bullet.png
new file mode 100644
index 0000000..4a8672b
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/list-bullet.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/list-numeric.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/list-numeric.png
new file mode 100644
index 0000000..33b0b8d
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/list-numeric.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/picture.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/picture.png
new file mode 100644
index 0000000..4a158fe
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/picture.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/preview.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/preview.png
new file mode 100644
index 0000000..a9925a0
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/preview.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/images/quotes.png b/objectapp/static/objectapp/js/markitup/sets/markdown/images/quotes.png
new file mode 100644
index 0000000..e54ebeb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/images/quotes.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/set.js b/objectapp/static/objectapp/js/markitup/sets/markdown/set.js
new file mode 100644
index 0000000..7d87ef7
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/set.js
@@ -0,0 +1,49 @@
+// -------------------------------------------------------------------
+// markItUp!
+// -------------------------------------------------------------------
+// Copyright (C) 2008 Jay Salvat
+// http://markitup.jaysalvat.com/
+// -------------------------------------------------------------------
+// MarkDown tags example
+// http://en.wikipedia.org/wiki/Markdown
+// http://daringfireball.net/projects/markdown/
+// -------------------------------------------------------------------
+// Feel free to add more tags
+// -------------------------------------------------------------------
+mySettings = {
+ onShiftEnter: {keepDefault:false, openWith:'\n\n'},
+ markupSet: [
+ {name:'Heading 1', key:'1', placeHolder:'Your title here...', closeWith:function(markItUp) { return miu.markdownTitle(markItUp, '=') } },
+ {name:'Heading 2', key:'2', placeHolder:'Your title here...', closeWith:function(markItUp) { return miu.markdownTitle(markItUp, '-') } },
+ {name:'Heading 3', key:'3', openWith:'### ', placeHolder:'Your title here...' },
+ {name:'Heading 4', key:'4', openWith:'#### ', placeHolder:'Your title here...' },
+ {name:'Heading 5', key:'5', openWith:'##### ', placeHolder:'Your title here...' },
+ {name:'Heading 6', key:'6', openWith:'###### ', placeHolder:'Your title here...' },
+ {separator:'---------------' },
+ {name:'Bold', key:'B', openWith:'**', closeWith:'**'},
+ {name:'Italic', key:'I', openWith:'_', closeWith:'_'},
+ {separator:'---------------' },
+ {name:'Bulleted List', openWith:'- ' },
+ {name:'Numeric List', openWith:function(markItUp) {
+ return markItUp.line+'. ';
+ }},
+ {separator:'---------------' },
+ {name:'Picture', key:'P', replaceWith:'![[![Alternative text]!]]([![Url:!:http://]!] "[![Title]!]")'},
+ {name:'Link', key:'L', openWith:'[', closeWith:']([![Url:!:http://]!] "[![Title]!]")', placeHolder:'Your text to link here...' },
+ {separator:'---------------'},
+ {name:'Quotes', openWith:'> '},
+ {name:'Code Block / Code', openWith:'(!(\t|!|`)!)', closeWith:'(!(`)!)'}
+ ]
+}
+
+// mIu nameSpace to avoid conflict.
+miu = {
+ markdownTitle: function(markItUp, char) {
+ heading = '';
+ n = $.trim(markItUp.selection||markItUp.placeHolder).length;
+ for(i = 0; i < n; i++) {
+ heading += char;
+ }
+ return '\n'+heading;
+ }
+} \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/markitup/sets/markdown/style.css b/objectapp/static/objectapp/js/markitup/sets/markdown/style.css
new file mode 100644
index 0000000..03c3aea
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/markdown/style.css
@@ -0,0 +1,54 @@
+/* -------------------------------------------------------------------
+// markItUp!
+// By Jay Salvat - http://markitup.jaysalvat.com/
+// ------------------------------------------------------------------*/
+.markItUp .markItUpButton1 a {
+ background-image:url(images/h1.png);
+}
+.markItUp .markItUpButton2 a {
+ background-image:url(images/h2.png);
+}
+.markItUp .markItUpButton3 a {
+ background-image:url(images/h3.png);
+}
+.markItUp .markItUpButton4 a {
+ background-image:url(images/h4.png);
+}
+.markItUp .markItUpButton5 a {
+ background-image:url(images/h5.png);
+}
+.markItUp .markItUpButton6 a {
+ background-image:url(images/h6.png);
+}
+
+.markItUp .markItUpButton7 a {
+ background-image:url(images/bold.png);
+}
+.markItUp .markItUpButton8 a {
+ background-image:url(images/italic.png);
+}
+
+.markItUp .markItUpButton9 a {
+ background-image:url(images/list-bullet.png);
+}
+.markItUp .markItUpButton10 a {
+ background-image:url(images/list-numeric.png);
+}
+
+.markItUp .markItUpButton11 a {
+ background-image:url(images/picture.png);
+}
+.markItUp .markItUpButton12 a {
+ background-image:url(images/link.png);
+}
+
+.markItUp .markItUpButton13 a {
+ background-image:url(images/quotes.png);
+}
+.markItUp .markItUpButton14 a {
+ background-image:url(images/code.png);
+}
+
+.markItUp .preview a {
+ background-image:url(images/preview.png);
+} \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/bold.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/bold.png
new file mode 100644
index 0000000..889ae80
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/bold.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/code.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/code.png
new file mode 100644
index 0000000..63fe6ce
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/code.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h1.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h1.png
new file mode 100644
index 0000000..9c122e9
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h1.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h2.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h2.png
new file mode 100644
index 0000000..fbd8765
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h2.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h3.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h3.png
new file mode 100644
index 0000000..c7836cf
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h3.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h4.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h4.png
new file mode 100644
index 0000000..4e929ea
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h4.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h5.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h5.png
new file mode 100644
index 0000000..30cabeb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/h5.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/italic.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/italic.png
new file mode 100644
index 0000000..8482ac8
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/italic.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/link.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/link.png
new file mode 100644
index 0000000..25eacb7
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/link.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/list-bullet.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/list-bullet.png
new file mode 100644
index 0000000..4a8672b
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/list-bullet.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/list-numeric.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/list-numeric.png
new file mode 100644
index 0000000..33b0b8d
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/list-numeric.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/picture.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/picture.png
new file mode 100644
index 0000000..4a158fe
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/picture.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/preview.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/preview.png
new file mode 100644
index 0000000..a9925a0
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/preview.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/quotes.png b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/quotes.png
new file mode 100644
index 0000000..e54ebeb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/images/quotes.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/set.js b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/set.js
new file mode 100644
index 0000000..b468ae8
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/set.js
@@ -0,0 +1,48 @@
+// -------------------------------------------------------------------
+// markItUp!
+// -------------------------------------------------------------------
+// Copyright (C) 2011 Florent Gallaire <fgallaire@gmail.com>
+// License GNU GPLv3 or any later version.
+// Copyright (C) 2008 Jay Salvat
+// http://markitup.jaysalvat.com/
+// -------------------------------------------------------------------
+// ReStrucuredText tags example
+// http://docutils.sourceforge.net/docs/user/rst/quickref.html
+// http://docutils.sourceforge.net/docs/user/rst/quickstart.html
+// -------------------------------------------------------------------
+// Feel free to add more tags
+// -------------------------------------------------------------------
+mySettings = {
+ onShiftEnter: {keepDefault:false, replaceWith:'\n\n'},
+ markupSet: [
+ {name:'Heading 1', key:'1', placeHolder:'Your title here...', closeWith:function(markItUp) { return miu.markdownTitle(markItUp, '*') } },
+ {name:'Heading 2', key:'2', placeHolder:'Your title here...', closeWith:function(markItUp) { return miu.markdownTitle(markItUp, '=') } },
+ {name:'Heading 3', key:'3', placeHolder:'Your title here...', closeWith:function(markItUp) { return miu.markdownTitle(markItUp, '-') } },
+ {name:'Heading 4', key:'4', placeHolder:'Your title here...', closeWith:function(markItUp) { return miu.markdownTitle(markItUp, '^') } },
+ {name:'Heading 5', key:'5', placeHolder:'Your title here...', closeWith:function(markItUp) { return miu.markdownTitle(markItUp, '"') } },
+ {separator:'---------------' },
+ {name:'Bold', key:'B', openWith:'**', closeWith:'**'},
+ {name:'Italic', key:'I', openWith:'*', closeWith:'*'},
+ {separator:'---------------' },
+ {name:'Bulleted list', openWith:'- '},
+ {name:'Numeric list', openWith:'# '},
+ {separator:'---------------' },
+ {name:'Picture', key:'P', openWith:'\n\n.. image:: ', closeWith:'\n\n', placeHolder:'Your picture here...'},
+ {name:'Link', key:'L', openWith:'`', closeWith:' [![Link:!:http://]!]`_', placeHolder:'Your text to link here...'},
+ {separator:'---------------'},
+ {name:'Quote', openWith:'\t'},
+ {name:'Code', openWith:'``', closeWith:'``'}
+ ]
+}
+
+// mIu nameSpace to avoid conflict.
+miu = {
+ markdownTitle: function(markItUp, char) {
+ heading = '';
+ n = $.trim(markItUp.selection||markItUp.placeHolder).length;
+ for(i = 0; i < n; i++) {
+ heading += char;
+ }
+ return '\n'+heading;
+ }
+}
diff --git a/objectapp/static/objectapp/js/markitup/sets/restructuredtext/style.css b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/style.css
new file mode 100644
index 0000000..3da1a52
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/restructuredtext/style.css
@@ -0,0 +1,53 @@
+/* -------------------------------------------------------------------
+// Copyright (C) 2011 Florent Gallaire <fgallaire@gmail.com>
+// License GNU GPLv3 or any later version.
+// markItUp!
+// By Jay Salvat - http://markitup.jaysalvat.com/
+// ------------------------------------------------------------------*/
+.markItUp .markItUpButton1 a {
+ background-image:url(images/h1.png);
+}
+.markItUp .markItUpButton2 a {
+ background-image:url(images/h2.png);
+}
+.markItUp .markItUpButton3 a {
+ background-image:url(images/h3.png);
+}
+.markItUp .markItUpButton4 a {
+ background-image:url(images/h4.png);
+}
+.markItUp .markItUpButton5 a {
+ background-image:url(images/h5.png);
+}
+
+.markItUp .markItUpButton6 a {
+ background-image:url(images/bold.png);
+}
+.markItUp .markItUpButton7 a {
+ background-image:url(images/italic.png);
+}
+
+.markItUp .markItUpButton8 a {
+ background-image:url(images/list-bullet.png);
+}
+.markItUp .markItUpButton9 a {
+ background-image:url(images/list-numeric.png);
+}
+
+.markItUp .markItUpButton10 a {
+ background-image:url(images/picture.png);
+}
+.markItUp .markItUpButton11 a {
+ background-image:url(images/link.png);
+}
+
+.markItUp .markItUpButton12 a {
+ background-image:url(images/quotes.png);
+}
+.markItUp .markItUpButton13 a {
+ background-image:url(images/code.png);
+}
+
+.markItUp .preview a {
+ background-image:url(images/preview.png);
+}
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/bold.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/bold.png
new file mode 100644
index 0000000..889ae80
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/bold.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/code.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/code.png
new file mode 100644
index 0000000..63fe6ce
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/code.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/h1.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/h1.png
new file mode 100644
index 0000000..9c122e9
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/h1.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/h2.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/h2.png
new file mode 100644
index 0000000..fbd8765
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/h2.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/h3.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/h3.png
new file mode 100644
index 0000000..c7836cf
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/h3.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/h4.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/h4.png
new file mode 100644
index 0000000..4e929ea
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/h4.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/h5.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/h5.png
new file mode 100644
index 0000000..30cabeb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/h5.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/h6.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/h6.png
new file mode 100644
index 0000000..058170a
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/h6.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/italic.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/italic.png
new file mode 100644
index 0000000..8482ac8
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/italic.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/link.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/link.png
new file mode 100644
index 0000000..25eacb7
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/link.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/list-bullet.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/list-bullet.png
new file mode 100644
index 0000000..4a8672b
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/list-bullet.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/list-numeric.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/list-numeric.png
new file mode 100644
index 0000000..33b0b8d
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/list-numeric.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/paragraph.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/paragraph.png
new file mode 100644
index 0000000..95704fb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/paragraph.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/picture.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/picture.png
new file mode 100644
index 0000000..4a158fe
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/picture.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/preview.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/preview.png
new file mode 100644
index 0000000..a9925a0
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/preview.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/quotes.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/quotes.png
new file mode 100644
index 0000000..e54ebeb
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/quotes.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/images/stroke.png b/objectapp/static/objectapp/js/markitup/sets/textile/images/stroke.png
new file mode 100644
index 0000000..612058a
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/images/stroke.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/set.js b/objectapp/static/objectapp/js/markitup/sets/textile/set.js
new file mode 100644
index 0000000..dc7b6f6
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/set.js
@@ -0,0 +1,37 @@
+// -------------------------------------------------------------------
+// markItUp!
+// -------------------------------------------------------------------
+// Copyright (C) 2008 Jay Salvat
+// http://markitup.jaysalvat.com/
+// -------------------------------------------------------------------
+// Textile tags example
+// http://en.wikipedia.org/wiki/Textile_(markup_language)
+// http://www.textism.com/
+// -------------------------------------------------------------------
+// Feel free to add more tags
+// -------------------------------------------------------------------
+mySettings = {
+ onShiftEnter: {keepDefault:false, replaceWith:'\n\n'},
+ markupSet: [
+ {name:'Heading 1', key:'1', openWith:'h1(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
+ {name:'Heading 2', key:'2', openWith:'h2(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
+ {name:'Heading 3', key:'3', openWith:'h3(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
+ {name:'Heading 4', key:'4', openWith:'h4(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
+ {name:'Heading 5', key:'5', openWith:'h5(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
+ {name:'Heading 6', key:'6', openWith:'h6(!(([![Class]!]))!). ', placeHolder:'Your title here...' },
+ {name:'Paragraph', key:'P', openWith:'p(!(([![Class]!]))!). '},
+ {separator:'---------------' },
+ {name:'Bold', key:'B', closeWith:'*', openWith:'*'},
+ {name:'Italic', key:'I', closeWith:'_', openWith:'_'},
+ {name:'Stroke through', key:'S', closeWith:'-', openWith:'-'},
+ {separator:'---------------' },
+ {name:'Bulleted list', openWith:'(!(* |!|*)!)'},
+ {name:'Numeric list', openWith:'(!(# |!|#)!)'},
+ {separator:'---------------' },
+ {name:'Picture', replaceWith:'![![Source:!:http://]!]([![Alternative text]!])!'},
+ {name:'Link', key:'L', openWith:'"', closeWith:'([![Title]!])":[![Link:!:http://]!]', placeHolder:'Your text to link here...' },
+ {separator:'---------------' },
+ {name:'Quotes', openWith:'bq(!(([![Class]!]))!). '},
+ {name:'Code', openWith:'@', closeWith:'@'}
+ ]
+}
diff --git a/objectapp/static/objectapp/js/markitup/sets/textile/style.css b/objectapp/static/objectapp/js/markitup/sets/textile/style.css
new file mode 100644
index 0000000..6176df5
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/sets/textile/style.css
@@ -0,0 +1,60 @@
+/* -------------------------------------------------------------------
+// markItUp!
+// By Jay Salvat - http://markitup.jaysalvat.com/
+// ------------------------------------------------------------------*/
+.markItUp .markItUpButton1 a {
+ background-image:url(images/h1.png);
+}
+.markItUp .markItUpButton2 a {
+ background-image:url(images/h2.png);
+}
+.markItUp .markItUpButton3 a {
+ background-image:url(images/h3.png);
+}
+.markItUp .markItUpButton4 a {
+ background-image:url(images/h4.png);
+}
+.markItUp .markItUpButton5 a {
+ background-image:url(images/h5.png);
+}
+.markItUp .markItUpButton6 a {
+ background-image:url(images/h6.png);
+}
+.markItUp .markItUpButton7 a {
+ background-image:url(images/paragraph.png);
+}
+
+.markItUp .markItUpButton8 a {
+ background-image:url(images/bold.png);
+}
+.markItUp .markItUpButton9 a {
+ background-image:url(images/italic.png);
+}
+.markItUp .markItUpButton10 a {
+ background-image:url(images/stroke.png);
+}
+
+.markItUp .markItUpButton11 a {
+ background-image:url(images/list-bullet.png);
+}
+.markItUp .markItUpButton12 a {
+ background-image:url(images/list-numeric.png);
+}
+
+.markItUp .markItUpButton13 a {
+ background-image:url(images/picture.png);
+}
+.markItUp .markItUpButton14 a {
+ background-image:url(images/link.png);
+}
+
+.markItUp .markItUpButton15 a {
+ background-image:url(images/quotes.png);
+}
+.markItUp .markItUpButton16 a {
+ background-image:url(images/code.png);
+}
+
+.markItUp .preview a {
+ background-image:url(images/preview.png);
+} \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/markitup/skins/django/images/handle.png b/objectapp/static/objectapp/js/markitup/skins/django/images/handle.png
new file mode 100644
index 0000000..3993b20
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/skins/django/images/handle.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/skins/django/images/menu.png b/objectapp/static/objectapp/js/markitup/skins/django/images/menu.png
new file mode 100644
index 0000000..44a07af
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/skins/django/images/menu.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/skins/django/images/submenu.png b/objectapp/static/objectapp/js/markitup/skins/django/images/submenu.png
new file mode 100644
index 0000000..03d1977
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/skins/django/images/submenu.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/markitup/skins/django/style.css b/objectapp/static/objectapp/js/markitup/skins/django/style.css
new file mode 100644
index 0000000..8755e86
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/skins/django/style.css
@@ -0,0 +1,128 @@
+/* -------------------------------------------------------------------
+// markItUp! Universal MarkUp Engine, JQuery plugin
+// By Jay Salvat - http://markitup.jaysalvat.com/
+// ------------------------------------------------------------------*/
+.markItUp * {
+ margin:0px; padding:0px;
+ outline:none;
+}
+.markItUp a:link,
+.markItUp a:visited {
+ color:#000;
+ text-decoration:none;
+}
+.markItUp {
+ width:800px;
+ margin:5px 0 5px 0;
+ padding-left: 106px;
+}
+.markItUpContainer {
+ font:11px Verdana, Arial, Helvetica, sans-serif;
+}
+.markItUpHeader {
+/* margin-left: 0px;
+ padding-left: 0px;*/
+ padding-top: 3px;
+}
+#id_content.markItUpEditor {
+ font:12px 'Courier New', Courier, monospace;
+ padding:5px;
+ width:790px;
+ height:220px;
+ clear:both;
+ line-height:18px;
+ overflow:auto;
+}
+.markItUpPreviewFrame {
+ overflow:auto;
+ background-color:#FFF;
+ width:99.9%;
+ height:300px;
+ margin:5px 0;
+}
+.markItUpFooter {
+ width:100%;
+}
+.markItUpResizeHandle {
+ overflow:hidden;
+ width:22px; height:5px;
+ margin-left:auto;
+ margin-right:auto;
+ background-image:url(images/handle.png);
+ cursor:n-resize;
+}
+/***************************************************************************************/
+/* first row of buttons */
+div .markItUpHeader ul {
+ margin-left: 0px;
+ padding-left: 0px;
+}
+.markItUpHeader ul li {
+ list-style:none;
+ float:left;
+ position:relative;
+}
+.markItUpHeader ul li:hover > ul{
+ display:block;
+}
+.markItUpHeader ul .markItUpDropMenu {
+ background:transparent url(images/menu.png) no-repeat 115% 50%;
+ margin-right:5px;
+}
+.markItUpHeader ul .markItUpDropMenu li {
+ margin-right:0px;
+}
+/* next rows of buttons */
+.markItUpHeader ul ul {
+ display:none;
+ position:absolute;
+ top:18px; left:0px;
+ background:#FFF;
+ border:1px solid #000;
+}
+.markItUpHeader ul ul li {
+ float:none;
+ border-bottom:1px solid #000;
+}
+.markItUpHeader ul ul .markItUpDropMenu {
+ background:#FFF url(images/submenu.png) no-repeat 100% 50%;
+}
+.markItUpHeader ul .markItUpSeparator {
+ margin:0 10px;
+ width:1px;
+ height:16px;
+ overflow:hidden;
+ background-color:#CCC;
+}
+.markItUpHeader ul ul .markItUpSeparator {
+ width:auto; height:1px;
+ margin:0px;
+}
+/* next rows of buttons */
+.markItUpHeader ul ul ul {
+ position:absolute;
+ top:-1px; left:150px;
+}
+.markItUpHeader ul ul ul li {
+ float:none;
+}
+.markItUpHeader ul a {
+ display:block;
+ width:16px; height:16px;
+ text-indent:-10000px;
+ background-repeat:no-repeat;
+ padding:3px;
+ margin:0px;
+}
+.markItUpHeader ul ul a {
+ display:block;
+ padding-left:0px;
+ text-indent:0;
+ width:120px;
+ padding:5px 5px 5px 25px;
+ background-position:2px 50%;
+}
+.markItUpHeader ul ul a:hover {
+ color:#FFF;
+ background-color:#000;
+}
diff --git a/objectapp/static/objectapp/js/markitup/templates/preview.css b/objectapp/static/objectapp/js/markitup/templates/preview.css
new file mode 100644
index 0000000..ad91a87
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/templates/preview.css
@@ -0,0 +1,5 @@
+/* preview style examples */
+body {
+ background-color:#EFEFEF;
+ font:70% Verdana, Arial, Helvetica, sans-serif;
+} \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/markitup/templates/preview.html b/objectapp/static/objectapp/js/markitup/templates/preview.html
new file mode 100644
index 0000000..b8b3702
--- /dev/null
+++ b/objectapp/static/objectapp/js/markitup/templates/preview.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>markItUp! preview template</title>
+<link rel="stylesheet" type="text/css" href="~/templates/preview.css" />
+</head>
+<body>
+<!-- content -->
+</body>
+</html>
diff --git a/objectapp/static/objectapp/js/mptt_m2m_selectbox.js b/objectapp/static/objectapp/js/mptt_m2m_selectbox.js
new file mode 100644
index 0000000..e7924b6
--- /dev/null
+++ b/objectapp/static/objectapp/js/mptt_m2m_selectbox.js
@@ -0,0 +1,133 @@
+/*
+ * This is based on the SelectBox.js that comes with Django admin media
+ *
+ */
+
+var SelectBox = {
+ cache: new Object(),
+ init: function(id) {
+ var box = document.getElementById(id);
+ var node;
+ SelectBox.cache[id] = new Array();
+ var cache = SelectBox.cache[id];
+ for (var i = 0; (node = box.options[i]); i++) {
+ cache.push({value: node.value, text: node.text, displayed: 1, tree_id: node.getAttribute('data-tree-id'), left_val: node.getAttribute('data-left-value')});
+ }
+ },
+ redisplay: function(id) {
+ // Repopulate HTML select box from cache
+ var box = document.getElementById(id);
+
+ // for some reason both these steps are neccessary to get browsers to work properly...
+ for (i = 0; i < box.options.length; i++) {
+ box.options[0] = null;
+ }
+ box.options.length = 0;
+
+ SelectBox.sort(id);
+ for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) {
+ var node = SelectBox.cache[id][i];
+ if (node.displayed) {
+ newOpt = new Option(node.text, node.value, false, false);
+ newOpt.setAttribute('data-tree-id', node.tree_id);
+ newOpt.setAttribute('data-left-value', node.left_val);
+ box.options[box.options.length] = newOpt;
+ }
+ }
+ },
+ filter: function(id, text) {
+ // Redisplay the HTML select box, displaying only the choices containing ALL
+ // the words in text. (It's an AND search.)
+ var tokens = text.toLowerCase().split(/\s+/);
+ var node, token;
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
+ node.displayed = 1;
+ for (var j = 0; (token = tokens[j]); j++) {
+ if (node.text.toLowerCase().indexOf(token) == -1) {
+ node.displayed = 0;
+ }
+ }
+ }
+ SelectBox.redisplay(id);
+ },
+ delete_from_cache: function(id, value) {
+ var node, delete_index = null;
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
+ if (node.value == value) {
+ delete_index = i;
+ break;
+ }
+ }
+ var j = SelectBox.cache[id].length - 1;
+ for (var i = delete_index; i < j; i++) {
+ SelectBox.cache[id][i] = SelectBox.cache[id][i+1];
+ }
+ SelectBox.cache[id].length--;
+ },
+ add_to_cache: function(id, option) {
+ // in this case option is an anonymous object, not an html element
+ SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1, tree_id: option.tree_id, left_val: option.left_val});
+ },
+ cache_contains: function(id, value) {
+ // Check if an item is contained in the cache
+ var node;
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
+ if (node.value == value) {
+ return true;
+ }
+ }
+ return false;
+ },
+ move: function(from, to) {
+ var from_box = document.getElementById(from);
+ var to_box = document.getElementById(to);
+ var option;
+ for (var i = 0; (option = from_box.options[i]); i++) {
+ if (option.selected && SelectBox.cache_contains(from, option.value)) {
+ SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1, tree_id: option.getAttribute('data-tree-id'), left_val: option.getAttribute('data-left-value')});
+ SelectBox.delete_from_cache(from, option.value);
+ }
+ }
+ SelectBox.redisplay(from);
+ SelectBox.redisplay(to);
+ },
+ move_all: function(from, to) {
+ var from_box = document.getElementById(from);
+ var to_box = document.getElementById(to);
+ var option;
+ for (var i = 0; (option = from_box.options[i]); i++) {
+ if (SelectBox.cache_contains(from, option.value)) {
+ SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1, tree_id: option.getAttribute('data-tree-id'), left_val: option.getAttribute('data-left-value')});
+ SelectBox.delete_from_cache(from, option.value);
+ }
+ }
+ SelectBox.redisplay(from);
+ SelectBox.redisplay(to);
+ },
+ sort: function(id) {
+ SelectBox.cache[id].sort( function(a, b) {
+ a_tree_id = parseInt(a.tree_id);
+ b_tree_id = parseInt(b.tree_id);
+ a_left_val = parseInt(a.left_val);
+ b_left_val = parseInt(b.left_val);
+ try {
+ if (a_tree_id > b_tree_id) return 1;
+ if (a_tree_id < b_tree_id) return -1;
+ if (a_tree_id == b_tree_id) {
+ if (a_left_val > b_left_val) return 1;
+ if (a_left_val < b_left_val) return -1;
+ }
+ }
+ catch (e) {
+ // silently fail on IE 'unknown' exception
+ }
+ return 0;
+ } );
+ },
+ select_all: function(id) {
+ var box = document.getElementById(id);
+ for (var i = 0; i < box.options.length; i++) {
+ box.options[i].selected = 'selected';
+ }
+ }
+}
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-blockquote.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-blockquote.png
new file mode 100644
index 0000000..65ea205
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-blockquote.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h1.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h1.png
new file mode 100644
index 0000000..dea1da3
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h1.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h2.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h2.png
new file mode 100644
index 0000000..f4b4274
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h2.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h3.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h3.png
new file mode 100644
index 0000000..bd99f76
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h3.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h4.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h4.png
new file mode 100644
index 0000000..e06ab3d
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h4.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h5.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h5.png
new file mode 100644
index 0000000..360fc60
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h5.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h6.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h6.png
new file mode 100644
index 0000000..73e644a
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-h6.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-p.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-p.png
new file mode 100644
index 0000000..7f2bf34
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-p.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-pre.png b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-pre.png
new file mode 100644
index 0000000..e026e47
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/lbl-pre.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/wymiframe.css b/objectapp/static/objectapp/js/wymeditor/iframe/default/wymiframe.css
new file mode 100644
index 0000000..769520c
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/wymiframe.css
@@ -0,0 +1,90 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * wymeditor.css
+ * Main editor css file.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+*/
+
+/* VISUAL FEEDBACK */
+
+/* basic */
+ body { background: #e1e8f1;}
+
+/* make HTML blocs visible */
+ p,
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ ul,
+ ol,
+ table,
+ blockquote,
+ pre { background: #FFFFFF no-repeat 2px 2px;
+ padding:8px 5px 5px;
+ margin:10px; }
+ td { background: #F0F4F8; }
+ th { background: #ffffcc; }
+ ul,
+ ol { border-left:20px solid #B9C4D0; padding:0px 5px; }
+ caption { background: #E4E4B0; padding: 5px; font-weight: bold; }
+ table { font-size: 12px; width: 500px; }
+ td { width: 25%; }
+ blockquote { margin-left: 30px; }
+ pre { background-color:transparent; border: 1px solid white; }
+
+/* Gecko min height fix */
+ p { min-height: 1em; } /*min-height is needed under Firefox, because empty parargraphs */
+ *+html p { min-height: auto; } /* but we have to remove it under IE7 because it triggers the 'haslayout' mode */
+ td { height: 1.6em; }
+
+/* labels */
+ p { background-image: url(lbl-p.png); }
+ h1 { background-image: url(lbl-h1.png); }
+ h2 { background-image: url(lbl-h2.png); }
+ h3 { background-image: url(lbl-h3.png); }
+ h4 { background-image: url(lbl-h4.png); }
+ h5 { background-image: url(lbl-h5.png); }
+ h6 { background-image: url(lbl-h6.png); }
+ blockquote{ background-image: url(lbl-blockquote.png); }
+ pre { background-image: url(lbl-pre.png); }
+
+/* specific HTML elements */
+ caption { text-align: left; }
+ img { margin-right: 5px;
+ border-style: solid;
+ border-color: gray;
+ border-width: 0; }
+ a img { border-width: 1px; border-color: blue; }
+ acronym { border: 1px solid gray; }
+ span { background-color: #eef; }
+
+/* visual feedback for non-valid nesting of elements*/
+ h1 h1, h1 h2, h1 h3, h1 h4, h1 h5, h1 h6, h1 p, h1 pre, h1 address,
+ h2 h1, h2 h2, h2 h3, h2 h4, h2 h5, h2 h6, h2 p, h2 pre, h2 address,
+ h3 h1, h3 h2, h3 h3, h3 h4, h3 h5, h3 h6, h3 p, h3 pre, h3 address,
+ h4 h1, h4 h2, h4 h3, h4 h4, h4 h5, h4 h6, h4 p, h4 pre, h4 address,
+ h5 h1, h5 h2, h5 h3, h5 h4, h5 h5, h5 h6, h5 p, h5 pre, h5 address,
+ h6 h1, h6 h2, h6 h3, h6 h4, h6 h4, h6 h6, h6 p, h6 pre, h6 address,
+ p h1, p h2, p h3, p h4, p h5, p h6, p pre, p address,
+ pre h1, pre h2, pre h3, pre h4, pre h5, pre h6, pre p, pre pre, pre address,
+ address h1, address h2, address h3, address h4, address h5, address h6,
+ address p, address pre, address address
+ { background-color: #ff9999 !important;
+ border: 1px solid red !important;
+ font-size: 12px !important;
+ font-weight: normal; }
diff --git a/objectapp/static/objectapp/js/wymeditor/iframe/default/wymiframe.html b/objectapp/static/objectapp/js/wymeditor/iframe/default/wymiframe.html
new file mode 100644
index 0000000..91a690c
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/iframe/default/wymiframe.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * wymiframe.html
+ * Iframe used by designMode.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+-->
+<html>
+<head>
+<title>WYMeditor iframe</title>
+<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
+<link rel="stylesheet" type="text/css" media="screen" href="wymiframe.css" />
+</head>
+<body class="wym_iframe"></body>
+</html>
diff --git a/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.js b/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.js
new file mode 100644
index 0000000..0667d85
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.js
@@ -0,0 +1,4688 @@
+/**
+ * @version 0.5-rc1
+ *
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File: jquery.wymeditor.js
+ *
+ * Main JS file with core classes and functions.
+ * See the documentation for more info.
+ *
+ * About: authors
+ *
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Volker Mische (vmx a-t gmx dotde)
+ * Scott Lewis (lewiscot a-t gmail dotcom)
+ * Bermi Ferrer (wymeditor a-t bermi dotorg)
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+/*
+ Namespace: WYMeditor
+ Global WYMeditor namespace.
+*/
+if(!WYMeditor) var WYMeditor = {};
+
+//Wrap the Firebug console in WYMeditor.console
+(function() {
+ if ( !window.console || !console.firebug ) {
+ var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
+ "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
+
+ WYMeditor.console = {};
+ for (var i = 0; i < names.length; ++i)
+ WYMeditor.console[names[i]] = function() {}
+
+ } else WYMeditor.console = window.console;
+})();
+
+jQuery.extend(WYMeditor, {
+
+/*
+ Constants: Global WYMeditor constants.
+
+ VERSION - Defines WYMeditor version.
+ INSTANCES - An array of loaded WYMeditor.editor instances.
+ STRINGS - An array of loaded WYMeditor language pairs/values.
+ SKINS - An array of loaded WYMeditor skins.
+ NAME - The "name" attribute.
+ INDEX - A string replaced by the instance index.
+ WYM_INDEX - A string used to get/set the instance index.
+ BASE_PATH - A string replaced by WYMeditor's base path.
+ SKIN_PATH - A string replaced by WYMeditor's skin path.
+ WYM_PATH - A string replaced by WYMeditor's main JS file path.
+ SKINS_DEFAULT_PATH - The skins default base path.
+ SKINS_DEFAULT_CSS - The skins default CSS file.
+ LANG_DEFAULT_PATH - The language files default path.
+ IFRAME_BASE_PATH - A string replaced by the designmode iframe's base path.
+ IFRAME_DEFAULT - The iframe's default base path.
+ JQUERY_PATH - A string replaced by the computed jQuery path.
+ DIRECTION - A string replaced by the text direction (rtl or ltr).
+ LOGO - A string replaced by WYMeditor logo.
+ TOOLS - A string replaced by the toolbar's HTML.
+ TOOLS_ITEMS - A string replaced by the toolbar items.
+ TOOL_NAME - A string replaced by a toolbar item's name.
+ TOOL_TITLE - A string replaced by a toolbar item's title.
+ TOOL_CLASS - A string replaced by a toolbar item's class.
+ CLASSES - A string replaced by the classes panel's HTML.
+ CLASSES_ITEMS - A string replaced by the classes items.
+ CLASS_NAME - A string replaced by a class item's name.
+ CLASS_TITLE - A string replaced by a class item's title.
+ CONTAINERS - A string replaced by the containers panel's HTML.
+ CONTAINERS_ITEMS - A string replaced by the containers items.
+ CONTAINER_NAME - A string replaced by a container item's name.
+ CONTAINER_TITLE - A string replaced by a container item's title.
+ CONTAINER_CLASS - A string replaced by a container item's class.
+ HTML - A string replaced by the HTML view panel's HTML.
+ IFRAME - A string replaced by the designmode iframe.
+ STATUS - A string replaced by the status panel's HTML.
+ DIALOG_TITLE - A string replaced by a dialog's title.
+ DIALOG_BODY - A string replaced by a dialog's HTML body.
+ BODY - The BODY element.
+ STRING - The "string" type.
+ BODY,DIV,P,
+ H1,H2,H3,H4,H5,H6,
+ PRE,BLOCKQUOTE,
+ A,BR,IMG,
+ TABLE,TD,TH,
+ UL,OL,LI - HTML elements string representation.
+ CLASS,HREF,SRC,
+ TITLE,ALT - HTML attributes string representation.
+ DIALOG_LINK - A link dialog type.
+ DIALOG_IMAGE - An image dialog type.
+ DIALOG_TABLE - A table dialog type.
+ DIALOG_PASTE - A 'Paste from Word' dialog type.
+ BOLD - Command: (un)set selection to <strong>.
+ ITALIC - Command: (un)set selection to <em>.
+ CREATE_LINK - Command: open the link dialog or (un)set link.
+ INSERT_IMAGE - Command: open the image dialog or insert an image.
+ INSERT_TABLE - Command: open the table dialog.
+ PASTE - Command: open the paste dialog.
+ INDENT - Command: nest a list item.
+ OUTDENT - Command: unnest a list item.
+ TOGGLE_HTML - Command: display/hide the HTML view.
+ FORMAT_BLOCK - Command: set a block element to another type.
+ PREVIEW - Command: open the preview dialog.
+ UNLINK - Command: unset a link.
+ INSERT_UNORDEREDLIST- Command: insert an unordered list.
+ INSERT_ORDEREDLIST - Command: insert an ordered list.
+ MAIN_CONTAINERS - An array of the main HTML containers used in WYMeditor.
+ BLOCKS - An array of the HTML block elements.
+ KEY - Standard key codes.
+ NODE - Node types.
+
+*/
+
+ VERSION : "0.5-rc1",
+ INSTANCES : [],
+ STRINGS : [],
+ SKINS : [],
+ NAME : "name",
+ INDEX : "{Wym_Index}",
+ WYM_INDEX : "wym_index",
+ BASE_PATH : "{Wym_Base_Path}",
+ CSS_PATH : "{Wym_Css_Path}",
+ WYM_PATH : "{Wym_Wym_Path}",
+ SKINS_DEFAULT_PATH : "skins/",
+ SKINS_DEFAULT_CSS : "skin.css",
+ SKINS_DEFAULT_JS : "skin.js",
+ LANG_DEFAULT_PATH : "lang/",
+ IFRAME_BASE_PATH : "{Wym_Iframe_Base_Path}",
+ IFRAME_DEFAULT : "iframe/default/",
+ JQUERY_PATH : "{Wym_Jquery_Path}",
+ DIRECTION : "{Wym_Direction}",
+ LOGO : "{Wym_Logo}",
+ TOOLS : "{Wym_Tools}",
+ TOOLS_ITEMS : "{Wym_Tools_Items}",
+ TOOL_NAME : "{Wym_Tool_Name}",
+ TOOL_TITLE : "{Wym_Tool_Title}",
+ TOOL_CLASS : "{Wym_Tool_Class}",
+ CLASSES : "{Wym_Classes}",
+ CLASSES_ITEMS : "{Wym_Classes_Items}",
+ CLASS_NAME : "{Wym_Class_Name}",
+ CLASS_TITLE : "{Wym_Class_Title}",
+ CONTAINERS : "{Wym_Containers}",
+ CONTAINERS_ITEMS : "{Wym_Containers_Items}",
+ CONTAINER_NAME : "{Wym_Container_Name}",
+ CONTAINER_TITLE : "{Wym_Containers_Title}",
+ CONTAINER_CLASS : "{Wym_Container_Class}",
+ HTML : "{Wym_Html}",
+ IFRAME : "{Wym_Iframe}",
+ STATUS : "{Wym_Status}",
+ DIALOG_TITLE : "{Wym_Dialog_Title}",
+ DIALOG_BODY : "{Wym_Dialog_Body}",
+ STRING : "string",
+ BODY : "body",
+ DIV : "div",
+ P : "p",
+ H1 : "h1",
+ H2 : "h2",
+ H3 : "h3",
+ H4 : "h4",
+ H5 : "h5",
+ H6 : "h6",
+ PRE : "pre",
+ BLOCKQUOTE : "blockquote",
+ A : "a",
+ BR : "br",
+ IMG : "img",
+ TABLE : "table",
+ TD : "td",
+ TH : "th",
+ UL : "ul",
+ OL : "ol",
+ LI : "li",
+ CLASS : "class",
+ HREF : "href",
+ SRC : "src",
+ TITLE : "title",
+ ALT : "alt",
+ DIALOG_LINK : "Link",
+ DIALOG_IMAGE : "Image",
+ DIALOG_TABLE : "Table",
+ DIALOG_PASTE : "Paste_From_Word",
+ BOLD : "Bold",
+ ITALIC : "Italic",
+ CREATE_LINK : "CreateLink",
+ INSERT_IMAGE : "InsertImage",
+ INSERT_TABLE : "InsertTable",
+ INSERT_HTML : "InsertHTML",
+ PASTE : "Paste",
+ INDENT : "Indent",
+ OUTDENT : "Outdent",
+ TOGGLE_HTML : "ToggleHtml",
+ FORMAT_BLOCK : "FormatBlock",
+ PREVIEW : "Preview",
+ UNLINK : "Unlink",
+ INSERT_UNORDEREDLIST: "InsertUnorderedList",
+ INSERT_ORDEREDLIST : "InsertOrderedList",
+
+ MAIN_CONTAINERS : new Array("p","h1","h2","h3","h4","h5","h6","pre","blockquote"),
+
+ BLOCKS : new Array("address", "blockquote", "div", "dl",
+ "fieldset", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr",
+ "noscript", "ol", "p", "pre", "table", "ul", "dd", "dt",
+ "li", "tbody", "td", "tfoot", "th", "thead", "tr"),
+
+ KEY : {
+ BACKSPACE: 8,
+ ENTER: 13,
+ END: 35,
+ HOME: 36,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ CURSOR: new Array(37, 38, 39, 40),
+ DELETE: 46
+ },
+
+ NODE : {
+ ELEMENT: 1,
+ ATTRIBUTE: 2,
+ TEXT: 3
+ },
+
+ /*
+ Class: WYMeditor.editor
+ WYMeditor editor main class, instanciated for each editor occurrence.
+ */
+
+ editor : function(elem, options) {
+
+ /*
+ Constructor: WYMeditor.editor
+
+ Initializes main values (index, elements, paths, ...)
+ and call WYMeditor.editor.init which initializes the editor.
+
+ Parameters:
+
+ elem - The HTML element to be replaced by the editor.
+ options - The hash of options.
+
+ Returns:
+
+ Nothing.
+
+ See Also:
+
+ <WYMeditor.editor.init>
+ */
+
+ //store the instance in the INSTANCES array and store the index
+ this._index = WYMeditor.INSTANCES.push(this) - 1;
+ //store the element replaced by the editor
+ this._element = elem;
+ //store the options
+ this._options = options;
+ //store the element's inner value
+ this._html = jQuery(elem).val();
+
+ //store the HTML option, if any
+ if(this._options.html) this._html = this._options.html;
+ //get or compute the base path (where the main JS file is located)
+ this._options.basePath = this._options.basePath
+ || this.computeBasePath();
+ //get or set the skin path (where the skin files are located)
+ this._options.skinPath = this._options.skinPath
+ || this._options.basePath + WYMeditor.SKINS_DEFAULT_PATH
+ + this._options.skin + '/';
+ //get or compute the main JS file location
+ this._options.wymPath = this._options.wymPath
+ || this.computeWymPath();
+ //get or set the language files path
+ this._options.langPath = this._options.langPath
+ || this._options.basePath + WYMeditor.LANG_DEFAULT_PATH;
+ //get or set the designmode iframe's base path
+ this._options.iframeBasePath = this._options.iframeBasePath
+ || this._options.basePath + WYMeditor.IFRAME_DEFAULT;
+ //get or compute the jQuery JS file location
+ this._options.jQueryPath = this._options.jQueryPath
+ || this.computeJqueryPath();
+
+ //initialize the editor instance
+ this.init();
+
+ }
+
+});
+
+
+/********** JQUERY **********/
+
+/**
+ * Replace an HTML element by WYMeditor
+ *
+ * @example jQuery(".wymeditor").wymeditor(
+ * {
+ *
+ * }
+ * );
+ * @desc Example description here
+ *
+ * @name WYMeditor
+ * @description WYMeditor is a web-based WYSIWYM XHTML editor
+ * @param Hash hash A hash of parameters
+ * @option Integer iExample Description here
+ * @option String sExample Description here
+ *
+ * @type jQuery
+ * @cat Plugins/WYMeditor
+ * @author Jean-Francois Hovinne
+ */
+jQuery.fn.wymeditor = function(options) {
+
+ options = jQuery.extend({
+
+ html: "",
+
+ basePath: false,
+
+ skinPath: false,
+
+ wymPath: false,
+
+ iframeBasePath: false,
+
+ jQueryPath: false,
+
+ styles: false,
+
+ stylesheet: false,
+
+ skin: "default",
+ initSkin: true,
+ loadSkin: true,
+
+ lang: "en",
+
+ direction: "ltr",
+
+ boxHtml: "<div class='wym_box'>"
+ + "<div class='wym_area_top'>"
+ + WYMeditor.TOOLS
+ + "</div>"
+ + "<div class='wym_area_left'></div>"
+ + "<div class='wym_area_right'>"
+ + WYMeditor.CONTAINERS
+ + WYMeditor.CLASSES
+ + "</div>"
+ + "<div class='wym_area_main'>"
+ + WYMeditor.HTML
+ + WYMeditor.IFRAME
+ + WYMeditor.STATUS
+ + "</div>"
+ + "<div class='wym_area_bottom'>"
+ + WYMeditor.LOGO
+ + "</div>"
+ + "</div>",
+
+ logoHtml: "<a class='wym_wymeditor_link' "
+ + "href='http://www.wymeditor.org/'>WYMeditor</a>",
+
+ iframeHtml:"<div class='wym_iframe wym_section'>"
+ + "<iframe "
+ + "src='"
+ + WYMeditor.IFRAME_BASE_PATH
+ + "wymiframe.html' "
+ + "onload='this.contentWindow.parent.WYMeditor.INSTANCES["
+ + WYMeditor.INDEX + "].initIframe(this)'"
+ + "></iframe>"
+ + "</div>",
+
+ editorStyles: [],
+
+ toolsHtml: "<div class='wym_tools wym_section'>"
+ + "<h2>{Tools}</h2>"
+ + "<ul>"
+ + WYMeditor.TOOLS_ITEMS
+ + "</ul>"
+ + "</div>",
+
+ toolsItemHtml: "<li class='"
+ + WYMeditor.TOOL_CLASS
+ + "'><a href='#' name='"
+ + WYMeditor.TOOL_NAME
+ + "' title='"
+ + WYMeditor.TOOL_TITLE
+ + "'>"
+ + WYMeditor.TOOL_TITLE
+ + "</a></li>",
+
+ toolsItems: [
+ {'name': 'Bold', 'title': 'Strong', 'css': 'wym_tools_strong'},
+ {'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'},
+ {'name': 'Superscript', 'title': 'Superscript',
+ 'css': 'wym_tools_superscript'},
+ {'name': 'Subscript', 'title': 'Subscript',
+ 'css': 'wym_tools_subscript'},
+ {'name': 'InsertOrderedList', 'title': 'Ordered_List',
+ 'css': 'wym_tools_ordered_list'},
+ {'name': 'InsertUnorderedList', 'title': 'Unordered_List',
+ 'css': 'wym_tools_unordered_list'},
+ {'name': 'Indent', 'title': 'Indent', 'css': 'wym_tools_indent'},
+ {'name': 'Outdent', 'title': 'Outdent', 'css': 'wym_tools_outdent'},
+ {'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'},
+ {'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'},
+ {'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'},
+ {'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'},
+ {'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'},
+ {'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'},
+ {'name': 'Paste', 'title': 'Paste_From_Word',
+ 'css': 'wym_tools_paste'},
+ {'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'},
+ {'name': 'Preview', 'title': 'Preview', 'css': 'wym_tools_preview'}
+ ],
+
+ containersHtml: "<div class='wym_containers wym_section'>"
+ + "<h2>{Containers}</h2>"
+ + "<ul>"
+ + WYMeditor.CONTAINERS_ITEMS
+ + "</ul>"
+ + "</div>",
+
+ containersItemHtml:"<li class='"
+ + WYMeditor.CONTAINER_CLASS
+ + "'>"
+ + "<a href='#' name='"
+ + WYMeditor.CONTAINER_NAME
+ + "'>"
+ + WYMeditor.CONTAINER_TITLE
+ + "</a></li>",
+
+ containersItems: [
+ {'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'},
+ {'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'},
+ {'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'},
+ {'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'},
+ {'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'},
+ {'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'},
+ {'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'},
+ {'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'},
+ {'name': 'BLOCKQUOTE', 'title': 'Blockquote',
+ 'css': 'wym_containers_blockquote'},
+ {'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th'}
+ ],
+
+ classesHtml: "<div class='wym_classes wym_section'>"
+ + "<h2>{Classes}</h2><ul>"
+ + WYMeditor.CLASSES_ITEMS
+ + "</ul></div>",
+
+ classesItemHtml: "<li class='wym_classes_"
+ + WYMeditor.CLASS_NAME
+ + "'><a href='#' name='"
+ + WYMeditor.CLASS_NAME
+ + "'>"
+ + WYMeditor.CLASS_TITLE
+ + "</a></li>",
+
+ classesItems: [],
+
+ statusHtml: "<div class='wym_status wym_section'>"
+ + "<h2>{Status}</h2>"
+ + "</div>",
+
+ htmlHtml: "<div class='wym_html wym_section'>"
+ + "<h2>{Source_Code}</h2>"
+ + "<textarea class='wym_html_val'></textarea>"
+ + "</div>",
+
+ boxSelector: ".wym_box",
+ toolsSelector: ".wym_tools",
+ toolsListSelector: " ul",
+ containersSelector:".wym_containers",
+ classesSelector: ".wym_classes",
+ htmlSelector: ".wym_html",
+ iframeSelector: ".wym_iframe iframe",
+ iframeBodySelector:".wym_iframe",
+ statusSelector: ".wym_status",
+ toolSelector: ".wym_tools a",
+ containerSelector: ".wym_containers a",
+ classSelector: ".wym_classes a",
+ htmlValSelector: ".wym_html_val",
+
+ hrefSelector: ".wym_href",
+ srcSelector: ".wym_src",
+ titleSelector: ".wym_title",
+ altSelector: ".wym_alt",
+ textSelector: ".wym_text",
+
+ rowsSelector: ".wym_rows",
+ colsSelector: ".wym_cols",
+ captionSelector: ".wym_caption",
+ summarySelector: ".wym_summary",
+
+ submitSelector: ".wym_submit",
+ cancelSelector: ".wym_cancel",
+ previewSelector: "",
+
+ dialogTypeSelector: ".wym_dialog_type",
+ dialogLinkSelector: ".wym_dialog_link",
+ dialogImageSelector: ".wym_dialog_image",
+ dialogTableSelector: ".wym_dialog_table",
+ dialogPasteSelector: ".wym_dialog_paste",
+ dialogPreviewSelector: ".wym_dialog_preview",
+
+ updateSelector: ".wymupdate",
+ updateEvent: "click",
+
+ dialogFeatures: "menubar=no,titlebar=no,toolbar=no,resizable=no"
+ + ",width=560,height=300,top=0,left=0",
+ dialogFeaturesPreview: "menubar=no,titlebar=no,toolbar=no,resizable=no"
+ + ",scrollbars=yes,width=560,height=300,top=0,left=0",
+
+ dialogHtml: "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'"
+ + " 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>"
+ + "<html dir='"
+ + WYMeditor.DIRECTION
+ + "'><head>"
+ + "<link rel='stylesheet' type='text/css' media='screen'"
+ + " href='"
+ + WYMeditor.CSS_PATH
+ + "' />"
+ + "<title>"
+ + WYMeditor.DIALOG_TITLE
+ + "</title>"
+ + "<script type='text/javascript'"
+ + " src='"
+ + WYMeditor.JQUERY_PATH
+ + "'></script>"
+ + "<script type='text/javascript'"
+ + " src='"
+ + WYMeditor.WYM_PATH
+ + "'></script>"
+ + "</head>"
+ + WYMeditor.DIALOG_BODY
+ + "</html>",
+
+ dialogLinkHtml: "<body class='wym_dialog wym_dialog_link'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_LINK
+ + "' />"
+ + "<legend>{Link}</legend>"
+ + "<div class='row'>"
+ + "<label>{URL}</label>"
+ + "<input type='text' class='wym_href' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Title}</label>"
+ + "<input type='text' class='wym_title' value='' size='40' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='button'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogImageHtml: "<body class='wym_dialog wym_dialog_image'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_IMAGE
+ + "' />"
+ + "<legend>{Image}</legend>"
+ + "<div class='row'>"
+ + "<label>{URL}</label>"
+ + "<input type='text' class='wym_src' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Alternative_Text}</label>"
+ + "<input type='text' class='wym_alt' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Title}</label>"
+ + "<input type='text' class='wym_title' value='' size='40' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='button'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogTableHtml: "<body class='wym_dialog wym_dialog_table'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_TABLE
+ + "' />"
+ + "<legend>{Table}</legend>"
+ + "<div class='row'>"
+ + "<label>{Caption}</label>"
+ + "<input type='text' class='wym_caption' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Summary}</label>"
+ + "<input type='text' class='wym_summary' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Number_Of_Rows}</label>"
+ + "<input type='text' class='wym_rows' value='3' size='3' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Number_Of_Cols}</label>"
+ + "<input type='text' class='wym_cols' value='2' size='3' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='button'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogPasteHtml: "<body class='wym_dialog wym_dialog_paste'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_PASTE
+ + "' />"
+ + "<fieldset>"
+ + "<legend>{Paste_From_Word}</legend>"
+ + "<div class='row'>"
+ + "<textarea class='wym_text' rows='10' cols='50'></textarea>"
+ + "</div>"
+ + "<div class='row'>"
+ + "<input class='wym_submit' type='button'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogPreviewHtml: "<body class='wym_dialog wym_dialog_preview'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + "></body>",
+
+ dialogStyles: [],
+
+ stringDelimiterLeft: "{",
+ stringDelimiterRight:"}",
+
+ preInit: null,
+ preBind: null,
+ postInit: null,
+
+ preInitDialog: null,
+ postInitDialog: null
+
+ }, options);
+
+ return this.each(function() {
+
+ new WYMeditor.editor(jQuery(this),options);
+ });
+};
+
+/* @name extend
+ * @description Returns the WYMeditor instance based on its index
+ */
+jQuery.extend({
+ wymeditors: function(i) {
+ return (WYMeditor.INSTANCES[i]);
+ }
+});
+
+
+/********** WYMeditor **********/
+
+/* @name Wymeditor
+ * @description WYMeditor class
+ */
+
+/* @name init
+ * @description Initializes a WYMeditor instance
+ */
+WYMeditor.editor.prototype.init = function() {
+
+ //load subclass - browser specific
+ //unsupported browsers: do nothing
+ if (jQuery.browser.msie) {
+ var WymClass = new WYMeditor.WymClassExplorer(this);
+ }
+ else if (jQuery.browser.mozilla) {
+ var WymClass = new WYMeditor.WymClassMozilla(this);
+ }
+ else if (jQuery.browser.opera) {
+ var WymClass = new WYMeditor.WymClassOpera(this);
+ }
+ else if (jQuery.browser.safari) {
+ var WymClass = new WYMeditor.WymClassSafari(this);
+ }
+
+ if(WymClass) {
+
+ if(jQuery.isFunction(this._options.preInit)) this._options.preInit(this);
+
+ var SaxListener = new WYMeditor.XhtmlSaxListener();
+ jQuery.extend(SaxListener, WymClass);
+ this.parser = new WYMeditor.XhtmlParser(SaxListener);
+
+ if(this._options.styles || this._options.stylesheet){
+ this.configureEditorUsingRawCss();
+ }
+
+ this.helper = new WYMeditor.XmlHelper();
+
+ //extend the Wymeditor object
+ //don't use jQuery.extend since 1.1.4
+ //jQuery.extend(this, WymClass);
+ for (var prop in WymClass) { this[prop] = WymClass[prop]; }
+
+ //load wymbox
+ this._box = jQuery(this._element).hide().after(this._options.boxHtml).next().addClass('wym_box_' + this._index);
+
+ //store the instance index in wymbox and element replaced by editor instance
+ //but keep it compatible with jQuery < 1.2.3, see #122
+ if( jQuery.isFunction( jQuery.fn.data ) ) {
+ jQuery.data(this._box.get(0), WYMeditor.WYM_INDEX, this._index);
+ jQuery.data(this._element.get(0), WYMeditor.WYM_INDEX, this._index);
+ }
+
+ var h = WYMeditor.Helper;
+
+ //construct the iframe
+ var iframeHtml = this._options.iframeHtml;
+ iframeHtml = h.replaceAll(iframeHtml, WYMeditor.INDEX, this._index);
+ iframeHtml = h.replaceAll(iframeHtml, WYMeditor.IFRAME_BASE_PATH, this._options.iframeBasePath);
+
+ //construct wymbox
+ var boxHtml = jQuery(this._box).html();
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.LOGO, this._options.logoHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS, this._options.toolsHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS,this._options.containersHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES, this._options.classesHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.HTML, this._options.htmlHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.IFRAME, iframeHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.STATUS, this._options.statusHtml);
+
+ //construct tools list
+ var aTools = eval(this._options.toolsItems);
+ var sTools = "";
+
+ for(var i = 0; i < aTools.length; i++) {
+ var oTool = aTools[i];
+ if(oTool.name && oTool.title)
+ var sTool = this._options.toolsItemHtml;
+ var sTool = h.replaceAll(sTool, WYMeditor.TOOL_NAME, oTool.name);
+ sTool = h.replaceAll(sTool, WYMeditor.TOOL_TITLE, this._options.stringDelimiterLeft
+ + oTool.title
+ + this._options.stringDelimiterRight);
+ sTool = h.replaceAll(sTool, WYMeditor.TOOL_CLASS, oTool.css);
+ sTools += sTool;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS_ITEMS, sTools);
+
+ //construct classes list
+ var aClasses = eval(this._options.classesItems);
+ var sClasses = "";
+
+ for(var i = 0; i < aClasses.length; i++) {
+ var oClass = aClasses[i];
+ if(oClass.name && oClass.title)
+ var sClass = this._options.classesItemHtml;
+ sClass = h.replaceAll(sClass, WYMeditor.CLASS_NAME, oClass.name);
+ sClass = h.replaceAll(sClass, WYMeditor.CLASS_TITLE, oClass.title);
+ sClasses += sClass;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES_ITEMS, sClasses);
+
+ //construct containers list
+ var aContainers = eval(this._options.containersItems);
+ var sContainers = "";
+
+ for(var i = 0; i < aContainers.length; i++) {
+ var oContainer = aContainers[i];
+ if(oContainer.name && oContainer.title)
+ var sContainer = this._options.containersItemHtml;
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_NAME, oContainer.name);
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_TITLE,
+ this._options.stringDelimiterLeft
+ + oContainer.title
+ + this._options.stringDelimiterRight);
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_CLASS, oContainer.css);
+ sContainers += sContainer;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS_ITEMS, sContainers);
+
+ //l10n
+ boxHtml = this.replaceStrings(boxHtml);
+
+ //load html in wymbox
+ jQuery(this._box).html(boxHtml);
+
+ //hide the html value
+ jQuery(this._box).find(this._options.htmlSelector).hide();
+
+ //enable the skin
+ this.loadSkin();
+
+ }
+};
+
+WYMeditor.editor.prototype.bindEvents = function() {
+
+ //copy the instance
+ var wym = this;
+
+ //handle click event on tools buttons
+ jQuery(this._box).find(this._options.toolSelector).click(function() {
+ wym._iframe.contentWindow.focus(); //See #154
+ wym.exec(jQuery(this).attr(WYMeditor.NAME));
+ return(false);
+ });
+
+ //handle click event on containers buttons
+ jQuery(this._box).find(this._options.containerSelector).click(function() {
+ wym.container(jQuery(this).attr(WYMeditor.NAME));
+ return(false);
+ });
+
+ //handle keyup event on html value: set the editor value
+ //handle focus/blur events to check if the element has focus, see #147
+ jQuery(this._box).find(this._options.htmlValSelector)
+ .keyup(function() { jQuery(wym._doc.body).html(jQuery(this).val());})
+ .focus(function() { jQuery(this).toggleClass('hasfocus'); })
+ .blur(function() { jQuery(this).toggleClass('hasfocus'); });
+
+ //handle click event on classes buttons
+ jQuery(this._box).find(this._options.classSelector).click(function() {
+
+ var aClasses = eval(wym._options.classesItems);
+ var sName = jQuery(this).attr(WYMeditor.NAME);
+
+ var oClass = WYMeditor.Helper.findByName(aClasses, sName);
+
+ if(oClass) {
+ var jqexpr = oClass.expr;
+ wym.toggleClass(sName, jqexpr);
+ }
+ wym._iframe.contentWindow.focus(); //See #154
+ return(false);
+ });
+
+ //handle event on update element
+ jQuery(this._options.updateSelector)
+ .bind(this._options.updateEvent, function() {
+ wym.update();
+ });
+};
+
+WYMeditor.editor.prototype.ready = function() {
+ return(this._doc != null);
+};
+
+
+/********** METHODS **********/
+
+/* @name box
+ * @description Returns the WYMeditor container
+ */
+WYMeditor.editor.prototype.box = function() {
+ return(this._box);
+};
+
+/* @name html
+ * @description Get/Set the html value
+ */
+WYMeditor.editor.prototype.html = function(html) {
+
+ if(typeof html === 'string') jQuery(this._doc.body).html(html);
+ else return(jQuery(this._doc.body).html());
+};
+
+/* @name xhtml
+ * @description Cleans up the HTML
+ */
+WYMeditor.editor.prototype.xhtml = function() {
+ return this.parser.parse(this.html());
+};
+
+/* @name exec
+ * @description Executes a button command
+ */
+WYMeditor.editor.prototype.exec = function(cmd) {
+
+ //base function for execCommand
+ //open a dialog or exec
+ switch(cmd) {
+ case WYMeditor.CREATE_LINK:
+ var container = this.container();
+ if(container || this._selected_image) this.dialog(WYMeditor.DIALOG_LINK);
+ break;
+
+ case WYMeditor.INSERT_IMAGE:
+ this.dialog(WYMeditor.DIALOG_IMAGE);
+ break;
+
+ case WYMeditor.INSERT_TABLE:
+ this.dialog(WYMeditor.DIALOG_TABLE);
+ break;
+
+ case WYMeditor.PASTE:
+ this.dialog(WYMeditor.DIALOG_PASTE);
+ break;
+
+ case WYMeditor.TOGGLE_HTML:
+ this.update();
+ this.toggleHtml();
+ break;
+
+ case WYMeditor.PREVIEW:
+ this.dialog(WYMeditor.PREVIEW, this._options.dialogFeaturesPreview);
+ break;
+
+ default:
+ this._exec(cmd);
+ break;
+ }
+};
+
+/* @name container
+ * @description Get/Set the selected container
+ */
+WYMeditor.editor.prototype.container = function(sType) {
+
+ if(sType) {
+
+ var container = null;
+
+ if(sType.toLowerCase() == WYMeditor.TH) {
+
+ container = this.container();
+
+ //find the TD or TH container
+ switch(container.tagName.toLowerCase()) {
+
+ case WYMeditor.TD: case WYMeditor.TH:
+ break;
+ default:
+ var aTypes = new Array(WYMeditor.TD,WYMeditor.TH);
+ container = this.findUp(this.container(), aTypes);
+ break;
+ }
+
+ //if it exists, switch
+ if(container!=null) {
+
+ sType = (container.tagName.toLowerCase() == WYMeditor.TD)? WYMeditor.TH: WYMeditor.TD;
+ this.switchTo(container,sType);
+ this.update();
+ }
+ } else {
+
+ //set the container type
+ var aTypes=new Array(WYMeditor.P,WYMeditor.H1,WYMeditor.H2,WYMeditor.H3,WYMeditor.H4,WYMeditor.H5,
+ WYMeditor.H6,WYMeditor.PRE,WYMeditor.BLOCKQUOTE);
+ container = this.findUp(this.container(), aTypes);
+
+ if(container) {
+
+ var newNode = null;
+
+ //blockquotes must contain a block level element
+ if(sType.toLowerCase() == WYMeditor.BLOCKQUOTE) {
+
+ var blockquote = this.findUp(this.container(), WYMeditor.BLOCKQUOTE);
+
+ if(blockquote == null) {
+
+ newNode = this._doc.createElement(sType);
+ container.parentNode.insertBefore(newNode,container);
+ newNode.appendChild(container);
+ this.setFocusToNode(newNode.firstChild);
+
+ } else {
+
+ var nodes = blockquote.childNodes;
+ var lgt = nodes.length;
+ var firstNode = null;
+
+ if(lgt > 0) firstNode = nodes.item(0);
+ for(var x=0; x<lgt; x++) {
+ blockquote.parentNode.insertBefore(nodes.item(0),blockquote);
+ }
+ blockquote.parentNode.removeChild(blockquote);
+ if(firstNode) this.setFocusToNode(firstNode);
+ }
+ }
+
+ else this.switchTo(container,sType);
+
+ this.update();
+ }
+ }
+ }
+ else return(this.selected());
+};
+
+/* @name toggleClass
+ * @description Toggles class on selected element, or one of its parents
+ */
+WYMeditor.editor.prototype.toggleClass = function(sClass, jqexpr) {
+
+ var container = (this._selected_image
+ ? this._selected_image
+ : jQuery(this.selected()));
+ container = jQuery(container).parentsOrSelf(jqexpr);
+ jQuery(container).toggleClass(sClass);
+
+ if(!jQuery(container).attr(WYMeditor.CLASS)) jQuery(container).removeAttr(this._class);
+
+};
+
+/* @name findUp
+ * @description Returns the first parent or self container, based on its type
+ */
+WYMeditor.editor.prototype.findUp = function(node, filter) {
+
+ //filter is a string or an array of strings
+
+ if(node) {
+
+ var tagname = node.tagName.toLowerCase();
+
+ if(typeof(filter) == WYMeditor.STRING) {
+
+ while(tagname != filter && tagname != WYMeditor.BODY) {
+
+ node = node.parentNode;
+ tagname = node.tagName.toLowerCase();
+ }
+
+ } else {
+
+ var bFound = false;
+
+ while(!bFound && tagname != WYMeditor.BODY) {
+ for(var i = 0; i < filter.length; i++) {
+ if(tagname == filter[i]) {
+ bFound = true;
+ break;
+ }
+ }
+ if(!bFound) {
+ node = node.parentNode;
+ tagname = node.tagName.toLowerCase();
+ }
+ }
+ }
+
+ if(tagname != WYMeditor.BODY) return(node);
+ else return(null);
+
+ } else return(null);
+};
+
+/* @name switchTo
+ * @description Switch the node's type
+ */
+WYMeditor.editor.prototype.switchTo = function(node,sType) {
+
+ var newNode = this._doc.createElement(sType);
+ var html = jQuery(node).html();
+ node.parentNode.replaceChild(newNode,node);
+ jQuery(newNode).html(html);
+ this.setFocusToNode(newNode);
+};
+
+WYMeditor.editor.prototype.replaceStrings = function(sVal) {
+ //check if the language file has already been loaded
+ //if not, get it via a synchronous ajax call
+ if(!WYMeditor.STRINGS[this._options.lang]) {
+ try {
+ eval(jQuery.ajax({url:this._options.langPath
+ + this._options.lang + '.js', async:false}).responseText);
+ } catch(e) {
+ WYMeditor.console.error("WYMeditor: error while parsing language file.");
+ return sVal;
+ }
+ }
+
+ //replace all the strings in sVal and return it
+ for (var key in WYMeditor.STRINGS[this._options.lang]) {
+ sVal = WYMeditor.Helper.replaceAll(sVal, this._options.stringDelimiterLeft + key
+ + this._options.stringDelimiterRight,
+ WYMeditor.STRINGS[this._options.lang][key]);
+ };
+ return(sVal);
+};
+
+WYMeditor.editor.prototype.encloseString = function(sVal) {
+
+ return(this._options.stringDelimiterLeft
+ + sVal
+ + this._options.stringDelimiterRight);
+};
+
+/* @name status
+ * @description Prints a status message
+ */
+WYMeditor.editor.prototype.status = function(sMessage) {
+
+ //print status message
+ jQuery(this._box).find(this._options.statusSelector).html(sMessage);
+};
+
+/* @name update
+ * @description Updates the element and textarea values
+ */
+WYMeditor.editor.prototype.update = function() {
+
+ var html = this.xhtml();
+ jQuery(this._element).val(html);
+ jQuery(this._box).find(this._options.htmlValSelector).not('.hasfocus').val(html); //#147
+};
+
+/* @name dialog
+ * @description Opens a dialog box
+ */
+WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) {
+
+ var features = dialogFeatures || this._wym._options.dialogFeatures;
+ var wDialog = window.open('', 'dialog', features);
+
+ if(wDialog) {
+
+ var sBodyHtml = "";
+
+ switch( dialogType ) {
+
+ case(WYMeditor.DIALOG_LINK):
+ sBodyHtml = this._options.dialogLinkHtml;
+ break;
+ case(WYMeditor.DIALOG_IMAGE):
+ sBodyHtml = this._options.dialogImageHtml;
+ break;
+ case(WYMeditor.DIALOG_TABLE):
+ sBodyHtml = this._options.dialogTableHtml;
+ break;
+ case(WYMeditor.DIALOG_PASTE):
+ sBodyHtml = this._options.dialogPasteHtml;
+ break;
+ case(WYMeditor.PREVIEW):
+ sBodyHtml = this._options.dialogPreviewHtml;
+ break;
+
+ default:
+ sBodyHtml = bodyHtml;
+ }
+
+ var h = WYMeditor.Helper;
+
+ //construct the dialog
+ var dialogHtml = this._options.dialogHtml;
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.BASE_PATH, this._options.basePath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIRECTION, this._options.direction);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.CSS_PATH, this._options.skinPath + WYMeditor.SKINS_DEFAULT_CSS);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.WYM_PATH, this._options.wymPath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.JQUERY_PATH, this._options.jQueryPath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_TITLE, this.encloseString( dialogType ));
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_BODY, sBodyHtml);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.INDEX, this._index);
+
+ dialogHtml = this.replaceStrings(dialogHtml);
+
+ var doc = wDialog.document;
+ doc.write(dialogHtml);
+ doc.close();
+ }
+};
+
+/* @name toggleHtml
+ * @description Shows/Hides the HTML
+ */
+WYMeditor.editor.prototype.toggleHtml = function() {
+ jQuery(this._box).find(this._options.htmlSelector).toggle();
+};
+
+WYMeditor.editor.prototype.uniqueStamp = function() {
+ var now = new Date();
+ return("wym-" + now.getTime());
+};
+
+WYMeditor.editor.prototype.paste = function(sData) {
+
+ var sTmp;
+ var container = this.selected();
+
+ //split the data, using double newlines as the separator
+ var aP = sData.split(this._newLine + this._newLine);
+ var rExp = new RegExp(this._newLine, "g");
+
+ //add a P for each item
+ if(container && container.tagName.toLowerCase() != WYMeditor.BODY) {
+ for(x = aP.length - 1; x >= 0; x--) {
+ sTmp = aP[x];
+ //simple newlines are replaced by a break
+ sTmp = sTmp.replace(rExp, "<br />");
+ jQuery(container).after("<p>" + sTmp + "</p>");
+ }
+ } else {
+ for(x = 0; x < aP.length; x++) {
+ sTmp = aP[x];
+ //simple newlines are replaced by a break
+ sTmp = sTmp.replace(rExp, "<br />");
+ jQuery(this._doc.body).append("<p>" + sTmp + "</p>");
+ }
+
+ }
+};
+
+WYMeditor.editor.prototype.insert = function(html) {
+ // Do we have a selection?
+ if (this._iframe.contentWindow.getSelection().focusNode != null) {
+ // Overwrite selection with provided html
+ this._exec( WYMeditor.INSERT_HTML, html);
+ } else {
+ // Fall back to the internal paste function if there's no selection
+ this.paste(html)
+ }
+};
+
+WYMeditor.editor.prototype.wrap = function(left, right) {
+ // Do we have a selection?
+ if (this._iframe.contentWindow.getSelection().focusNode != null) {
+ // Wrap selection with provided html
+ this._exec( WYMeditor.INSERT_HTML, left + this._iframe.contentWindow.getSelection().toString() + right);
+ }
+};
+
+WYMeditor.editor.prototype.unwrap = function() {
+ // Do we have a selection?
+ if (this._iframe.contentWindow.getSelection().focusNode != null) {
+ // Unwrap selection
+ this._exec( WYMeditor.INSERT_HTML, this._iframe.contentWindow.getSelection().toString() );
+ }
+};
+
+WYMeditor.editor.prototype.setFocusToNode = function(node, toStart) {
+ var range = this._doc.createRange(),
+ selection = this._iframe.contentWindow.getSelection();
+ toStart = toStart ? 0 : 1;
+
+ range.selectNodeContents(node);
+ selection.addRange(range);
+ selection.collapse(node, toStart);
+ this._iframe.contentWindow.focus();
+};
+
+WYMeditor.editor.prototype.addCssRules = function(doc, aCss) {
+ var styles = doc.styleSheets[0];
+ if(styles) {
+ for(var i = 0; i < aCss.length; i++) {
+ var oCss = aCss[i];
+ if(oCss.name && oCss.css) this.addCssRule(styles, oCss);
+ }
+ }
+};
+
+/********** CONFIGURATION **********/
+
+WYMeditor.editor.prototype.computeBasePath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src').replace(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/, '');
+};
+
+WYMeditor.editor.prototype.computeWymPath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src');
+};
+
+WYMeditor.editor.prototype.computeJqueryPath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery(-(.*)){0,1}(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src');
+};
+
+WYMeditor.editor.prototype.computeCssPath = function() {
+ return jQuery(jQuery.grep(jQuery('link'), function(s){
+ return (s.href && s.href.match(/wymeditor\/skins\/(.*)screen\.css(\?.*)?$/ ))
+ })).attr('href');
+};
+
+WYMeditor.editor.prototype.configureEditorUsingRawCss = function() {
+
+ var CssParser = new WYMeditor.WymCssParser();
+ if(this._options.stylesheet){
+ CssParser.parse(jQuery.ajax({url: this._options.stylesheet,async:false}).responseText);
+ }else{
+ CssParser.parse(this._options.styles, false);
+ }
+
+ if(this._options.classesItems.length == 0) {
+ this._options.classesItems = CssParser.css_settings.classesItems;
+ }
+ if(this._options.editorStyles.length == 0) {
+ this._options.editorStyles = CssParser.css_settings.editorStyles;
+ }
+ if(this._options.dialogStyles.length == 0) {
+ this._options.dialogStyles = CssParser.css_settings.dialogStyles;
+ }
+};
+
+/********** EVENTS **********/
+
+WYMeditor.editor.prototype.listen = function() {
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ //jQuery(this._doc.body).find("*").bind("mouseup", this.mouseup);
+
+ jQuery(this._doc.body).bind("mousedown", this.mousedown);
+};
+
+WYMeditor.editor.prototype.mousedown = function(evt) {
+ var wym = WYMeditor.INSTANCES[this.ownerDocument.title];
+ wym._selected_image = (evt.target.tagName.toLowerCase() == WYMeditor.IMG) ? evt.target : null;
+};
+
+/********** SKINS **********/
+
+/*
+ * Function: WYMeditor.loadCss
+ * Loads a stylesheet in the document.
+ *
+ * Parameters:
+ * href - The CSS path.
+ */
+WYMeditor.loadCss = function(href) {
+
+ var link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = href;
+
+ var head = jQuery('head').get(0);
+ head.appendChild(link);
+};
+
+/*
+ * Function: WYMeditor.editor.loadSkin
+ * Loads the skin CSS and initialization script (if needed).
+ */
+WYMeditor.editor.prototype.loadSkin = function() {
+
+ //does the user want to automatically load the CSS (default: yes)?
+ //we also test if it hasn't been already loaded by another instance
+ //see below for a better (second) test
+ if(this._options.loadSkin && !WYMeditor.SKINS[this._options.skin]) {
+
+ //check if it hasn't been already loaded
+ //so we don't load it more than once
+ //(we check the existing <link> elements)
+
+ var found = false;
+ var rExp = new RegExp(this._options.skin
+ + '\/' + WYMeditor.SKINS_DEFAULT_CSS + '$');
+
+ jQuery('link').each( function() {
+ if(this.href.match(rExp)) found = true;
+ });
+
+ //load it, using the skin path
+ if(!found) WYMeditor.loadCss( this._options.skinPath
+ + WYMeditor.SKINS_DEFAULT_CSS );
+ }
+
+ //put the classname (ex. wym_skin_default) on wym_box
+ jQuery(this._box).addClass( "wym_skin_" + this._options.skin );
+
+ //does the user want to use some JS to initialize the skin (default: yes)?
+ //also check if it hasn't already been loaded by another instance
+ if(this._options.initSkin && !WYMeditor.SKINS[this._options.skin]) {
+
+ eval(jQuery.ajax({url:this._options.skinPath
+ + WYMeditor.SKINS_DEFAULT_JS, async:false}).responseText);
+ }
+
+ //init the skin, if needed
+ if(WYMeditor.SKINS[this._options.skin]
+ && WYMeditor.SKINS[this._options.skin].init)
+ WYMeditor.SKINS[this._options.skin].init(this);
+
+};
+
+
+/********** DIALOGS **********/
+
+WYMeditor.INIT_DIALOG = function(index) {
+
+ var wym = window.opener.WYMeditor.INSTANCES[index];
+ var doc = window.document;
+ var selected = wym.selected();
+ var dialogType = jQuery(wym._options.dialogTypeSelector).val();
+ var sStamp = wym.uniqueStamp();
+
+ switch(dialogType) {
+
+ case WYMeditor.DIALOG_LINK:
+ //ensure that we select the link to populate the fields
+ if(selected && selected.tagName && selected.tagName.toLowerCase != WYMeditor.A)
+ selected = jQuery(selected).parentsOrSelf(WYMeditor.A);
+
+ //fix MSIE selection if link image has been clicked
+ if(!selected && wym._selected_image)
+ selected = jQuery(wym._selected_image).parentsOrSelf(WYMeditor.A);
+ break;
+
+ }
+
+ //pre-init functions
+ if(jQuery.isFunction(wym._options.preInitDialog))
+ wym._options.preInitDialog(wym,window);
+
+ //add css rules from options
+ var styles = doc.styleSheets[0];
+ var aCss = eval(wym._options.dialogStyles);
+
+ wym.addCssRules(doc, aCss);
+
+ //auto populate fields if selected container (e.g. A)
+ if(selected) {
+ jQuery(wym._options.hrefSelector).val(jQuery(selected).attr(WYMeditor.HREF));
+ jQuery(wym._options.srcSelector).val(jQuery(selected).attr(WYMeditor.SRC));
+ jQuery(wym._options.titleSelector).val(jQuery(selected).attr(WYMeditor.TITLE));
+ jQuery(wym._options.altSelector).val(jQuery(selected).attr(WYMeditor.ALT));
+ }
+
+ //auto populate image fields if selected image
+ if(wym._selected_image) {
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.srcSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.SRC));
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.titleSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.TITLE));
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.altSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.ALT));
+ }
+
+ jQuery(wym._options.dialogLinkSelector + " "
+ + wym._options.submitSelector).click(function() {
+
+ var sUrl = jQuery(wym._options.hrefSelector).val();
+ if(sUrl.length > 0) {
+ var link;
+
+ if (selected[0] && selected[0].tagName.toLowerCase() == WYMeditor.A) {
+ link = selected;
+ } else {
+ wym._exec(WYMeditor.CREATE_LINK, sStamp);
+ link = jQuery("a[href=" + sStamp + "]", wym._doc.body);
+ }
+
+ link.attr(WYMeditor.HREF, sUrl)
+ .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val());
+
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogImageSelector + " "
+ + wym._options.submitSelector).click(function() {
+
+ var sUrl = jQuery(wym._options.srcSelector).val();
+ if(sUrl.length > 0) {
+
+ wym._exec(WYMeditor.INSERT_IMAGE, sStamp);
+
+ jQuery("img[src$=" + sStamp + "]", wym._doc.body)
+ .attr(WYMeditor.SRC, sUrl)
+ .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val())
+ .attr(WYMeditor.ALT, jQuery(wym._options.altSelector).val());
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogTableSelector + " "
+ + wym._options.submitSelector).click(function() {
+
+ var iRows = jQuery(wym._options.rowsSelector).val();
+ var iCols = jQuery(wym._options.colsSelector).val();
+
+ if(iRows > 0 && iCols > 0) {
+
+ var table = wym._doc.createElement(WYMeditor.TABLE);
+ var newRow = null;
+ var newCol = null;
+
+ var sCaption = jQuery(wym._options.captionSelector).val();
+
+ //we create the caption
+ var newCaption = table.createCaption();
+ newCaption.innerHTML = sCaption;
+
+ //we create the rows and cells
+ for(x=0; x<iRows; x++) {
+ newRow = table.insertRow(x);
+ for(y=0; y<iCols; y++) {newRow.insertCell(y);}
+ }
+
+ //set the summary attr
+ jQuery(table).attr('summary',
+ jQuery(wym._options.summarySelector).val());
+
+ //append the table after the selected container
+ var node = jQuery(wym.findUp(wym.container(),
+ WYMeditor.MAIN_CONTAINERS)).get(0);
+ if(!node || !node.parentNode) jQuery(wym._doc.body).append(table);
+ else jQuery(node).after(table);
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogPasteSelector + " "
+ + wym._options.submitSelector).click(function() {
+
+ var sText = jQuery(wym._options.textSelector).val();
+ wym.paste(sText);
+ window.close();
+ });
+
+ jQuery(wym._options.dialogPreviewSelector + " "
+ + wym._options.previewSelector)
+ .html(wym.xhtml());
+
+ //cancel button
+ jQuery(wym._options.cancelSelector).mousedown(function() {
+ window.close();
+ });
+
+ //pre-init functions
+ if(jQuery.isFunction(wym._options.postInitDialog))
+ wym._options.postInitDialog(wym,window);
+
+};
+
+/********** XHTML LEXER/PARSER **********/
+
+/*
+* @name xml
+* @description Use these methods to generate XML and XHTML compliant tags and
+* escape tag attributes correctly
+* @author Bermi Ferrer - http://bermi.org
+* @author David Heinemeier Hansson http://loudthinking.com
+*/
+WYMeditor.XmlHelper = function()
+{
+ this._entitiesDiv = document.createElement('div');
+ return this;
+};
+
+
+/*
+* @name tag
+* @description
+* Returns an empty HTML tag of type *name* which by default is XHTML
+* compliant. Setting *open* to true will create an open tag compatible
+* with HTML 4.0 and below. Add HTML attributes by passing an attributes
+* array to *options*. For attributes with no value like (disabled and
+* readonly), give it a value of true in the *options* array.
+*
+* Examples:
+*
+* this.tag('br')
+* # => <br />
+* this.tag ('br', false, true)
+* # => <br>
+* this.tag ('input', jQuery({type:'text',disabled:true }) )
+* # => <input type="text" disabled="disabled" />
+*/
+WYMeditor.XmlHelper.prototype.tag = function(name, options, open)
+{
+ options = options || false;
+ open = open || false;
+ return '<'+name+(options ? this.tagOptions(options) : '')+(open ? '>' : ' />');
+};
+
+/*
+* @name contentTag
+* @description
+* Returns a XML block tag of type *name* surrounding the *content*. Add
+* XML attributes by passing an attributes array to *options*. For attributes
+* with no value like (disabled and readonly), give it a value of true in
+* the *options* array. You can use symbols or strings for the attribute names.
+*
+* this.contentTag ('p', 'Hello world!' )
+* # => <p>Hello world!</p>
+* this.contentTag('div', this.contentTag('p', "Hello world!"), jQuery({class : "strong"}))
+* # => <div class="strong"><p>Hello world!</p></div>
+* this.contentTag("select", options, jQuery({multiple : true}))
+* # => <select multiple="multiple">...options...</select>
+*/
+WYMeditor.XmlHelper.prototype.contentTag = function(name, content, options)
+{
+ options = options || false;
+ return '<'+name+(options ? this.tagOptions(options) : '')+'>'+content+'</'+name+'>';
+};
+
+/*
+* @name cdataSection
+* @description
+* Returns a CDATA section for the given +content+. CDATA sections
+* are used to escape blocks of text containing characters which would
+* otherwise be recognized as markup. CDATA sections begin with the string
+* <tt>&lt;![CDATA[</tt> and } with (and may not contain) the string
+* <tt>]]></tt>.
+*/
+WYMeditor.XmlHelper.prototype.cdataSection = function(content)
+{
+ return '<![CDATA['+content+']]>';
+};
+
+
+/*
+* @name escapeOnce
+* @description
+* Returns the escaped +xml+ without affecting existing escaped entities.
+*
+* this.escapeOnce( "1 > 2 &amp; 3")
+* # => "1 &gt; 2 &amp; 3"
+*/
+WYMeditor.XmlHelper.prototype.escapeOnce = function(xml)
+{
+ return this._fixDoubleEscape(this.escapeEntities(xml));
+};
+
+/*
+* @name _fixDoubleEscape
+* @description
+* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc.
+*/
+WYMeditor.XmlHelper.prototype._fixDoubleEscape = function(escaped)
+{
+ return escaped.replace(/&amp;([a-z]+|(#\d+));/ig, "&$1;");
+};
+
+/*
+* @name tagOptions
+* @description
+* Takes an array like the one generated by Tag.parseAttributes
+* [["src", "http://www.editam.com/?a=b&c=d&amp;f=g"], ["title", "Editam, <Simplified> CMS"]]
+* or an object like {src:"http://www.editam.com/?a=b&c=d&amp;f=g", title:"Editam, <Simplified> CMS"}
+* and returns a string properly escaped like
+* ' src = "http://www.editam.com/?a=b&amp;c=d&amp;f=g" title = "Editam, &lt;Simplified&gt; CMS"'
+* which is valid for strict XHTML
+*/
+WYMeditor.XmlHelper.prototype.tagOptions = function(options)
+{
+ var xml = this;
+ xml._formated_options = '';
+
+ for (var key in options) {
+ var formated_options = '';
+ var value = options[key];
+ if(typeof value != 'function' && value.length > 0) {
+
+ if(parseInt(key) == key && typeof value == 'object'){
+ key = value.shift();
+ value = value.pop();
+ }
+ if(key != '' && value != ''){
+ xml._formated_options += ' '+key+'="'+xml.escapeOnce(value)+'"';
+ }
+ }
+ }
+ return xml._formated_options;
+};
+
+/*
+* @name escapeEntities
+* @description
+* Escapes XML/HTML entities <, >, & and ". If seccond parameter is set to false it
+* will not escape ". If set to true it will also escape '
+*/
+WYMeditor.XmlHelper.prototype.escapeEntities = function(string, escape_quotes)
+{
+ this._entitiesDiv.innerHTML = string;
+ this._entitiesDiv.textContent = string;
+ var result = this._entitiesDiv.innerHTML;
+ if(typeof escape_quotes == 'undefined'){
+ if(escape_quotes != false) result = result.replace('"', '&quot;');
+ if(escape_quotes == true) result = result.replace('"', '&#039;');
+ }
+ return result;
+};
+
+/*
+* Parses a string conatining tag attributes and values an returns an array formated like
+* [["src", "http://www.editam.com"], ["title", "Editam, Simplified CMS"]]
+*/
+WYMeditor.XmlHelper.prototype.parseAttributes = function(tag_attributes)
+{
+ // Use a compounded regex to match single quoted, double quoted and unquoted attribute pairs
+ var result = [];
+ var matches = tag_attributes.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g);
+ if(matches.toString() != tag_attributes){
+ for (var k in matches) {
+ var v = matches[k];
+ if(typeof v != 'function' && v.length != 0){
+ var re = new RegExp('(\\w+)\\s*'+v);
+ if(match = tag_attributes.match(re) ){
+ var value = v.replace(/^[\s=]+/, "");
+ var delimiter = value.charAt(0);
+ delimiter = delimiter == '"' ? '"' : (delimiter=="'"?"'":'');
+ if(delimiter != ''){
+ value = delimiter == '"' ? value.replace(/^"|"+$/g, '') : value.replace(/^'|'+$/g, '');
+ }
+ tag_attributes = tag_attributes.replace(match[0],'');
+ result.push([match[1] , value]);
+ }
+ }
+ }
+ }
+ return result;
+};
+
+/**
+* XhtmlValidator for validating tag attributes
+*
+* @author Bermi Ferrer - http://bermi.org
+*/
+WYMeditor.XhtmlValidator = {
+ "_attributes":
+ {
+ "core":
+ {
+ "except":[
+ "base",
+ "head",
+ "html",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "class",
+ "id",
+ "style",
+ "title",
+ "accesskey",
+ "tabindex"
+ ]
+ },
+ "language":
+ {
+ "except":[
+ "base",
+ "br",
+ "hr",
+ "iframe",
+ "param",
+ "script"
+ ],
+ "attributes":
+ {
+ "dir":[
+ "ltr",
+ "rtl"
+ ],
+ "0":"lang",
+ "1":"xml:lang"
+ }
+ },
+ "keyboard":
+ {
+ "attributes":
+ {
+ "accesskey":/^(\w){1}$/,
+ "tabindex":/^(\d)+$/
+ }
+ }
+ },
+ "_events":
+ {
+ "window":
+ {
+ "only":[
+ "body"
+ ],
+ "attributes":[
+ "onload",
+ "onunload"
+ ]
+ },
+ "form":
+ {
+ "only":[
+ "form",
+ "input",
+ "textarea",
+ "select",
+ "a",
+ "label",
+ "button"
+ ],
+ "attributes":[
+ "onchange",
+ "onsubmit",
+ "onreset",
+ "onselect",
+ "onblur",
+ "onfocus"
+ ]
+ },
+ "keyboard":
+ {
+ "except":[
+ "base",
+ "bdo",
+ "br",
+ "frame",
+ "frameset",
+ "head",
+ "html",
+ "iframe",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "onkeydown",
+ "onkeypress",
+ "onkeyup"
+ ]
+ },
+ "mouse":
+ {
+ "except":[
+ "base",
+ "bdo",
+ "br",
+ "head",
+ "html",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "onclick",
+ "ondblclick",
+ "onmousedown",
+ "onmousemove",
+ "onmouseover",
+ "onmouseout",
+ "onmouseup"
+ ]
+ }
+ },
+ "_tags":
+ {
+ "a":
+ {
+ "attributes":
+ {
+ "0":"charset",
+ "1":"coords",
+ "2":"href",
+ "3":"hreflang",
+ "4":"name",
+ "rel":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,
+ "rev":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,
+ "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/,
+ "5":"type"
+ }
+ },
+ "0":"abbr",
+ "1":"acronym",
+ "2":"address",
+ "area":
+ {
+ "attributes":
+ {
+ "0":"alt",
+ "1":"coords",
+ "2":"href",
+ "nohref":/^(true|false)$/,
+ "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/
+ },
+ "required":[
+ "alt"
+ ]
+ },
+ "3":"b",
+ "base":
+ {
+ "attributes":[
+ "href"
+ ],
+ "required":[
+ "href"
+ ]
+ },
+ "bdo":
+ {
+ "attributes":
+ {
+ "dir":/^(ltr|rtl)$/
+ },
+ "required":[
+ "dir"
+ ]
+ },
+ "4":"big",
+ "blockquote":
+ {
+ "attributes":[
+ "cite"
+ ]
+ },
+ "5":"body",
+ "6":"br",
+ "button":
+ {
+ "attributes":
+ {
+ "disabled":/^(disabled)$/,
+ "type":/^(button|reset|submit)$/,
+ "0":"value"
+ },
+ "inside":"form"
+ },
+ "7":"caption",
+ "8":"cite",
+ "9":"code",
+ "col":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "span":/^(\d)+$/,
+ "valign":/^(top|middle|bottom|baseline)$/,
+ "2":"width"
+ },
+ "inside":"colgroup"
+ },
+ "colgroup":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "span":/^(\d)+$/,
+ "valign":/^(top|middle|bottom|baseline)$/,
+ "2":"width"
+ }
+ },
+ "10":"dd",
+ "del":
+ {
+ "attributes":
+ {
+ "0":"cite",
+ "datetime":/^([0-9]){8}/
+ }
+ },
+ "11":"div",
+ "12":"dfn",
+ "13":"dl",
+ "14":"dt",
+ "15":"em",
+ "fieldset":
+ {
+ "inside":"form"
+ },
+ "form":
+ {
+ "attributes":
+ {
+ "0":"action",
+ "1":"accept",
+ "2":"accept-charset",
+ "3":"enctype",
+ "method":/^(get|post)$/
+ },
+ "required":[
+ "action"
+ ]
+ },
+ "head":
+ {
+ "attributes":[
+ "profile"
+ ]
+ },
+ "16":"h1",
+ "17":"h2",
+ "18":"h3",
+ "19":"h4",
+ "20":"h5",
+ "21":"h6",
+ "22":"hr",
+ "html":
+ {
+ "attributes":[
+ "xmlns"
+ ]
+ },
+ "23":"i",
+ "img":
+ {
+ "attributes":[
+ "alt",
+ "src",
+ "height",
+ "ismap",
+ "longdesc",
+ "usemap",
+ "width"
+ ],
+ "required":[
+ "alt",
+ "src"
+ ]
+ },
+ "input":
+ {
+ "attributes":
+ {
+ "0":"accept",
+ "1":"alt",
+ "checked":/^(checked)$/,
+ "disabled":/^(disabled)$/,
+ "maxlength":/^(\d)+$/,
+ "2":"name",
+ "readonly":/^(readonly)$/,
+ "size":/^(\d)+$/,
+ "3":"src",
+ "type":/^(button|checkbox|file|hidden|image|password|radio|reset|submit|text)$/,
+ "4":"value"
+ },
+ "inside":"form"
+ },
+ "ins":
+ {
+ "attributes":
+ {
+ "0":"cite",
+ "datetime":/^([0-9]){8}/
+ }
+ },
+ "24":"kbd",
+ "label":
+ {
+ "attributes":[
+ "for"
+ ],
+ "inside":"form"
+ },
+ "25":"legend",
+ "26":"li",
+ "link":
+ {
+ "attributes":
+ {
+ "0":"charset",
+ "1":"href",
+ "2":"hreflang",
+ "media":/^(all|braille|print|projection|screen|speech|,|;| )+$/i,
+ //next comment line required by Opera!
+ /*"rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,*/
+ "rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
+ "rev":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
+ "3":"type"
+ },
+ "inside":"head"
+ },
+ "map":
+ {
+ "attributes":[
+ "id",
+ "name"
+ ],
+ "required":[
+ "id"
+ ]
+ },
+ "meta":
+ {
+ "attributes":
+ {
+ "0":"content",
+ "http-equiv":/^(content\-type|expires|refresh|set\-cookie)$/i,
+ "1":"name",
+ "2":"scheme"
+ },
+ "required":[
+ "content"
+ ]
+ },
+ "27":"noscript",
+ "object":
+ {
+ "attributes":[
+ "archive",
+ "classid",
+ "codebase",
+ "codetype",
+ "data",
+ "declare",
+ "height",
+ "name",
+ "standby",
+ "type",
+ "usemap",
+ "width"
+ ]
+ },
+ "28":"ol",
+ "optgroup":
+ {
+ "attributes":
+ {
+ "0":"label",
+ "disabled": /^(disabled)$/
+ },
+ "required":[
+ "label"
+ ]
+ },
+ "option":
+ {
+ "attributes":
+ {
+ "0":"label",
+ "disabled":/^(disabled)$/,
+ "selected":/^(selected)$/,
+ "1":"value"
+ },
+ "inside":"select"
+ },
+ "29":"p",
+ "param":
+ {
+ "attributes":
+ {
+ "0":"type",
+ "valuetype":/^(data|ref|object)$/,
+ "1":"valuetype",
+ "2":"value"
+ },
+ "required":[
+ "name"
+ ]
+ },
+ "30":"pre",
+ "q":
+ {
+ "attributes":[
+ "cite"
+ ]
+ },
+ "31":"samp",
+ "script":
+ {
+ "attributes":
+ {
+ "type":/^(text\/ecmascript|text\/javascript|text\/jscript|text\/vbscript|text\/vbs|text\/xml)$/,
+ "0":"charset",
+ "defer":/^(defer)$/,
+ "1":"src"
+ },
+ "required":[
+ "type"
+ ]
+ },
+ "select":
+ {
+ "attributes":
+ {
+ "disabled":/^(disabled)$/,
+ "multiple":/^(multiple)$/,
+ "0":"name",
+ "1":"size"
+ },
+ "inside":"form"
+ },
+ "32":"small",
+ "33":"span",
+ "34":"strong",
+ "style":
+ {
+ "attributes":
+ {
+ "0":"type",
+ "media":/^(screen|tty|tv|projection|handheld|print|braille|aural|all)$/
+ },
+ "required":[
+ "type"
+ ]
+ },
+ "35":"sub",
+ "36":"sup",
+ "table":
+ {
+ "attributes":
+ {
+ "0":"border",
+ "1":"cellpadding",
+ "2":"cellspacing",
+ "frame":/^(void|above|below|hsides|lhs|rhs|vsides|box|border)$/,
+ "rules":/^(none|groups|rows|cols|all)$/,
+ "3":"summary",
+ "4":"width"
+ }
+ },
+ "tbody":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "td":
+ {
+ "attributes":
+ {
+ "0":"abbr",
+ "align":/^(left|right|center|justify|char)$/,
+ "1":"axis",
+ "2":"char",
+ "3":"charoff",
+ "colspan":/^(\d)+$/,
+ "4":"headers",
+ "rowspan":/^(\d)+$/,
+ "scope":/^(col|colgroup|row|rowgroup)$/,
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "textarea":
+ {
+ "attributes":[
+ "cols",
+ "rows",
+ "disabled",
+ "name",
+ "readonly"
+ ],
+ "required":[
+ "cols",
+ "rows"
+ ],
+ "inside":"form"
+ },
+ "tfoot":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "valign":/^(top|middle|bottom)$/,
+ "2":"baseline"
+ }
+ },
+ "th":
+ {
+ "attributes":
+ {
+ "0":"abbr",
+ "align":/^(left|right|center|justify|char)$/,
+ "1":"axis",
+ "2":"char",
+ "3":"charoff",
+ "colspan":/^(\d)+$/,
+ "4":"headers",
+ "rowspan":/^(\d)+$/,
+ "scope":/^(col|colgroup|row|rowgroup)$/,
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "thead":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "37":"title",
+ "tr":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify|char)$/,
+ "0":"char",
+ "1":"charoff",
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "38":"tt",
+ "39":"ul",
+ "40":"var"
+ },
+
+ // Temporary skiped attributes
+ skiped_attributes : [],
+ skiped_attribute_values : [],
+
+ getValidTagAttributes: function(tag, attributes)
+ {
+ var valid_attributes = {};
+ var possible_attributes = this.getPossibleTagAttributes(tag);
+ for(var attribute in attributes) {
+ var value = attributes[attribute];
+ var h = WYMeditor.Helper;
+ if(!h.contains(this.skiped_attributes, attribute) && !h.contains(this.skiped_attribute_values, value)){
+ if (typeof value != 'function' && h.contains(possible_attributes, attribute)) {
+ if (this.doesAttributeNeedsValidation(tag, attribute)) {
+ if(this.validateAttribute(tag, attribute, value)){
+ valid_attributes[attribute] = value;
+ }
+ }else{
+ valid_attributes[attribute] = value;
+ }
+ }
+ }
+ }
+ return valid_attributes;
+ },
+ getUniqueAttributesAndEventsForTag : function(tag)
+ {
+ var result = [];
+
+ if (this._tags[tag] && this._tags[tag]['attributes']) {
+ for (k in this._tags[tag]['attributes']) {
+ result.push(parseInt(k) == k ? this._tags[tag]['attributes'][k] : k);
+ }
+ }
+ return result;
+ },
+ getDefaultAttributesAndEventsForTags : function()
+ {
+ var result = [];
+ for (var key in this._events){
+ result.push(this._events[key]);
+ }
+ for (var key in this._attributes){
+ result.push(this._attributes[key]);
+ }
+ return result;
+ },
+ isValidTag : function(tag)
+ {
+ if(this._tags[tag]){
+ return true;
+ }
+ for(var key in this._tags){
+ if(this._tags[key] == tag){
+ return true;
+ }
+ }
+ return false;
+ },
+ getDefaultAttributesAndEventsForTag : function(tag)
+ {
+ var default_attributes = [];
+ if (this.isValidTag(tag)) {
+ var default_attributes_and_events = this.getDefaultAttributesAndEventsForTags();
+
+ for(var key in default_attributes_and_events) {
+ var defaults = default_attributes_and_events[key];
+ if(typeof defaults == 'object'){
+ var h = WYMeditor.Helper;
+ if ((defaults['except'] && h.contains(defaults['except'], tag)) || (defaults['only'] && !h.contains(defaults['only'], tag))) {
+ continue;
+ }
+
+ var tag_defaults = defaults['attributes'] ? defaults['attributes'] : defaults['events'];
+ for(var k in tag_defaults) {
+ default_attributes.push(typeof tag_defaults[k] != 'string' ? k : tag_defaults[k]);
+ }
+ }
+ }
+ }
+ return default_attributes;
+ },
+ doesAttributeNeedsValidation: function(tag, attribute)
+ {
+ return this._tags[tag] && ((this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute]) || (this._tags[tag]['required'] &&
+ WYMeditor.Helper.contains(this._tags[tag]['required'], attribute)));
+ },
+ validateAttribute : function(tag, attribute, value)
+ {
+ if ( this._tags[tag] &&
+ (this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute] && value.length > 0 && !value.match(this._tags[tag]['attributes'][attribute])) || // invalid format
+ (this._tags[tag] && this._tags[tag]['required'] && WYMeditor.Helper.contains(this._tags[tag]['required'], attribute) && value.length == 0) // required attribute
+ ) {
+ return false;
+ }
+ return typeof this._tags[tag] != 'undefined';
+ },
+ getPossibleTagAttributes : function(tag)
+ {
+ if (!this._possible_tag_attributes) {
+ this._possible_tag_attributes = {};
+ }
+ if (!this._possible_tag_attributes[tag]) {
+ this._possible_tag_attributes[tag] = this.getUniqueAttributesAndEventsForTag(tag).concat(this.getDefaultAttributesAndEventsForTag(tag));
+ }
+ return this._possible_tag_attributes[tag];
+ }
+};
+
+
+/**
+* Compounded regular expression. Any of
+* the contained patterns could match and
+* when one does, it's label is returned.
+*
+* Constructor. Starts with no patterns.
+* @param boolean case True for case sensitive, false
+* for insensitive.
+* @access public
+* @author Marcus Baker (http://lastcraft.com)
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.ParallelRegex = function(case_sensitive)
+{
+ this._case = case_sensitive;
+ this._patterns = [];
+ this._labels = [];
+ this._regex = null;
+ return this;
+};
+
+
+/**
+* Adds a pattern with an optional label.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string label Label of regex to be returned
+* on a match.
+* @access public
+*/
+WYMeditor.ParallelRegex.prototype.addPattern = function(pattern, label)
+{
+ label = label || true;
+ var count = this._patterns.length;
+ this._patterns[count] = pattern;
+ this._labels[count] = label;
+ this._regex = null;
+};
+
+/**
+* Attempts to match all patterns at once against
+* a string.
+* @param string subject String to match against.
+*
+* @return boolean True on success.
+* @return string match First matched portion of
+* subject.
+* @access public
+*/
+WYMeditor.ParallelRegex.prototype.match = function(subject)
+{
+ if (this._patterns.length == 0) {
+ return [false, ''];
+ }
+ var matches = subject.match(this._getCompoundedRegex());
+
+ if(!matches){
+ return [false, ''];
+ }
+ var match = matches[0];
+ for (var i = 1; i < matches.length; i++) {
+ if (matches[i]) {
+ return [this._labels[i-1], match];
+ }
+ }
+ return [true, matches[0]];
+};
+
+/**
+* Compounds the patterns into a single
+* regular expression separated with the
+* "or" operator. Caches the regex.
+* Will automatically escape (, ) and / tokens.
+* @param array patterns List of patterns in order.
+* @access private
+*/
+WYMeditor.ParallelRegex.prototype._getCompoundedRegex = function()
+{
+ if (this._regex == null) {
+ for (var i = 0, count = this._patterns.length; i < count; i++) {
+ this._patterns[i] = '(' + this._untokenizeRegex(this._tokenizeRegex(this._patterns[i]).replace(/([\/\(\)])/g,'\\$1')) + ')';
+ }
+ this._regex = new RegExp(this._patterns.join("|") ,this._getPerlMatchingFlags());
+ }
+ return this._regex;
+};
+
+/**
+* Escape lookahead/lookbehind blocks
+*/
+WYMeditor.ParallelRegex.prototype._tokenizeRegex = function(regex)
+{
+ return regex.
+ replace(/\(\?(i|m|s|x|U)\)/, '~~~~~~Tk1\$1~~~~~~').
+ replace(/\(\?(\-[i|m|s|x|U])\)/, '~~~~~~Tk2\$1~~~~~~').
+ replace(/\(\?\=(.*)\)/, '~~~~~~Tk3\$1~~~~~~').
+ replace(/\(\?\!(.*)\)/, '~~~~~~Tk4\$1~~~~~~').
+ replace(/\(\?\<\=(.*)\)/, '~~~~~~Tk5\$1~~~~~~').
+ replace(/\(\?\<\!(.*)\)/, '~~~~~~Tk6\$1~~~~~~').
+ replace(/\(\?\:(.*)\)/, '~~~~~~Tk7\$1~~~~~~');
+};
+
+/**
+* Unscape lookahead/lookbehind blocks
+*/
+WYMeditor.ParallelRegex.prototype._untokenizeRegex = function(regex)
+{
+ return regex.
+ replace(/~~~~~~Tk1(.{1})~~~~~~/, "(?\$1)").
+ replace(/~~~~~~Tk2(.{2})~~~~~~/, "(?\$1)").
+ replace(/~~~~~~Tk3(.*)~~~~~~/, "(?=\$1)").
+ replace(/~~~~~~Tk4(.*)~~~~~~/, "(?!\$1)").
+ replace(/~~~~~~Tk5(.*)~~~~~~/, "(?<=\$1)").
+ replace(/~~~~~~Tk6(.*)~~~~~~/, "(?<!\$1)").
+ replace(/~~~~~~Tk7(.*)~~~~~~/, "(?:\$1)");
+};
+
+
+/**
+* Accessor for perl regex mode flags to use.
+* @return string Perl regex flags.
+* @access private
+*/
+WYMeditor.ParallelRegex.prototype._getPerlMatchingFlags = function()
+{
+ return (this._case ? "m" : "mi");
+};
+
+
+
+/**
+* States for a stack machine.
+*
+* Constructor. Starts in named state.
+* @param string start Starting state name.
+* @access public
+* @author Marcus Baker (http://lastcraft.com)
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.StateStack = function(start)
+{
+ this._stack = [start];
+ return this;
+};
+
+/**
+* Accessor for current state.
+* @return string State.
+* @access public
+*/
+WYMeditor.StateStack.prototype.getCurrent = function()
+{
+ return this._stack[this._stack.length - 1];
+};
+
+/**
+* Adds a state to the stack and sets it
+* to be the current state.
+* @param string state New state.
+* @access public
+*/
+WYMeditor.StateStack.prototype.enter = function(state)
+{
+ this._stack.push(state);
+};
+
+/**
+* Leaves the current state and reverts
+* to the previous one.
+* @return boolean False if we drop off
+* the bottom of the list.
+* @access public
+*/
+WYMeditor.StateStack.prototype.leave = function()
+{
+ if (this._stack.length == 1) {
+ return false;
+ }
+ this._stack.pop();
+ return true;
+};
+
+
+// GLOBALS
+WYMeditor.LEXER_ENTER = 1;
+WYMeditor.LEXER_MATCHED = 2;
+WYMeditor.LEXER_UNMATCHED = 3;
+WYMeditor.LEXER_EXIT = 4;
+WYMeditor.LEXER_SPECIAL = 5;
+
+
+/**
+* Accepts text and breaks it into tokens.
+* Some optimisation to make the sure the
+* content is only scanned by the PHP regex
+* parser once. Lexer modes must not start
+* with leading underscores.
+*
+* Sets up the lexer in case insensitive matching
+* by default.
+* @param Parser parser Handling strategy by reference.
+* @param string start Starting handler.
+* @param boolean case True for case sensitive.
+* @access public
+* @author Marcus Baker (http://lastcraft.com)
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.Lexer = function(parser, start, case_sensitive)
+{
+ start = start || 'accept';
+ this._case = case_sensitive || false;
+ this._regexes = {};
+ this._parser = parser;
+ this._mode = new WYMeditor.StateStack(start);
+ this._mode_handlers = {};
+ this._mode_handlers[start] = start;
+ return this;
+};
+
+/**
+* Adds a token search pattern for a particular
+* parsing mode. The pattern does not change the
+* current mode.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string mode Should only apply this
+* pattern when dealing with
+* this type of input.
+* @access public
+*/
+WYMeditor.Lexer.prototype.addPattern = function(pattern, mode)
+{
+ var mode = mode || "accept";
+ if (typeof this._regexes[mode] == 'undefined') {
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
+ }
+ this._regexes[mode].addPattern(pattern);
+ if (typeof this._mode_handlers[mode] == 'undefined') {
+ this._mode_handlers[mode] = mode;
+ }
+};
+
+/**
+* Adds a pattern that will enter a new parsing
+* mode. Useful for entering parenthesis, strings,
+* tags, etc.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string mode Should only apply this
+* pattern when dealing with
+* this type of input.
+* @param string new_mode Change parsing to this new
+* nested mode.
+* @access public
+*/
+WYMeditor.Lexer.prototype.addEntryPattern = function(pattern, mode, new_mode)
+{
+ if (typeof this._regexes[mode] == 'undefined') {
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
+ }
+ this._regexes[mode].addPattern(pattern, new_mode);
+ if (typeof this._mode_handlers[new_mode] == 'undefined') {
+ this._mode_handlers[new_mode] = new_mode;
+ }
+};
+
+/**
+* Adds a pattern that will exit the current mode
+* and re-enter the previous one.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string mode Mode to leave.
+* @access public
+*/
+WYMeditor.Lexer.prototype.addExitPattern = function(pattern, mode)
+{
+ if (typeof this._regexes[mode] == 'undefined') {
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
+ }
+ this._regexes[mode].addPattern(pattern, "__exit");
+ if (typeof this._mode_handlers[mode] == 'undefined') {
+ this._mode_handlers[mode] = mode;
+ }
+};
+
+/**
+* Adds a pattern that has a special mode. Acts as an entry
+* and exit pattern in one go, effectively calling a special
+* parser handler for this token only.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string mode Should only apply this
+* pattern when dealing with
+* this type of input.
+* @param string special Use this mode for this one token.
+* @access public
+*/
+WYMeditor.Lexer.prototype.addSpecialPattern = function(pattern, mode, special)
+{
+ if (typeof this._regexes[mode] == 'undefined') {
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
+ }
+ this._regexes[mode].addPattern(pattern, '_'+special);
+ if (typeof this._mode_handlers[special] == 'undefined') {
+ this._mode_handlers[special] = special;
+ }
+};
+
+/**
+* Adds a mapping from a mode to another handler.
+* @param string mode Mode to be remapped.
+* @param string handler New target handler.
+* @access public
+*/
+WYMeditor.Lexer.prototype.mapHandler = function(mode, handler)
+{
+ this._mode_handlers[mode] = handler;
+};
+
+/**
+* Splits the page text into tokens. Will fail
+* if the handlers report an error or if no
+* content is consumed. If successful then each
+* unparsed and parsed token invokes a call to the
+* held listener.
+* @param string raw Raw HTML text.
+* @return boolean True on success, else false.
+* @access public
+*/
+WYMeditor.Lexer.prototype.parse = function(raw)
+{
+ if (typeof this._parser == 'undefined') {
+ return false;
+ }
+
+ var length = raw.length;
+ var parsed;
+ while (typeof (parsed = this._reduce(raw)) == 'object') {
+ var raw = parsed[0];
+ var unmatched = parsed[1];
+ var matched = parsed[2];
+ var mode = parsed[3];
+
+ if (! this._dispatchTokens(unmatched, matched, mode)) {
+ return false;
+ }
+
+ if (raw == '') {
+ return true;
+ }
+ if (raw.length == length) {
+ return false;
+ }
+ length = raw.length;
+ }
+ if (! parsed ) {
+ return false;
+ }
+
+ return this._invokeParser(raw, WYMeditor.LEXER_UNMATCHED);
+};
+
+/**
+* Sends the matched token and any leading unmatched
+* text to the parser changing the lexer to a new
+* mode if one is listed.
+* @param string unmatched Unmatched leading portion.
+* @param string matched Actual token match.
+* @param string mode Mode after match. A boolean
+* false mode causes no change.
+* @return boolean False if there was any error
+* from the parser.
+* @access private
+*/
+WYMeditor.Lexer.prototype._dispatchTokens = function(unmatched, matched, mode)
+{
+ mode = mode || false;
+
+ if (! this._invokeParser(unmatched, WYMeditor.LEXER_UNMATCHED)) {
+ return false;
+ }
+
+ if (typeof mode == 'boolean') {
+ return this._invokeParser(matched, WYMeditor.LEXER_MATCHED);
+ }
+ if (this._isModeEnd(mode)) {
+ if (! this._invokeParser(matched, WYMeditor.LEXER_EXIT)) {
+ return false;
+ }
+ return this._mode.leave();
+ }
+ if (this._isSpecialMode(mode)) {
+ this._mode.enter(this._decodeSpecial(mode));
+ if (! this._invokeParser(matched, WYMeditor.LEXER_SPECIAL)) {
+ return false;
+ }
+ return this._mode.leave();
+ }
+ this._mode.enter(mode);
+
+ return this._invokeParser(matched, WYMeditor.LEXER_ENTER);
+};
+
+/**
+* Tests to see if the new mode is actually to leave
+* the current mode and pop an item from the matching
+* mode stack.
+* @param string mode Mode to test.
+* @return boolean True if this is the exit mode.
+* @access private
+*/
+WYMeditor.Lexer.prototype._isModeEnd = function(mode)
+{
+ return (mode === "__exit");
+};
+
+/**
+* Test to see if the mode is one where this mode
+* is entered for this token only and automatically
+* leaves immediately afterwoods.
+* @param string mode Mode to test.
+* @return boolean True if this is the exit mode.
+* @access private
+*/
+WYMeditor.Lexer.prototype._isSpecialMode = function(mode)
+{
+ return (mode.substring(0,1) == "_");
+};
+
+/**
+* Strips the magic underscore marking single token
+* modes.
+* @param string mode Mode to decode.
+* @return string Underlying mode name.
+* @access private
+*/
+WYMeditor.Lexer.prototype._decodeSpecial = function(mode)
+{
+ return mode.substring(1);
+};
+
+/**
+* Calls the parser method named after the current
+* mode. Empty content will be ignored. The lexer
+* has a parser handler for each mode in the lexer.
+* @param string content Text parsed.
+* @param boolean is_match Token is recognised rather
+* than unparsed data.
+* @access private
+*/
+WYMeditor.Lexer.prototype._invokeParser = function(content, is_match)
+{
+
+ if (content === '') {
+ return true;
+ }
+ var current = this._mode.getCurrent();
+ var handler = this._mode_handlers[current];
+ var result;
+ eval('result = this._parser.' + handler + '(content, is_match);');
+ return result;
+};
+
+/**
+* Tries to match a chunk of text and if successful
+* removes the recognised chunk and any leading
+* unparsed data. Empty strings will not be matched.
+* @param string raw The subject to parse. This is the
+* content that will be eaten.
+* @return array/boolean Three item list of unparsed
+* content followed by the
+* recognised token and finally the
+* action the parser is to take.
+* True if no match, false if there
+* is a parsing error.
+* @access private
+*/
+WYMeditor.Lexer.prototype._reduce = function(raw)
+{
+ var matched = this._regexes[this._mode.getCurrent()].match(raw);
+ var match = matched[1];
+ var action = matched[0];
+ if (action) {
+ var unparsed_character_count = raw.indexOf(match);
+ var unparsed = raw.substr(0, unparsed_character_count);
+ raw = raw.substring(unparsed_character_count + match.length);
+ return [raw, unparsed, match, action];
+ }
+ return true;
+};
+
+
+
+/**
+* This are the rules for breaking the XHTML code into events
+* handled by the provided parser.
+*
+* @author Marcus Baker (http://lastcraft.com)
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.XhtmlLexer = function(parser)
+{
+ jQuery.extend(this, new WYMeditor.Lexer(parser, 'Text'));
+
+ this.mapHandler('Text', 'Text');
+
+ this.addTokens();
+
+ this.init();
+
+ return this;
+};
+
+
+WYMeditor.XhtmlLexer.prototype.init = function()
+{
+};
+
+WYMeditor.XhtmlLexer.prototype.addTokens = function()
+{
+ this.addCommentTokens('Text');
+ this.addScriptTokens('Text');
+ this.addCssTokens('Text');
+ this.addTagTokens('Text');
+};
+
+WYMeditor.XhtmlLexer.prototype.addCommentTokens = function(scope)
+{
+ this.addEntryPattern("<!--", scope, 'Comment');
+ this.addExitPattern("-->", 'Comment');
+};
+
+WYMeditor.XhtmlLexer.prototype.addScriptTokens = function(scope)
+{
+ this.addEntryPattern("<script", scope, 'Script');
+ this.addExitPattern("</script>", 'Script');
+};
+
+WYMeditor.XhtmlLexer.prototype.addCssTokens = function(scope)
+{
+ this.addEntryPattern("<style", scope, 'Css');
+ this.addExitPattern("</style>", 'Css');
+};
+
+WYMeditor.XhtmlLexer.prototype.addTagTokens = function(scope)
+{
+ this.addSpecialPattern("<\\s*[a-z0-9:\-]+\\s*>", scope, 'OpeningTag');
+ this.addEntryPattern("<[a-z0-9:\-]+"+'[\\\/ \\\>]+', scope, 'OpeningTag');
+ this.addInTagDeclarationTokens('OpeningTag');
+
+ this.addSpecialPattern("</\\s*[a-z0-9:\-]+\\s*>", scope, 'ClosingTag');
+
+};
+
+WYMeditor.XhtmlLexer.prototype.addInTagDeclarationTokens = function(scope)
+{
+ this.addSpecialPattern('\\s+', scope, 'Ignore');
+
+ this.addAttributeTokens(scope);
+
+ this.addExitPattern('/>', scope);
+ this.addExitPattern('>', scope);
+
+};
+
+WYMeditor.XhtmlLexer.prototype.addAttributeTokens = function(scope)
+{
+ this.addSpecialPattern("\\s*[a-z-_0-9]*:?[a-z-_0-9]+\\s*(?=\=)\\s*", scope, 'TagAttributes');
+
+ this.addEntryPattern('=\\s*"', scope, 'DoubleQuotedAttribute');
+ this.addPattern("\\\\\"", 'DoubleQuotedAttribute');
+ this.addExitPattern('"', 'DoubleQuotedAttribute');
+
+ this.addEntryPattern("=\\s*'", scope, 'SingleQuotedAttribute');
+ this.addPattern("\\\\'", 'SingleQuotedAttribute');
+ this.addExitPattern("'", 'SingleQuotedAttribute');
+
+ this.addSpecialPattern('=\\s*[^>\\s]*', scope, 'UnquotedAttribute');
+};
+
+
+
+/**
+* XHTML Parser.
+*
+* This XHTML parser will trigger the events available on on
+* current SaxListener
+*
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.XhtmlParser = function(Listener, mode)
+{
+ var mode = mode || 'Text';
+ this._Lexer = new WYMeditor.XhtmlLexer(this);
+ this._Listener = Listener;
+ this._mode = mode;
+ this._matches = [];
+ this._last_match = '';
+ this._current_match = '';
+
+ return this;
+};
+
+WYMeditor.XhtmlParser.prototype.parse = function(raw)
+{
+ this._Lexer.parse(this.beforeParsing(raw));
+ return this.afterParsing(this._Listener.getResult());
+};
+
+WYMeditor.XhtmlParser.prototype.beforeParsing = function(raw)
+{
+ if(raw.match(/class="MsoNormal"/) || raw.match(/ns = "urn:schemas-microsoft-com/)){
+ // Usefull for cleaning up content pasted from other sources (MSWord)
+ this._Listener.avoidStylingTagsAndAttributes();
+ }
+ return this._Listener.beforeParsing(raw);
+};
+
+WYMeditor.XhtmlParser.prototype.afterParsing = function(parsed)
+{
+ if(this._Listener._avoiding_tags_implicitly){
+ this._Listener.allowStylingTagsAndAttributes();
+ }
+ return this._Listener.afterParsing(parsed);
+};
+
+
+WYMeditor.XhtmlParser.prototype.Ignore = function(match, state)
+{
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.Text = function(text)
+{
+ this._Listener.addContent(text);
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.Comment = function(match, status)
+{
+ return this._addNonTagBlock(match, status, 'addComment');
+};
+
+WYMeditor.XhtmlParser.prototype.Script = function(match, status)
+{
+ return this._addNonTagBlock(match, status, 'addScript');
+};
+
+WYMeditor.XhtmlParser.prototype.Css = function(match, status)
+{
+ return this._addNonTagBlock(match, status, 'addCss');
+};
+
+WYMeditor.XhtmlParser.prototype._addNonTagBlock = function(match, state, type)
+{
+ switch (state){
+ case WYMeditor.LEXER_ENTER:
+ this._non_tag = match;
+ break;
+ case WYMeditor.LEXER_UNMATCHED:
+ this._non_tag += match;
+ break;
+ case WYMeditor.LEXER_EXIT:
+ switch(type) {
+ case 'addComment':
+ this._Listener.addComment(this._non_tag+match);
+ break;
+ case 'addScript':
+ this._Listener.addScript(this._non_tag+match);
+ break;
+ case 'addCss':
+ this._Listener.addCss(this._non_tag+match);
+ break;
+ }
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.OpeningTag = function(match, state)
+{
+ switch (state){
+ case WYMeditor.LEXER_ENTER:
+ this._tag = this.normalizeTag(match);
+ this._tag_attributes = {};
+ break;
+ case WYMeditor.LEXER_SPECIAL:
+ this._callOpenTagListener(this.normalizeTag(match));
+ break;
+ case WYMeditor.LEXER_EXIT:
+ this._callOpenTagListener(this._tag, this._tag_attributes);
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.ClosingTag = function(match, state)
+{
+ this._callCloseTagListener(this.normalizeTag(match));
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype._callOpenTagListener = function(tag, attributes)
+{
+ var attributes = attributes || {};
+ this.autoCloseUnclosedBeforeNewOpening(tag);
+
+ if(this._Listener.isBlockTag(tag)){
+ this._Listener._tag_stack.push(tag);
+ this._Listener.fixNestingBeforeOpeningBlockTag(tag, attributes);
+ this._Listener.openBlockTag(tag, attributes);
+ this._increaseOpenTagCounter(tag);
+ }else if(this._Listener.isInlineTag(tag)){
+ this._Listener.inlineTag(tag, attributes);
+ }else{
+ this._Listener.openUnknownTag(tag, attributes);
+ this._increaseOpenTagCounter(tag);
+ }
+ this._Listener.last_tag = tag;
+ this._Listener.last_tag_opened = true;
+ this._Listener.last_tag_attributes = attributes;
+};
+
+WYMeditor.XhtmlParser.prototype._callCloseTagListener = function(tag)
+{
+ if(this._decreaseOpenTagCounter(tag)){
+ this.autoCloseUnclosedBeforeTagClosing(tag);
+
+ if(this._Listener.isBlockTag(tag)){
+ var expected_tag = this._Listener._tag_stack.pop();
+ if(expected_tag == false){
+ return;
+ }else if(expected_tag != tag){
+ tag = expected_tag;
+ }
+ this._Listener.closeBlockTag(tag);
+ }else{
+ this._Listener.closeUnknownTag(tag);
+ }
+ }else{
+ this._Listener.closeUnopenedTag(tag);
+ }
+ this._Listener.last_tag = tag;
+ this._Listener.last_tag_opened = false;
+};
+
+WYMeditor.XhtmlParser.prototype._increaseOpenTagCounter = function(tag)
+{
+ this._Listener._open_tags[tag] = this._Listener._open_tags[tag] || 0;
+ this._Listener._open_tags[tag]++;
+};
+
+WYMeditor.XhtmlParser.prototype._decreaseOpenTagCounter = function(tag)
+{
+ if(this._Listener._open_tags[tag]){
+ this._Listener._open_tags[tag]--;
+ if(this._Listener._open_tags[tag] == 0){
+ this._Listener._open_tags[tag] = undefined;
+ }
+ return true;
+ }
+ return false;
+};
+
+WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeNewOpening = function(new_tag)
+{
+ this._autoCloseUnclosed(new_tag, false);
+};
+
+WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeTagClosing = function(tag)
+{
+ this._autoCloseUnclosed(tag, true);
+};
+
+WYMeditor.XhtmlParser.prototype._autoCloseUnclosed = function(new_tag, closing)
+{
+ var closing = closing || false;
+ if(this._Listener._open_tags){
+ for (var tag in this._Listener._open_tags) {
+ var counter = this._Listener._open_tags[tag];
+ if(counter > 0 && this._Listener.shouldCloseTagAutomatically(tag, new_tag, closing)){
+ this._callCloseTagListener(tag, true);
+ }
+ }
+ }
+};
+
+WYMeditor.XhtmlParser.prototype.getTagReplacements = function()
+{
+ return this._Listener.getTagReplacements();
+};
+
+WYMeditor.XhtmlParser.prototype.normalizeTag = function(tag)
+{
+ tag = tag.replace(/^([\s<\/>]*)|([\s<\/>]*)$/gm,'').toLowerCase();
+ var tags = this._Listener.getTagReplacements();
+ if(tags[tag]){
+ return tags[tag];
+ }
+ return tag;
+};
+
+WYMeditor.XhtmlParser.prototype.TagAttributes = function(match, state)
+{
+ if(WYMeditor.LEXER_SPECIAL == state){
+ this._current_attribute = match;
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.DoubleQuotedAttribute = function(match, state)
+{
+ if(WYMeditor.LEXER_UNMATCHED == state){
+ this._tag_attributes[this._current_attribute] = match;
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.SingleQuotedAttribute = function(match, state)
+{
+ if(WYMeditor.LEXER_UNMATCHED == state){
+ this._tag_attributes[this._current_attribute] = match;
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.UnquotedAttribute = function(match, state)
+{
+ this._tag_attributes[this._current_attribute] = match.replace(/^=/,'');
+ return true;
+};
+
+
+
+/**
+* XHTML Sax parser.
+*
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.XhtmlSaxListener = function()
+{
+ this.output = '';
+ this.helper = new WYMeditor.XmlHelper();
+ this._open_tags = {};
+ this.validator = WYMeditor.XhtmlValidator;
+ this._tag_stack = [];
+ this.avoided_tags = [];
+
+ this.entities = {
+ '&nbsp;':'&#160;','&iexcl;':'&#161;','&cent;':'&#162;',
+ '&pound;':'&#163;','&curren;':'&#164;','&yen;':'&#165;',
+ '&brvbar;':'&#166;','&sect;':'&#167;','&uml;':'&#168;',
+ '&copy;':'&#169;','&ordf;':'&#170;','&laquo;':'&#171;',
+ '&not;':'&#172;','&shy;':'&#173;','&reg;':'&#174;',
+ '&macr;':'&#175;','&deg;':'&#176;','&plusmn;':'&#177;',
+ '&sup2;':'&#178;','&sup3;':'&#179;','&acute;':'&#180;',
+ '&micro;':'&#181;','&para;':'&#182;','&middot;':'&#183;',
+ '&cedil;':'&#184;','&sup1;':'&#185;','&ordm;':'&#186;',
+ '&raquo;':'&#187;','&frac14;':'&#188;','&frac12;':'&#189;',
+ '&frac34;':'&#190;','&iquest;':'&#191;','&Agrave;':'&#192;',
+ '&Aacute;':'&#193;','&Acirc;':'&#194;','&Atilde;':'&#195;',
+ '&Auml;':'&#196;','&Aring;':'&#197;','&AElig;':'&#198;',
+ '&Ccedil;':'&#199;','&Egrave;':'&#200;','&Eacute;':'&#201;',
+ '&Ecirc;':'&#202;','&Euml;':'&#203;','&Igrave;':'&#204;',
+ '&Iacute;':'&#205;','&Icirc;':'&#206;','&Iuml;':'&#207;',
+ '&ETH;':'&#208;','&Ntilde;':'&#209;','&Ograve;':'&#210;',
+ '&Oacute;':'&#211;','&Ocirc;':'&#212;','&Otilde;':'&#213;',
+ '&Ouml;':'&#214;','&times;':'&#215;','&Oslash;':'&#216;',
+ '&Ugrave;':'&#217;','&Uacute;':'&#218;','&Ucirc;':'&#219;',
+ '&Uuml;':'&#220;','&Yacute;':'&#221;','&THORN;':'&#222;',
+ '&szlig;':'&#223;','&agrave;':'&#224;','&aacute;':'&#225;',
+ '&acirc;':'&#226;','&atilde;':'&#227;','&auml;':'&#228;',
+ '&aring;':'&#229;','&aelig;':'&#230;','&ccedil;':'&#231;',
+ '&egrave;':'&#232;','&eacute;':'&#233;','&ecirc;':'&#234;',
+ '&euml;':'&#235;','&igrave;':'&#236;','&iacute;':'&#237;',
+ '&icirc;':'&#238;','&iuml;':'&#239;','&eth;':'&#240;',
+ '&ntilde;':'&#241;','&ograve;':'&#242;','&oacute;':'&#243;',
+ '&ocirc;':'&#244;','&otilde;':'&#245;','&ouml;':'&#246;',
+ '&divide;':'&#247;','&oslash;':'&#248;','&ugrave;':'&#249;',
+ '&uacute;':'&#250;','&ucirc;':'&#251;','&uuml;':'&#252;',
+ '&yacute;':'&#253;','&thorn;':'&#254;','&yuml;':'&#255;',
+ '&OElig;':'&#338;','&oelig;':'&#339;','&Scaron;':'&#352;',
+ '&scaron;':'&#353;','&Yuml;':'&#376;','&fnof;':'&#402;',
+ '&circ;':'&#710;','&tilde;':'&#732;','&Alpha;':'&#913;',
+ '&Beta;':'&#914;','&Gamma;':'&#915;','&Delta;':'&#916;',
+ '&Epsilon;':'&#917;','&Zeta;':'&#918;','&Eta;':'&#919;',
+ '&Theta;':'&#920;','&Iota;':'&#921;','&Kappa;':'&#922;',
+ '&Lambda;':'&#923;','&Mu;':'&#924;','&Nu;':'&#925;',
+ '&Xi;':'&#926;','&Omicron;':'&#927;','&Pi;':'&#928;',
+ '&Rho;':'&#929;','&Sigma;':'&#931;','&Tau;':'&#932;',
+ '&Upsilon;':'&#933;','&Phi;':'&#934;','&Chi;':'&#935;',
+ '&Psi;':'&#936;','&Omega;':'&#937;','&alpha;':'&#945;',
+ '&beta;':'&#946;','&gamma;':'&#947;','&delta;':'&#948;',
+ '&epsilon;':'&#949;','&zeta;':'&#950;','&eta;':'&#951;',
+ '&theta;':'&#952;','&iota;':'&#953;','&kappa;':'&#954;',
+ '&lambda;':'&#955;','&mu;':'&#956;','&nu;':'&#957;',
+ '&xi;':'&#958;','&omicron;':'&#959;','&pi;':'&#960;',
+ '&rho;':'&#961;','&sigmaf;':'&#962;','&sigma;':'&#963;',
+ '&tau;':'&#964;','&upsilon;':'&#965;','&phi;':'&#966;',
+ '&chi;':'&#967;','&psi;':'&#968;','&omega;':'&#969;',
+ '&thetasym;':'&#977;','&upsih;':'&#978;','&piv;':'&#982;',
+ '&ensp;':'&#8194;','&emsp;':'&#8195;','&thinsp;':'&#8201;',
+ '&zwnj;':'&#8204;','&zwj;':'&#8205;','&lrm;':'&#8206;',
+ '&rlm;':'&#8207;','&ndash;':'&#8211;','&mdash;':'&#8212;',
+ '&lsquo;':'&#8216;','&rsquo;':'&#8217;','&sbquo;':'&#8218;',
+ '&ldquo;':'&#8220;','&rdquo;':'&#8221;','&bdquo;':'&#8222;',
+ '&dagger;':'&#8224;','&Dagger;':'&#8225;','&bull;':'&#8226;',
+ '&hellip;':'&#8230;','&permil;':'&#8240;','&prime;':'&#8242;',
+ '&Prime;':'&#8243;','&lsaquo;':'&#8249;','&rsaquo;':'&#8250;',
+ '&oline;':'&#8254;','&frasl;':'&#8260;','&euro;':'&#8364;',
+ '&image;':'&#8465;','&weierp;':'&#8472;','&real;':'&#8476;',
+ '&trade;':'&#8482;','&alefsym;':'&#8501;','&larr;':'&#8592;',
+ '&uarr;':'&#8593;','&rarr;':'&#8594;','&darr;':'&#8595;',
+ '&harr;':'&#8596;','&crarr;':'&#8629;','&lArr;':'&#8656;',
+ '&uArr;':'&#8657;','&rArr;':'&#8658;','&dArr;':'&#8659;',
+ '&hArr;':'&#8660;','&forall;':'&#8704;','&part;':'&#8706;',
+ '&exist;':'&#8707;','&empty;':'&#8709;','&nabla;':'&#8711;',
+ '&isin;':'&#8712;','&notin;':'&#8713;','&ni;':'&#8715;',
+ '&prod;':'&#8719;','&sum;':'&#8721;','&minus;':'&#8722;',
+ '&lowast;':'&#8727;','&radic;':'&#8730;','&prop;':'&#8733;',
+ '&infin;':'&#8734;','&ang;':'&#8736;','&and;':'&#8743;',
+ '&or;':'&#8744;','&cap;':'&#8745;','&cup;':'&#8746;',
+ '&int;':'&#8747;','&there4;':'&#8756;','&sim;':'&#8764;',
+ '&cong;':'&#8773;','&asymp;':'&#8776;','&ne;':'&#8800;',
+ '&equiv;':'&#8801;','&le;':'&#8804;','&ge;':'&#8805;',
+ '&sub;':'&#8834;','&sup;':'&#8835;','&nsub;':'&#8836;',
+ '&sube;':'&#8838;','&supe;':'&#8839;','&oplus;':'&#8853;',
+ '&otimes;':'&#8855;','&perp;':'&#8869;','&sdot;':'&#8901;',
+ '&lceil;':'&#8968;','&rceil;':'&#8969;','&lfloor;':'&#8970;',
+ '&rfloor;':'&#8971;','&lang;':'&#9001;','&rang;':'&#9002;',
+ '&loz;':'&#9674;','&spades;':'&#9824;','&clubs;':'&#9827;',
+ '&hearts;':'&#9829;','&diams;':'&#9830;'};
+
+ this.block_tags = ["a", "abbr", "acronym", "address", "area", "b",
+ "base", "bdo", "big", "blockquote", "body", "button",
+ "caption", "cite", "code", "col", "colgroup", "dd", "del", "div",
+ "dfn", "dl", "dt", "em", "fieldset", "form", "head", "h1", "h2",
+ "h3", "h4", "h5", "h6", "html", "i", "ins",
+ "kbd", "label", "legend", "li", "map", "noscript",
+ "object", "ol", "optgroup", "option", "p", "param", "pre", "q",
+ "samp", "script", "select", "small", "span", "strong", "style",
+ "sub", "sup", "table", "tbody", "td", "textarea", "tfoot", "th",
+ "thead", "title", "tr", "tt", "ul", "var", "extends"];
+
+
+ this.inline_tags = ["br", "hr", "img", "input"];
+
+ return this;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.shouldCloseTagAutomatically = function(tag, now_on_tag, closing)
+{
+ var closing = closing || false;
+ if(tag == 'td'){
+ if((closing && now_on_tag == 'tr') || (!closing && now_on_tag == 'td')){
+ return true;
+ }
+ }
+ if(tag == 'option'){
+ if((closing && now_on_tag == 'select') || (!closing && now_on_tag == 'option')){
+ return true;
+ }
+ }
+ return false;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.beforeParsing = function(raw)
+{
+ this.output = '';
+ return raw;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.afterParsing = function(xhtml)
+{
+ xhtml = this.replaceNamedEntities(xhtml);
+ xhtml = this.joinRepeatedEntities(xhtml);
+ xhtml = this.removeEmptyTags(xhtml);
+ xhtml = this.removeBrInPre(xhtml);
+ return xhtml;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.replaceNamedEntities = function(xhtml)
+{
+ for (var entity in this.entities) {
+ xhtml = xhtml.replace(new RegExp(entity, 'g'), this.entities[entity]);
+ }
+ return xhtml;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.joinRepeatedEntities = function(xhtml)
+{
+ var tags = 'em|strong|sub|sup|acronym|pre|del|address';
+ return xhtml.replace(new RegExp('<\/('+tags+')><\\1>' ,''),'').
+ replace(new RegExp('(\s*<('+tags+')>\s*){2}(.*)(\s*<\/\\2>\s*){2}' ,''),'<\$2>\$3<\$2>');
+};
+
+WYMeditor.XhtmlSaxListener.prototype.removeEmptyTags = function(xhtml)
+{
+ return xhtml.replace(new RegExp('<('+this.block_tags.join("|").replace(/\|td/,'').replace(/\|th/, '')+')>(<br \/>|&#160;|&nbsp;|\\s)*<\/\\1>' ,'g'),'');
+};
+
+WYMeditor.XhtmlSaxListener.prototype.removeBrInPre = function(xhtml)
+{
+ var matches = xhtml.match(new RegExp('<pre[^>]*>(.*?)<\/pre>','gmi'));
+ if(matches) {
+ for(var i=0; i<matches.length; i++) {
+ xhtml = xhtml.replace(matches[i], matches[i].replace(new RegExp('<br \/>', 'g'), String.fromCharCode(13,10)));
+ }
+ }
+ return xhtml;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.getResult = function()
+{
+ return this.output;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.getTagReplacements = function()
+{
+ return {'b':'strong', 'i':'em'};
+};
+
+WYMeditor.XhtmlSaxListener.prototype.addContent = function(text)
+{
+ this.output += text;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.addComment = function(text)
+{
+ if(this.remove_comments){
+ this.output += text;
+ }
+};
+
+WYMeditor.XhtmlSaxListener.prototype.addScript = function(text)
+{
+ if(!this.remove_scripts){
+ this.output += text;
+ }
+};
+
+WYMeditor.XhtmlSaxListener.prototype.addCss = function(text)
+{
+ if(!this.remove_embeded_styles){
+ this.output += text;
+ }
+};
+
+WYMeditor.XhtmlSaxListener.prototype.openBlockTag = function(tag, attributes)
+{
+ this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes), true);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.inlineTag = function(tag, attributes)
+{
+ this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes));
+};
+
+WYMeditor.XhtmlSaxListener.prototype.openUnknownTag = function(tag, attributes)
+{
+ //this.output += this.helper.tag(tag, attributes, true);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.closeBlockTag = function(tag)
+{
+ this.output = this.output.replace(/<br \/>$/, '')+this._getClosingTagContent('before', tag)+"</"+tag+">"+this._getClosingTagContent('after', tag);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.closeUnknownTag = function(tag)
+{
+ //this.output += "</"+tag+">";
+};
+
+WYMeditor.XhtmlSaxListener.prototype.closeUnopenedTag = function(tag)
+{
+ this.output += "</"+tag+">";
+};
+
+WYMeditor.XhtmlSaxListener.prototype.avoidStylingTagsAndAttributes = function()
+{
+ this.avoided_tags = ['div','span'];
+ this.validator.skiped_attributes = ['style'];
+ this.validator.skiped_attribute_values = ['MsoNormal','main1']; // MS Word attributes for class
+ this._avoiding_tags_implicitly = true;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.allowStylingTagsAndAttributes = function()
+{
+ this.avoided_tags = [];
+ this.validator.skiped_attributes = [];
+ this.validator.skiped_attribute_values = [];
+ this._avoiding_tags_implicitly = false;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.isBlockTag = function(tag)
+{
+ return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.block_tags, tag);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.isInlineTag = function(tag)
+{
+ return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.inline_tags, tag);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.insertContentAfterClosingTag = function(tag, content)
+{
+ this._insertContentWhenClosingTag('after', tag, content);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.insertContentBeforeClosingTag = function(tag, content)
+{
+ this._insertContentWhenClosingTag('before', tag, content);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.fixNestingBeforeOpeningBlockTag = function(tag, attributes)
+{
+ if(tag != 'li' && (tag == 'ul' || tag == 'ol') && this.last_tag && !this.last_tag_opened && this.last_tag == 'li'){
+ this.output = this.output.replace(/<\/li>$/, '');
+ this.insertContentAfterClosingTag(tag, '</li>');
+ }
+};
+
+WYMeditor.XhtmlSaxListener.prototype._insertContentWhenClosingTag = function(position, tag, content)
+{
+ if(!this['_insert_'+position+'_closing']){
+ this['_insert_'+position+'_closing'] = [];
+ }
+ if(!this['_insert_'+position+'_closing'][tag]){
+ this['_insert_'+position+'_closing'][tag] = [];
+ }
+ this['_insert_'+position+'_closing'][tag].push(content);
+};
+
+WYMeditor.XhtmlSaxListener.prototype._getClosingTagContent = function(position, tag)
+{
+ if( this['_insert_'+position+'_closing'] &&
+ this['_insert_'+position+'_closing'][tag] &&
+ this['_insert_'+position+'_closing'][tag].length > 0){
+ return this['_insert_'+position+'_closing'][tag].pop();
+ }
+ return '';
+};
+
+
+/********** CSS PARSER **********/
+
+
+WYMeditor.WymCssLexer = function(parser, only_wym_blocks)
+{
+ var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? true : only_wym_blocks);
+
+ jQuery.extend(this, new WYMeditor.Lexer(parser, (only_wym_blocks?'Ignore':'WymCss')));
+
+ this.mapHandler('WymCss', 'Ignore');
+
+ if(only_wym_blocks == true){
+ this.addEntryPattern("/\\\x2a[<\\s]*WYMeditor[>\\s]*\\\x2a/", 'Ignore', 'WymCss');
+ this.addExitPattern("/\\\x2a[<\/\\s]*WYMeditor[>\\s]*\\\x2a/", 'WymCss');
+ }
+
+ this.addSpecialPattern("[\\sa-z1-6]*\\\x2e[a-z-_0-9]+", 'WymCss', 'WymCssStyleDeclaration');
+
+ this.addEntryPattern("/\\\x2a", 'WymCss', 'WymCssComment');
+ this.addExitPattern("\\\x2a/", 'WymCssComment');
+
+ this.addEntryPattern("\x7b", 'WymCss', 'WymCssStyle');
+ this.addExitPattern("\x7d", 'WymCssStyle');
+
+ this.addEntryPattern("/\\\x2a", 'WymCssStyle', 'WymCssFeedbackStyle');
+ this.addExitPattern("\\\x2a/", 'WymCssFeedbackStyle');
+
+ return this;
+};
+
+WYMeditor.WymCssParser = function()
+{
+ this._in_style = false;
+ this._has_title = false;
+ this.only_wym_blocks = true;
+ this.css_settings = {'classesItems':[], 'editorStyles':[], 'dialogStyles':[]};
+ return this;
+};
+
+WYMeditor.WymCssParser.prototype.parse = function(raw, only_wym_blocks)
+{
+ var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? this.only_wym_blocks : only_wym_blocks);
+ this._Lexer = new WYMeditor.WymCssLexer(this, only_wym_blocks);
+ this._Lexer.parse(raw);
+};
+
+WYMeditor.WymCssParser.prototype.Ignore = function(match, state)
+{
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.WymCssComment = function(text, status)
+{
+ if(text.match(/end[a-z0-9\s]*wym[a-z0-9\s]*/mi)){
+ return false;
+ }
+ if(status == WYMeditor.LEXER_UNMATCHED){
+ if(!this._in_style){
+ this._has_title = true;
+ this._current_item = {'title':WYMeditor.Helper.trim(text)};
+ }else{
+ if(this._current_item[this._current_element]){
+ if(!this._current_item[this._current_element].expressions){
+ this._current_item[this._current_element].expressions = [text];
+ }else{
+ this._current_item[this._current_element].expressions.push(text);
+ }
+ }
+ }
+ this._in_style = true;
+ }
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.WymCssStyle = function(match, status)
+{
+ if(status == WYMeditor.LEXER_UNMATCHED){
+ match = WYMeditor.Helper.trim(match);
+ if(match != ''){
+ this._current_item[this._current_element].style = match;
+ }
+ }else if (status == WYMeditor.LEXER_EXIT){
+ this._in_style = false;
+ this._has_title = false;
+ this.addStyleSetting(this._current_item);
+ }
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.WymCssFeedbackStyle = function(match, status)
+{
+ if(status == WYMeditor.LEXER_UNMATCHED){
+ this._current_item[this._current_element].feedback_style = match.replace(/^([\s\/\*]*)|([\s\/\*]*)$/gm,'');
+ }
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.WymCssStyleDeclaration = function(match)
+{
+ match = match.replace(/^([\s\.]*)|([\s\.*]*)$/gm, '');
+
+ var tag = '';
+ if(match.indexOf('.') > 0){
+ var parts = match.split('.');
+ this._current_element = parts[1];
+ var tag = parts[0];
+ }else{
+ this._current_element = match;
+ }
+
+ if(!this._has_title){
+ this._current_item = {'title':(!tag?'':tag.toUpperCase()+': ')+this._current_element};
+ this._has_title = true;
+ }
+
+ if(!this._current_item[this._current_element]){
+ this._current_item[this._current_element] = {'name':this._current_element};
+ }
+ if(tag){
+ if(!this._current_item[this._current_element].tags){
+ this._current_item[this._current_element].tags = [tag];
+ }else{
+ this._current_item[this._current_element].tags.push(tag);
+ }
+ }
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.addStyleSetting = function(style_details)
+{
+ for (var name in style_details){
+ var details = style_details[name];
+ if(typeof details == 'object' && name != 'title'){
+
+ this.css_settings.classesItems.push({
+ 'name': WYMeditor.Helper.trim(details.name),
+ 'title': style_details.title,
+ 'expr' : WYMeditor.Helper.trim((details.expressions||details.tags).join(', '))
+ });
+ if(details.feedback_style){
+ this.css_settings.editorStyles.push({
+ 'name': '.'+ WYMeditor.Helper.trim(details.name),
+ 'css': details.feedback_style
+ });
+ }
+ if(details.style){
+ this.css_settings.dialogStyles.push({
+ 'name': '.'+ WYMeditor.Helper.trim(details.name),
+ 'css': details.style
+ });
+ }
+ }
+ }
+};
+
+/********** HELPERS **********/
+
+// Returns true if it is a text node with whitespaces only
+jQuery.fn.isPhantomNode = function() {
+ if (this[0].nodeType == 3)
+ return !(/[^\t\n\r ]/.test(this[0].data));
+
+ return false;
+};
+
+WYMeditor.isPhantomNode = function(n) {
+ if (n.nodeType == 3)
+ return !(/[^\t\n\r ]/.test(n.data));
+
+ return false;
+};
+
+WYMeditor.isPhantomString = function(str) {
+ return !(/[^\t\n\r ]/.test(str));
+};
+
+// Returns the Parents or the node itself
+// jqexpr = a jQuery expression
+jQuery.fn.parentsOrSelf = function(jqexpr) {
+ var n = this;
+
+ if (n[0].nodeType == 3)
+ n = n.parents().slice(0,1);
+
+// if (n.is(jqexpr)) // XXX should work, but doesn't (probably a jQuery bug)
+ if (n.filter(jqexpr).size() == 1)
+ return n;
+ else
+ return n.parents(jqexpr).slice(0,1);
+};
+
+// String & array helpers
+
+WYMeditor.Helper = {
+
+ //replace all instances of 'old' by 'rep' in 'str' string
+ replaceAll: function(str, old, rep) {
+ var rExp = new RegExp(old, "g");
+ return(str.replace(rExp, rep));
+ },
+
+ //insert 'inserted' at position 'pos' in 'str' string
+ insertAt: function(str, inserted, pos) {
+ return(str.substr(0,pos) + inserted + str.substring(pos));
+ },
+
+ //trim 'str' string
+ trim: function(str) {
+ return str.replace(/^(\s*)|(\s*)$/gm,'');
+ },
+
+ //return true if 'arr' array contains 'elem', or false
+ contains: function(arr, elem) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] === elem) return true;
+ }
+ return false;
+ },
+
+ //return 'item' position in 'arr' array, or -1
+ indexOf: function(arr, item) {
+ var ret=-1;
+ for(var i = 0; i < arr.length; i++) {
+ if (arr[i] == item) {
+ ret = i;
+ break;
+ }
+ }
+ return(ret);
+ },
+
+ //return 'item' object in 'arr' array, checking its 'name' property, or null
+ findByName: function(arr, name) {
+ for(var i = 0; i < arr.length; i++) {
+ var item = arr[i];
+ if(item.name == name) return(item);
+ }
+ return(null);
+ }
+};
+
+
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.explorer.js
+ * MSIE specific class and functions.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Bermi Ferrer (wymeditor a-t bermi dotorg)
+ * Frédéric Palluel-Lafleur (fpalluel a-t gmail dotcom)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+WYMeditor.WymClassExplorer = function(wym) {
+
+ this._wym = wym;
+ this._class = "className";
+ this._newLine = "\r\n";
+
+};
+
+WYMeditor.WymClassExplorer.prototype.initIframe = function(iframe) {
+
+ //This function is executed twice, though it is called once!
+ //But MSIE needs that, otherwise designMode won't work.
+ //Weird.
+
+ this._iframe = iframe;
+ this._doc = iframe.contentWindow.document;
+
+ //add css rules from options
+ var styles = this._doc.styleSheets[0];
+ var aCss = eval(this._options.editorStyles);
+
+ this.addCssRules(this._doc, aCss);
+
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
+
+ //init html value
+ jQuery(this._doc.body).html(this._wym._html);
+
+ //handle events
+ var wym = this;
+
+ this._doc.body.onfocus = function()
+ {wym._doc.designMode = "on"; wym._doc = iframe.contentWindow.document;};
+ this._doc.onbeforedeactivate = function() {wym.saveCaret();};
+ this._doc.onkeyup = function() {
+ wym.saveCaret();
+ wym.keyup();
+ };
+ this._doc.onclick = function() {wym.saveCaret();};
+
+ this._doc.body.onbeforepaste = function() {
+ wym._iframe.contentWindow.event.returnValue = false;
+ };
+
+ this._doc.body.onpaste = function() {
+ wym._iframe.contentWindow.event.returnValue = false;
+ wym.paste(window.clipboardData.getData("Text"));
+ };
+
+ //callback can't be executed twice, so we check
+ if(this._initialized) {
+
+ //pre-bind functions
+ if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
+
+ //bind external events
+ this._wym.bindEvents();
+
+ //post-init functions
+ if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
+
+ //add event listeners to doc elements, e.g. images
+ this.listen();
+ }
+
+ this._initialized = true;
+
+ //init designMode
+ this._doc.designMode="on";
+ try{
+ // (bermi's note) noticed when running unit tests on IE6
+ // Is this really needed, it trigger an unexisting property on IE6
+ this._doc = iframe.contentWindow.document;
+ }catch(e){}
+};
+
+WYMeditor.WymClassExplorer.prototype._exec = function(cmd,param) {
+
+ switch(cmd) {
+
+ case WYMeditor.INDENT: case WYMeditor.OUTDENT:
+
+ var container = this.findUp(this.container(), WYMeditor.LI);
+ if(container) {
+ var ancestor = container.parentNode.parentNode;
+ if(container.parentNode.childNodes.length>1
+ || ancestor.tagName.toLowerCase() == WYMeditor.OL
+ || ancestor.tagName.toLowerCase() == WYMeditor.UL)
+ this._doc.execCommand(cmd);
+ }
+ break;
+ default:
+ if(param) this._doc.execCommand(cmd,false,param);
+ else this._doc.execCommand(cmd);
+ break;
+ }
+
+};
+
+WYMeditor.WymClassExplorer.prototype.selected = function() {
+
+ var caretPos = this._iframe.contentWindow.document.caretPos;
+ if(caretPos!=null) {
+ if(caretPos.parentElement!=undefined)
+ return(caretPos.parentElement());
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.saveCaret = function() {
+
+ this._doc.caretPos = this._doc.selection.createRange();
+};
+
+WYMeditor.WymClassExplorer.prototype.addCssRule = function(styles, oCss) {
+
+ styles.addRule(oCss.name, oCss.css);
+};
+
+WYMeditor.WymClassExplorer.prototype.insert = function(html) {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Overwrite selection with provided html
+ range.pasteHTML(html);
+ } catch (e) { }
+ } else {
+ // Fall back to the internal paste function if there's no selection
+ this.paste(html);
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.wrap = function(left, right) {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Overwrite selection with provided html
+ range.pasteHTML(left + range.text + right);
+ } catch (e) { }
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.unwrap = function() {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Unwrap selection
+ var text = range.text;
+ this._exec( 'Cut' );
+ range.pasteHTML( text );
+ } catch (e) { }
+ }
+};
+
+//keyup handler
+WYMeditor.WymClassExplorer.prototype.keyup = function() {
+ this._selected_image = null;
+};
+
+WYMeditor.WymClassExplorer.prototype.setFocusToNode = function(node, toStart) {
+ var range = this._doc.selection.createRange();
+ toStart = toStart ? true : false;
+
+ range.moveToElementText(node);
+ range.collapse(toStart);
+ range.select();
+ node.focus();
+};
+
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.mozilla.js
+ * Gecko specific class and functions.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Volker Mische (vmx a-t gmx dotde)
+ * Bermi Ferrer (wymeditor a-t bermi dotorg)
+ * Frédéric Palluel-Lafleur (fpalluel a-t gmail dotcom)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+WYMeditor.WymClassMozilla = function(wym) {
+
+ this._wym = wym;
+ this._class = "class";
+ this._newLine = "\n";
+};
+
+WYMeditor.WymClassMozilla.prototype.initIframe = function(iframe) {
+ var wym = this;
+
+ this._iframe = iframe;
+ this._doc = iframe.contentDocument;
+
+ //add css rules from options
+
+ var styles = this._doc.styleSheets[0];
+ var aCss = eval(this._options.editorStyles);
+
+ this.addCssRules(this._doc, aCss);
+
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
+
+ //init html value
+ this.html(this._wym._html);
+
+ //init designMode
+ this.enableDesignMode();
+
+ //pre-bind functions
+ if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
+
+ //bind external events
+ this._wym.bindEvents();
+
+ //bind editor keydown events
+ jQuery(this._doc).bind("keydown", this.keydown);
+
+ //bind editor keyup events
+ jQuery(this._doc).bind("keyup", this.keyup);
+
+ //bind editor focus events (used to reset designmode - Gecko bug)
+ jQuery(this._doc).bind("focus", function () {
+ // Fix scope
+ wym.enableDesignMode.call(wym);
+ });
+
+ //post-init functions
+ if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
+
+ //add event listeners to doc elements, e.g. images
+ this.listen();
+};
+
+/* @name html
+ * @description Get/Set the html value
+ */
+WYMeditor.WymClassMozilla.prototype.html = function(html) {
+
+ if(typeof html === 'string') {
+
+ //disable designMode
+ try { this._doc.designMode = "off"; } catch(e) { };
+
+ //replace em by i and strong by bold
+ //(designMode issue)
+ html = html.replace(/<em(\b[^>]*)>/gi, "<i$1>")
+ .replace(/<\/em>/gi, "</i>")
+ .replace(/<strong(\b[^>]*)>/gi, "<b$1>")
+ .replace(/<\/strong>/gi, "</b>");
+
+ //update the html body
+ jQuery(this._doc.body).html(html);
+
+ //re-init designMode
+ this.enableDesignMode();
+ }
+ else return(jQuery(this._doc.body).html());
+};
+
+WYMeditor.WymClassMozilla.prototype._exec = function(cmd,param) {
+
+ if(!this.selected()) return(false);
+
+ switch(cmd) {
+
+ case WYMeditor.INDENT: case WYMeditor.OUTDENT:
+
+ var focusNode = this.selected();
+ var sel = this._iframe.contentWindow.getSelection();
+ var anchorNode = sel.anchorNode;
+ if(anchorNode.nodeName == "#text") anchorNode = anchorNode.parentNode;
+
+ focusNode = this.findUp(focusNode, WYMeditor.BLOCKS);
+ anchorNode = this.findUp(anchorNode, WYMeditor.BLOCKS);
+
+ if(focusNode && focusNode == anchorNode
+ && focusNode.tagName.toLowerCase() == WYMeditor.LI) {
+
+ var ancestor = focusNode.parentNode.parentNode;
+
+ if(focusNode.parentNode.childNodes.length>1
+ || ancestor.tagName.toLowerCase() == WYMeditor.OL
+ || ancestor.tagName.toLowerCase() == WYMeditor.UL)
+ this._doc.execCommand(cmd,'',null);
+ }
+
+ break;
+
+ default:
+
+ if(param) this._doc.execCommand(cmd,'',param);
+ else this._doc.execCommand(cmd,'',null);
+ }
+
+ //set to P if parent = BODY
+ var container = this.selected();
+ if(container.tagName.toLowerCase() == WYMeditor.BODY)
+ this._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
+};
+
+/* @name selected
+ * @description Returns the selected container
+ */
+WYMeditor.WymClassMozilla.prototype.selected = function() {
+
+ var sel = this._iframe.contentWindow.getSelection();
+ var node = sel.focusNode;
+ if(node) {
+ if(node.nodeName == "#text") return(node.parentNode);
+ else return(node);
+ } else return(null);
+};
+
+WYMeditor.WymClassMozilla.prototype.addCssRule = function(styles, oCss) {
+
+ styles.insertRule(oCss.name + " {" + oCss.css + "}",
+ styles.cssRules.length);
+};
+
+
+//keydown handler, mainly used for keyboard shortcuts
+WYMeditor.WymClassMozilla.prototype.keydown = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+ var container = null;
+
+ if(evt.ctrlKey){
+ if(evt.keyCode == 66){
+ //CTRL+b => STRONG
+ wym._exec(WYMeditor.BOLD);
+ return false;
+ }
+ if(evt.keyCode == 73){
+ //CTRL+i => EMPHASIS
+ wym._exec(WYMeditor.ITALIC);
+ return false;
+ }
+ }
+
+ else if(evt.keyCode == 13) {
+ if(!evt.shiftKey){
+ //fix PRE bug #73
+ container = wym.selected();
+ if(container && container.tagName.toLowerCase() == WYMeditor.PRE) {
+ evt.preventDefault();
+ wym.insert('<p></p>');
+ }
+ }
+ }
+};
+
+//keyup handler, mainly used for cleanups
+WYMeditor.WymClassMozilla.prototype.keyup = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+
+ wym._selected_image = null;
+ var container = null;
+
+ if(evt.keyCode == 13 && !evt.shiftKey) {
+
+ //RETURN key
+ //cleanup <br><br> between paragraphs
+ jQuery(wym._doc.body).children(WYMeditor.BR).remove();
+ }
+
+ if(evt.keyCode != 8
+ && evt.keyCode != 17
+ && evt.keyCode != 46
+ && evt.keyCode != 224
+ && !evt.metaKey
+ && !evt.ctrlKey) {
+
+ //NOT BACKSPACE, NOT DELETE, NOT CTRL, NOT COMMAND
+ //text nodes replaced by P
+
+ container = wym.selected();
+ var name = container.tagName.toLowerCase();
+
+ //fix forbidden main containers
+ if(
+ name == "strong" ||
+ name == "b" ||
+ name == "em" ||
+ name == "i" ||
+ name == "sub" ||
+ name == "sup" ||
+ name == "a"
+
+ ) name = container.parentNode.tagName.toLowerCase();
+
+ if(name == WYMeditor.BODY) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
+ }
+};
+
+WYMeditor.WymClassMozilla.prototype.enableDesignMode = function() {
+ if(this._doc.designMode == "off") {
+ try {
+ this._doc.designMode = "on";
+ this._doc.execCommand("styleWithCSS", '', false);
+ } catch(e) { }
+ }
+};
+
+WYMeditor.WymClassMozilla.prototype.openBlockTag = function(tag, attributes)
+{
+ var attributes = this.validator.getValidTagAttributes(tag, attributes);
+
+ // Handle Mozilla styled spans
+ if(tag == 'span' && attributes.style){
+ var new_tag = this.getTagForStyle(attributes.style);
+ if(new_tag){
+ this._tag_stack.pop();
+ var tag = new_tag;
+ this._tag_stack.push(new_tag);
+ attributes.style = '';
+ }else{
+ return;
+ }
+ }
+
+ this.output += this.helper.tag(tag, attributes, true);
+};
+
+WYMeditor.WymClassMozilla.prototype.getTagForStyle = function(style) {
+
+ if(/bold/.test(style)) return 'strong';
+ if(/italic/.test(style)) return 'em';
+ if(/sub/.test(style)) return 'sub';
+ if(/super/.test(style)) return 'sup';
+ return false;
+};
+
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.opera.js
+ * Opera specific class and functions.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ */
+
+WYMeditor.WymClassOpera = function(wym) {
+
+ this._wym = wym;
+ this._class = "class";
+ this._newLine = "\r\n";
+};
+
+WYMeditor.WymClassOpera.prototype.initIframe = function(iframe) {
+
+ this._iframe = iframe;
+ this._doc = iframe.contentWindow.document;
+
+ //add css rules from options
+ var styles = this._doc.styleSheets[0];
+ var aCss = eval(this._options.editorStyles);
+
+ this.addCssRules(this._doc, aCss);
+
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
+
+ //init designMode
+ this._doc.designMode = "on";
+
+ //init html value
+ this.html(this._wym._html);
+
+ //pre-bind functions
+ if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
+
+ //bind external events
+ this._wym.bindEvents();
+
+ //bind editor keydown events
+ jQuery(this._doc).bind("keydown", this.keydown);
+
+ //bind editor events
+ jQuery(this._doc).bind("keyup", this.keyup);
+
+ //post-init functions
+ if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
+
+ //add event listeners to doc elements, e.g. images
+ this.listen();
+};
+
+WYMeditor.WymClassOpera.prototype._exec = function(cmd,param) {
+
+ if(param) this._doc.execCommand(cmd,false,param);
+ else this._doc.execCommand(cmd);
+
+};
+
+WYMeditor.WymClassOpera.prototype.selected = function() {
+
+ var sel=this._iframe.contentWindow.getSelection();
+ var node=sel.focusNode;
+ if(node) {
+ if(node.nodeName=="#text")return(node.parentNode);
+ else return(node);
+ } else return(null);
+};
+
+WYMeditor.WymClassOpera.prototype.addCssRule = function(styles, oCss) {
+
+ styles.insertRule(oCss.name + " {" + oCss.css + "}",
+ styles.cssRules.length);
+};
+
+//keydown handler
+WYMeditor.WymClassOpera.prototype.keydown = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+ var sel = wym._iframe.contentWindow.getSelection();
+ startNode = sel.getRangeAt(0).startContainer;
+
+ //Get a P instead of no container
+ if(!jQuery(startNode).parentsOrSelf(
+ WYMeditor.MAIN_CONTAINERS.join(","))[0]
+ && !jQuery(startNode).parentsOrSelf('li')
+ && evt.keyCode != WYMeditor.KEY.ENTER
+ && evt.keyCode != WYMeditor.KEY.LEFT
+ && evt.keyCode != WYMeditor.KEY.UP
+ && evt.keyCode != WYMeditor.KEY.RIGHT
+ && evt.keyCode != WYMeditor.KEY.DOWN
+ && evt.keyCode != WYMeditor.KEY.BACKSPACE
+ && evt.keyCode != WYMeditor.KEY.DELETE)
+ wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
+
+};
+
+//keyup handler
+WYMeditor.WymClassOpera.prototype.keyup = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+ wym._selected_image = null;
+};
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.safari.js
+ * Safari specific class and functions.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Scott Lewis (lewiscot a-t gmail dotcom)
+ */
+
+WYMeditor.WymClassSafari = function(wym) {
+
+ this._wym = wym;
+ this._class = "class";
+ this._newLine = "\n";
+};
+
+WYMeditor.WymClassSafari.prototype.initIframe = function(iframe) {
+
+ this._iframe = iframe;
+ this._doc = iframe.contentDocument;
+
+ //add css rules from options
+
+ var styles = this._doc.styleSheets[0];
+ var aCss = eval(this._options.editorStyles);
+
+ this.addCssRules(this._doc, aCss);
+
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
+
+ //init designMode
+ this._doc.designMode = "on";
+
+ //init html value
+ this.html(this._wym._html);
+
+ //pre-bind functions
+ if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
+
+ //bind external events
+ this._wym.bindEvents();
+
+ //bind editor keydown events
+ jQuery(this._doc).bind("keydown", this.keydown);
+
+ //bind editor keyup events
+ jQuery(this._doc).bind("keyup", this.keyup);
+
+ //post-init functions
+ if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
+
+ //add event listeners to doc elements, e.g. images
+ this.listen();
+};
+
+WYMeditor.WymClassSafari.prototype._exec = function(cmd,param) {
+
+ if(!this.selected()) return(false);
+
+ switch(cmd) {
+
+ case WYMeditor.INDENT: case WYMeditor.OUTDENT:
+
+ var focusNode = this.selected();
+ var sel = this._iframe.contentWindow.getSelection();
+ var anchorNode = sel.anchorNode;
+ if(anchorNode.nodeName == "#text") anchorNode = anchorNode.parentNode;
+
+ focusNode = this.findUp(focusNode, WYMeditor.BLOCKS);
+ anchorNode = this.findUp(anchorNode, WYMeditor.BLOCKS);
+
+ if(focusNode && focusNode == anchorNode
+ && focusNode.tagName.toLowerCase() == WYMeditor.LI) {
+
+ var ancestor = focusNode.parentNode.parentNode;
+
+ if(focusNode.parentNode.childNodes.length>1
+ || ancestor.tagName.toLowerCase() == WYMeditor.OL
+ || ancestor.tagName.toLowerCase() == WYMeditor.UL)
+ this._doc.execCommand(cmd,'',null);
+ }
+
+ break;
+
+ case WYMeditor.INSERT_ORDEREDLIST: case WYMeditor.INSERT_UNORDEREDLIST:
+
+ this._doc.execCommand(cmd,'',null);
+
+ //Safari creates lists in e.g. paragraphs.
+ //Find the container, and remove it.
+ var focusNode = this.selected();
+ var container = this.findUp(focusNode, WYMeditor.MAIN_CONTAINERS);
+ if(container) jQuery(container).replaceWith(jQuery(container).html());
+
+ break;
+
+ default:
+
+ if(param) this._doc.execCommand(cmd,'',param);
+ else this._doc.execCommand(cmd,'',null);
+ }
+
+ //set to P if parent = BODY
+ var container = this.selected();
+ if(container && container.tagName.toLowerCase() == WYMeditor.BODY)
+ this._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
+
+};
+
+/* @name selected
+ * @description Returns the selected container
+ */
+WYMeditor.WymClassSafari.prototype.selected = function() {
+
+ var sel = this._iframe.contentWindow.getSelection();
+ var node = sel.focusNode;
+ if(node) {
+ if(node.nodeName == "#text") return(node.parentNode);
+ else return(node);
+ } else return(null);
+};
+
+WYMeditor.WymClassSafari.prototype.addCssRule = function(styles, oCss) {
+
+ styles.insertRule(oCss.name + " {" + oCss.css + "}",
+ styles.cssRules.length);
+};
+
+
+//keydown handler, mainly used for keyboard shortcuts
+WYMeditor.WymClassSafari.prototype.keydown = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+
+ if(evt.ctrlKey){
+ if(evt.keyCode == 66){
+ //CTRL+b => STRONG
+ wym._exec(WYMeditor.BOLD);
+ return false;
+ }
+ if(evt.keyCode == 73){
+ //CTRL+i => EMPHASIS
+ wym._exec(WYMeditor.ITALIC);
+ return false;
+ }
+ }
+};
+
+//keyup handler, mainly used for cleanups
+WYMeditor.WymClassSafari.prototype.keyup = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+
+ wym._selected_image = null;
+ var container = null;
+
+ if(evt.keyCode == 13 && !evt.shiftKey) {
+
+ //RETURN key
+ //cleanup <br><br> between paragraphs
+ jQuery(wym._doc.body).children(WYMeditor.BR).remove();
+
+ //fix PRE bug #73
+ container = wym.selected();
+ if(container && container.tagName.toLowerCase() == WYMeditor.PRE)
+ wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //create P after PRE
+ }
+
+ //fix #112
+ if(evt.keyCode == 13 && evt.shiftKey) {
+ wym._exec('InsertLineBreak');
+ }
+
+ if(evt.keyCode != 8
+ && evt.keyCode != 17
+ && evt.keyCode != 46
+ && evt.keyCode != 224
+ && !evt.metaKey
+ && !evt.ctrlKey) {
+
+ //NOT BACKSPACE, NOT DELETE, NOT CTRL, NOT COMMAND
+ //text nodes replaced by P
+
+ container = wym.selected();
+ var name = container.tagName.toLowerCase();
+
+ //fix forbidden main containers
+ if(
+ name == "strong" ||
+ name == "b" ||
+ name == "em" ||
+ name == "i" ||
+ name == "sub" ||
+ name == "sup" ||
+ name == "a" ||
+ name == "span" //fix #110
+
+ ) name = container.parentNode.tagName.toLowerCase();
+
+ if(name == WYMeditor.BODY || name == WYMeditor.DIV) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //fix #110 for DIV
+ }
+};
+
+WYMeditor.WymClassSafari.prototype.openBlockTag = function(tag, attributes)
+{
+ var attributes = this.validator.getValidTagAttributes(tag, attributes);
+
+ // Handle Safari styled spans
+ if(tag == 'span' && attributes.style) {
+ var new_tag = this.getTagForStyle(attributes.style);
+ if(new_tag){
+ this._tag_stack.pop();
+ var tag = new_tag;
+ this._tag_stack.push(new_tag);
+ attributes.style = '';
+
+ //should fix #125 - also removed the xhtml() override
+ if(typeof attributes['class'] == 'string')
+ attributes['class'] = attributes['class'].replace(/apple-style-span/gi, '');
+
+ } else {
+ return;
+ }
+ }
+
+ this.output += this.helper.tag(tag, attributes, true);
+};
+
+WYMeditor.WymClassSafari.prototype.getTagForStyle = function(style) {
+
+ if(/bold/.test(style)) return 'strong';
+ if(/italic/.test(style)) return 'em';
+ if(/sub/.test(style)) return 'sub';
+ if(/super/.test(style)) return 'sup';
+ return false;
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.min.js b/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.min.js
new file mode 100644
index 0000000..a7f9018
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.min.js
@@ -0,0 +1 @@
+if(!WYMeditor){var WYMeditor={}}(function(){if(!window.console||!console.firebug){var b=["log","debug","info","warn","error","assert","dir","dirxml","group","groupEnd","time","timeEnd","count","trace","profile","profileEnd"];WYMeditor.console={};for(var a=0;a<b.length;++a){WYMeditor.console[b[a]]=function(){}}}else{WYMeditor.console=window.console}})();jQuery.extend(WYMeditor,{VERSION:"0.5-rc1",INSTANCES:[],STRINGS:[],SKINS:[],NAME:"name",INDEX:"{Wym_Index}",WYM_INDEX:"wym_index",BASE_PATH:"{Wym_Base_Path}",CSS_PATH:"{Wym_Css_Path}",WYM_PATH:"{Wym_Wym_Path}",SKINS_DEFAULT_PATH:"skins/",SKINS_DEFAULT_CSS:"skin.css",SKINS_DEFAULT_JS:"skin.js",LANG_DEFAULT_PATH:"lang/",IFRAME_BASE_PATH:"{Wym_Iframe_Base_Path}",IFRAME_DEFAULT:"iframe/default/",JQUERY_PATH:"{Wym_Jquery_Path}",DIRECTION:"{Wym_Direction}",LOGO:"{Wym_Logo}",TOOLS:"{Wym_Tools}",TOOLS_ITEMS:"{Wym_Tools_Items}",TOOL_NAME:"{Wym_Tool_Name}",TOOL_TITLE:"{Wym_Tool_Title}",TOOL_CLASS:"{Wym_Tool_Class}",CLASSES:"{Wym_Classes}",CLASSES_ITEMS:"{Wym_Classes_Items}",CLASS_NAME:"{Wym_Class_Name}",CLASS_TITLE:"{Wym_Class_Title}",CONTAINERS:"{Wym_Containers}",CONTAINERS_ITEMS:"{Wym_Containers_Items}",CONTAINER_NAME:"{Wym_Container_Name}",CONTAINER_TITLE:"{Wym_Containers_Title}",CONTAINER_CLASS:"{Wym_Container_Class}",HTML:"{Wym_Html}",IFRAME:"{Wym_Iframe}",STATUS:"{Wym_Status}",DIALOG_TITLE:"{Wym_Dialog_Title}",DIALOG_BODY:"{Wym_Dialog_Body}",STRING:"string",BODY:"body",DIV:"div",P:"p",H1:"h1",H2:"h2",H3:"h3",H4:"h4",H5:"h5",H6:"h6",PRE:"pre",BLOCKQUOTE:"blockquote",A:"a",BR:"br",IMG:"img",TABLE:"table",TD:"td",TH:"th",UL:"ul",OL:"ol",LI:"li",CLASS:"class",HREF:"href",SRC:"src",TITLE:"title",ALT:"alt",DIALOG_LINK:"Link",DIALOG_IMAGE:"Image",DIALOG_TABLE:"Table",DIALOG_PASTE:"Paste_From_Word",BOLD:"Bold",ITALIC:"Italic",CREATE_LINK:"CreateLink",INSERT_IMAGE:"InsertImage",INSERT_TABLE:"InsertTable",INSERT_HTML:"InsertHTML",PASTE:"Paste",INDENT:"Indent",OUTDENT:"Outdent",TOGGLE_HTML:"ToggleHtml",FORMAT_BLOCK:"FormatBlock",PREVIEW:"Preview",UNLINK:"Unlink",INSERT_UNORDEREDLIST:"InsertUnorderedList",INSERT_ORDEREDLIST:"InsertOrderedList",MAIN_CONTAINERS:new Array("p","h1","h2","h3","h4","h5","h6","pre","blockquote"),BLOCKS:new Array("address","blockquote","div","dl","fieldset","form","h1","h2","h3","h4","h5","h6","hr","noscript","ol","p","pre","table","ul","dd","dt","li","tbody","td","tfoot","th","thead","tr"),KEY:{BACKSPACE:8,ENTER:13,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,CURSOR:new Array(37,38,39,40),DELETE:46},NODE:{ELEMENT:1,ATTRIBUTE:2,TEXT:3},editor:function(b,a){this._index=WYMeditor.INSTANCES.push(this)-1;this._element=b;this._options=a;this._html=jQuery(b).val();if(this._options.html){this._html=this._options.html}this._options.basePath=this._options.basePath||this.computeBasePath();this._options.skinPath=this._options.skinPath||this._options.basePath+WYMeditor.SKINS_DEFAULT_PATH+this._options.skin+"/";this._options.wymPath=this._options.wymPath||this.computeWymPath();this._options.langPath=this._options.langPath||this._options.basePath+WYMeditor.LANG_DEFAULT_PATH;this._options.iframeBasePath=this._options.iframeBasePath||this._options.basePath+WYMeditor.IFRAME_DEFAULT;this._options.jQueryPath=this._options.jQueryPath||this.computeJqueryPath();this.init()}});jQuery.fn.wymeditor=function(a){a=jQuery.extend({html:"",basePath:false,skinPath:false,wymPath:false,iframeBasePath:false,jQueryPath:false,styles:false,stylesheet:false,skin:"default",initSkin:true,loadSkin:true,lang:"en",direction:"ltr",boxHtml:"<div class='wym_box'><div class='wym_area_top'>"+WYMeditor.TOOLS+"</div><div class='wym_area_left'></div><div class='wym_area_right'>"+WYMeditor.CONTAINERS+WYMeditor.CLASSES+"</div><div class='wym_area_main'>"+WYMeditor.HTML+WYMeditor.IFRAME+WYMeditor.STATUS+"</div><div class='wym_area_bottom'>"+WYMeditor.LOGO+"</div></div>",logoHtml:"<a class='wym_wymeditor_link' href='http://www.wymeditor.org/'>WYMeditor</a>",iframeHtml:"<div class='wym_iframe wym_section'><iframe src='"+WYMeditor.IFRAME_BASE_PATH+"wymiframe.html' onload='this.contentWindow.parent.WYMeditor.INSTANCES["+WYMeditor.INDEX+"].initIframe(this)'></iframe></div>",editorStyles:[],toolsHtml:"<div class='wym_tools wym_section'><h2>{Tools}</h2><ul>"+WYMeditor.TOOLS_ITEMS+"</ul></div>",toolsItemHtml:"<li class='"+WYMeditor.TOOL_CLASS+"'><a href='#' name='"+WYMeditor.TOOL_NAME+"' title='"+WYMeditor.TOOL_TITLE+"'>"+WYMeditor.TOOL_TITLE+"</a></li>",toolsItems:[{name:"Bold",title:"Strong",css:"wym_tools_strong"},{name:"Italic",title:"Emphasis",css:"wym_tools_emphasis"},{name:"Superscript",title:"Superscript",css:"wym_tools_superscript"},{name:"Subscript",title:"Subscript",css:"wym_tools_subscript"},{name:"InsertOrderedList",title:"Ordered_List",css:"wym_tools_ordered_list"},{name:"InsertUnorderedList",title:"Unordered_List",css:"wym_tools_unordered_list"},{name:"Indent",title:"Indent",css:"wym_tools_indent"},{name:"Outdent",title:"Outdent",css:"wym_tools_outdent"},{name:"Undo",title:"Undo",css:"wym_tools_undo"},{name:"Redo",title:"Redo",css:"wym_tools_redo"},{name:"CreateLink",title:"Link",css:"wym_tools_link"},{name:"Unlink",title:"Unlink",css:"wym_tools_unlink"},{name:"InsertImage",title:"Image",css:"wym_tools_image"},{name:"InsertTable",title:"Table",css:"wym_tools_table"},{name:"Paste",title:"Paste_From_Word",css:"wym_tools_paste"},{name:"ToggleHtml",title:"HTML",css:"wym_tools_html"},{name:"Preview",title:"Preview",css:"wym_tools_preview"}],containersHtml:"<div class='wym_containers wym_section'><h2>{Containers}</h2><ul>"+WYMeditor.CONTAINERS_ITEMS+"</ul></div>",containersItemHtml:"<li class='"+WYMeditor.CONTAINER_CLASS+"'><a href='#' name='"+WYMeditor.CONTAINER_NAME+"'>"+WYMeditor.CONTAINER_TITLE+"</a></li>",containersItems:[{name:"P",title:"Paragraph",css:"wym_containers_p"},{name:"H1",title:"Heading_1",css:"wym_containers_h1"},{name:"H2",title:"Heading_2",css:"wym_containers_h2"},{name:"H3",title:"Heading_3",css:"wym_containers_h3"},{name:"H4",title:"Heading_4",css:"wym_containers_h4"},{name:"H5",title:"Heading_5",css:"wym_containers_h5"},{name:"H6",title:"Heading_6",css:"wym_containers_h6"},{name:"PRE",title:"Preformatted",css:"wym_containers_pre"},{name:"BLOCKQUOTE",title:"Blockquote",css:"wym_containers_blockquote"},{name:"TH",title:"Table_Header",css:"wym_containers_th"}],classesHtml:"<div class='wym_classes wym_section'><h2>{Classes}</h2><ul>"+WYMeditor.CLASSES_ITEMS+"</ul></div>",classesItemHtml:"<li class='wym_classes_"+WYMeditor.CLASS_NAME+"'><a href='#' name='"+WYMeditor.CLASS_NAME+"'>"+WYMeditor.CLASS_TITLE+"</a></li>",classesItems:[],statusHtml:"<div class='wym_status wym_section'><h2>{Status}</h2></div>",htmlHtml:"<div class='wym_html wym_section'><h2>{Source_Code}</h2><textarea class='wym_html_val'></textarea></div>",boxSelector:".wym_box",toolsSelector:".wym_tools",toolsListSelector:" ul",containersSelector:".wym_containers",classesSelector:".wym_classes",htmlSelector:".wym_html",iframeSelector:".wym_iframe iframe",iframeBodySelector:".wym_iframe",statusSelector:".wym_status",toolSelector:".wym_tools a",containerSelector:".wym_containers a",classSelector:".wym_classes a",htmlValSelector:".wym_html_val",hrefSelector:".wym_href",srcSelector:".wym_src",titleSelector:".wym_title",altSelector:".wym_alt",textSelector:".wym_text",rowsSelector:".wym_rows",colsSelector:".wym_cols",captionSelector:".wym_caption",summarySelector:".wym_summary",submitSelector:".wym_submit",cancelSelector:".wym_cancel",previewSelector:"",dialogTypeSelector:".wym_dialog_type",dialogLinkSelector:".wym_dialog_link",dialogImageSelector:".wym_dialog_image",dialogTableSelector:".wym_dialog_table",dialogPasteSelector:".wym_dialog_paste",dialogPreviewSelector:".wym_dialog_preview",updateSelector:".wymupdate",updateEvent:"click",dialogFeatures:"menubar=no,titlebar=no,toolbar=no,resizable=no,width=560,height=300,top=0,left=0",dialogFeaturesPreview:"menubar=no,titlebar=no,toolbar=no,resizable=no,scrollbars=yes,width=560,height=300,top=0,left=0",dialogHtml:"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'><html dir='"+WYMeditor.DIRECTION+"'><head><link rel='stylesheet' type='text/css' media='screen' href='"+WYMeditor.CSS_PATH+"' /><title>"+WYMeditor.DIALOG_TITLE+"</title><script type='text/javascript' src='"+WYMeditor.JQUERY_PATH+"'><\/script><script type='text/javascript' src='"+WYMeditor.WYM_PATH+"'><\/script></head>"+WYMeditor.DIALOG_BODY+"</html>",dialogLinkHtml:"<body class='wym_dialog wym_dialog_link' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'><form><fieldset><input type='hidden' class='wym_dialog_type' value='"+WYMeditor.DIALOG_LINK+"' /><legend>{Link}</legend><div class='row'><label>{URL}</label><input type='text' class='wym_href' value='' size='40' /></div><div class='row'><label>{Title}</label><input type='text' class='wym_title' value='' size='40' /></div><div class='row row-indent'><input class='wym_submit' type='button' value='{Submit}' /><input class='wym_cancel' type='button'value='{Cancel}' /></div></fieldset></form></body>",dialogImageHtml:"<body class='wym_dialog wym_dialog_image' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'><form><fieldset><input type='hidden' class='wym_dialog_type' value='"+WYMeditor.DIALOG_IMAGE+"' /><legend>{Image}</legend><div class='row'><label>{URL}</label><input type='text' class='wym_src' value='' size='40' /></div><div class='row'><label>{Alternative_Text}</label><input type='text' class='wym_alt' value='' size='40' /></div><div class='row'><label>{Title}</label><input type='text' class='wym_title' value='' size='40' /></div><div class='row row-indent'><input class='wym_submit' type='button' value='{Submit}' /><input class='wym_cancel' type='button'value='{Cancel}' /></div></fieldset></form></body>",dialogTableHtml:"<body class='wym_dialog wym_dialog_table' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'><form><fieldset><input type='hidden' class='wym_dialog_type' value='"+WYMeditor.DIALOG_TABLE+"' /><legend>{Table}</legend><div class='row'><label>{Caption}</label><input type='text' class='wym_caption' value='' size='40' /></div><div class='row'><label>{Summary}</label><input type='text' class='wym_summary' value='' size='40' /></div><div class='row'><label>{Number_Of_Rows}</label><input type='text' class='wym_rows' value='3' size='3' /></div><div class='row'><label>{Number_Of_Cols}</label><input type='text' class='wym_cols' value='2' size='3' /></div><div class='row row-indent'><input class='wym_submit' type='button' value='{Submit}' /><input class='wym_cancel' type='button'value='{Cancel}' /></div></fieldset></form></body>",dialogPasteHtml:"<body class='wym_dialog wym_dialog_paste' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'><form><input type='hidden' class='wym_dialog_type' value='"+WYMeditor.DIALOG_PASTE+"' /><fieldset><legend>{Paste_From_Word}</legend><div class='row'><textarea class='wym_text' rows='10' cols='50'></textarea></div><div class='row'><input class='wym_submit' type='button' value='{Submit}' /><input class='wym_cancel' type='button'value='{Cancel}' /></div></fieldset></form></body>",dialogPreviewHtml:"<body class='wym_dialog wym_dialog_preview' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'></body>",dialogStyles:[],stringDelimiterLeft:"{",stringDelimiterRight:"}",preInit:null,preBind:null,postInit:null,preInitDialog:null,postInitDialog:null},a);return this.each(function(){new WYMeditor.editor(jQuery(this),a)})};jQuery.extend({wymeditors:function(a){return(WYMeditor.INSTANCES[a])}});WYMeditor.editor.prototype.init=function(){if(jQuery.browser.msie){var WymClass=new WYMeditor.WymClassExplorer(this)}else{if(jQuery.browser.mozilla){var WymClass=new WYMeditor.WymClassMozilla(this)}else{if(jQuery.browser.opera){var WymClass=new WYMeditor.WymClassOpera(this)}else{if(jQuery.browser.safari){var WymClass=new WYMeditor.WymClassSafari(this)}}}}if(WymClass){if(jQuery.isFunction(this._options.preInit)){this._options.preInit(this)}var SaxListener=new WYMeditor.XhtmlSaxListener();jQuery.extend(SaxListener,WymClass);this.parser=new WYMeditor.XhtmlParser(SaxListener);if(this._options.styles||this._options.stylesheet){this.configureEditorUsingRawCss()}this.helper=new WYMeditor.XmlHelper();for(var prop in WymClass){this[prop]=WymClass[prop]}this._box=jQuery(this._element).hide().after(this._options.boxHtml).next().addClass("wym_box_"+this._index);if(jQuery.isFunction(jQuery.fn.data)){jQuery.data(this._box.get(0),WYMeditor.WYM_INDEX,this._index);jQuery.data(this._element.get(0),WYMeditor.WYM_INDEX,this._index)}var h=WYMeditor.Helper;var iframeHtml=this._options.iframeHtml;iframeHtml=h.replaceAll(iframeHtml,WYMeditor.INDEX,this._index);iframeHtml=h.replaceAll(iframeHtml,WYMeditor.IFRAME_BASE_PATH,this._options.iframeBasePath);var boxHtml=jQuery(this._box).html();boxHtml=h.replaceAll(boxHtml,WYMeditor.LOGO,this._options.logoHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.TOOLS,this._options.toolsHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.CONTAINERS,this._options.containersHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.CLASSES,this._options.classesHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.HTML,this._options.htmlHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.IFRAME,iframeHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.STATUS,this._options.statusHtml);var aTools=eval(this._options.toolsItems);var sTools="";for(var i=0;i<aTools.length;i++){var oTool=aTools[i];if(oTool.name&&oTool.title){var sTool=this._options.toolsItemHtml}var sTool=h.replaceAll(sTool,WYMeditor.TOOL_NAME,oTool.name);sTool=h.replaceAll(sTool,WYMeditor.TOOL_TITLE,this._options.stringDelimiterLeft+oTool.title+this._options.stringDelimiterRight);sTool=h.replaceAll(sTool,WYMeditor.TOOL_CLASS,oTool.css);sTools+=sTool}boxHtml=h.replaceAll(boxHtml,WYMeditor.TOOLS_ITEMS,sTools);var aClasses=eval(this._options.classesItems);var sClasses="";for(var i=0;i<aClasses.length;i++){var oClass=aClasses[i];if(oClass.name&&oClass.title){var sClass=this._options.classesItemHtml}sClass=h.replaceAll(sClass,WYMeditor.CLASS_NAME,oClass.name);sClass=h.replaceAll(sClass,WYMeditor.CLASS_TITLE,oClass.title);sClasses+=sClass}boxHtml=h.replaceAll(boxHtml,WYMeditor.CLASSES_ITEMS,sClasses);var aContainers=eval(this._options.containersItems);var sContainers="";for(var i=0;i<aContainers.length;i++){var oContainer=aContainers[i];if(oContainer.name&&oContainer.title){var sContainer=this._options.containersItemHtml}sContainer=h.replaceAll(sContainer,WYMeditor.CONTAINER_NAME,oContainer.name);sContainer=h.replaceAll(sContainer,WYMeditor.CONTAINER_TITLE,this._options.stringDelimiterLeft+oContainer.title+this._options.stringDelimiterRight);sContainer=h.replaceAll(sContainer,WYMeditor.CONTAINER_CLASS,oContainer.css);sContainers+=sContainer}boxHtml=h.replaceAll(boxHtml,WYMeditor.CONTAINERS_ITEMS,sContainers);boxHtml=this.replaceStrings(boxHtml);jQuery(this._box).html(boxHtml);jQuery(this._box).find(this._options.htmlSelector).hide();this.loadSkin()}};WYMeditor.editor.prototype.bindEvents=function(){var wym=this;jQuery(this._box).find(this._options.toolSelector).click(function(){wym._iframe.contentWindow.focus();wym.exec(jQuery(this).attr(WYMeditor.NAME));return(false)});jQuery(this._box).find(this._options.containerSelector).click(function(){wym.container(jQuery(this).attr(WYMeditor.NAME));return(false)});jQuery(this._box).find(this._options.htmlValSelector).keyup(function(){jQuery(wym._doc.body).html(jQuery(this).val())}).focus(function(){jQuery(this).toggleClass("hasfocus")}).blur(function(){jQuery(this).toggleClass("hasfocus")});jQuery(this._box).find(this._options.classSelector).click(function(){var aClasses=eval(wym._options.classesItems);var sName=jQuery(this).attr(WYMeditor.NAME);var oClass=WYMeditor.Helper.findByName(aClasses,sName);if(oClass){var jqexpr=oClass.expr;wym.toggleClass(sName,jqexpr)}wym._iframe.contentWindow.focus();return(false)});jQuery(this._options.updateSelector).bind(this._options.updateEvent,function(){wym.update()})};WYMeditor.editor.prototype.ready=function(){return(this._doc!=null)};WYMeditor.editor.prototype.box=function(){return(this._box)};WYMeditor.editor.prototype.html=function(a){if(typeof a==="string"){jQuery(this._doc.body).html(a)}else{return(jQuery(this._doc.body).html())}};WYMeditor.editor.prototype.xhtml=function(){return this.parser.parse(this.html())};WYMeditor.editor.prototype.exec=function(b){switch(b){case WYMeditor.CREATE_LINK:var a=this.container();if(a||this._selected_image){this.dialog(WYMeditor.DIALOG_LINK)}break;case WYMeditor.INSERT_IMAGE:this.dialog(WYMeditor.DIALOG_IMAGE);break;case WYMeditor.INSERT_TABLE:this.dialog(WYMeditor.DIALOG_TABLE);break;case WYMeditor.PASTE:this.dialog(WYMeditor.DIALOG_PASTE);break;case WYMeditor.TOGGLE_HTML:this.update();this.toggleHtml();break;case WYMeditor.PREVIEW:this.dialog(WYMeditor.PREVIEW,this._options.dialogFeaturesPreview);break;default:this._exec(b);break}};WYMeditor.editor.prototype.container=function(a){if(a){var d=null;if(a.toLowerCase()==WYMeditor.TH){d=this.container();switch(d.tagName.toLowerCase()){case WYMeditor.TD:case WYMeditor.TH:break;default:var e=new Array(WYMeditor.TD,WYMeditor.TH);d=this.findUp(this.container(),e);break}if(d!=null){a=(d.tagName.toLowerCase()==WYMeditor.TD)?WYMeditor.TH:WYMeditor.TD;this.switchTo(d,a);this.update()}}else{var e=new Array(WYMeditor.P,WYMeditor.H1,WYMeditor.H2,WYMeditor.H3,WYMeditor.H4,WYMeditor.H5,WYMeditor.H6,WYMeditor.PRE,WYMeditor.BLOCKQUOTE);d=this.findUp(this.container(),e);if(d){var i=null;if(a.toLowerCase()==WYMeditor.BLOCKQUOTE){var f=this.findUp(this.container(),WYMeditor.BLOCKQUOTE);if(f==null){i=this._doc.createElement(a);d.parentNode.insertBefore(i,d);i.appendChild(d);this.setFocusToNode(i.firstChild)}else{var c=f.childNodes;var g=c.length;var b=null;if(g>0){b=c.item(0)}for(var h=0;h<g;h++){f.parentNode.insertBefore(c.item(0),f)}f.parentNode.removeChild(f);if(b){this.setFocusToNode(b)}}}else{this.switchTo(d,a)}this.update()}}}else{return(this.selected())}};WYMeditor.editor.prototype.toggleClass=function(c,b){var a=(this._selected_image?this._selected_image:jQuery(this.selected()));a=jQuery(a).parentsOrSelf(b);jQuery(a).toggleClass(c);if(!jQuery(a).attr(WYMeditor.CLASS)){jQuery(a).removeAttr(this._class)}};WYMeditor.editor.prototype.findUp=function(d,c){if(d){var e=d.tagName.toLowerCase();if(typeof(c)==WYMeditor.STRING){while(e!=c&&e!=WYMeditor.BODY){d=d.parentNode;e=d.tagName.toLowerCase()}}else{var b=false;while(!b&&e!=WYMeditor.BODY){for(var a=0;a<c.length;a++){if(e==c[a]){b=true;break}}if(!b){d=d.parentNode;e=d.tagName.toLowerCase()}}}if(e!=WYMeditor.BODY){return(d)}else{return(null)}}else{return(null)}};WYMeditor.editor.prototype.switchTo=function(c,d){var b=this._doc.createElement(d);var a=jQuery(c).html();c.parentNode.replaceChild(b,c);jQuery(b).html(a);this.setFocusToNode(b)};WYMeditor.editor.prototype.replaceStrings=function(sVal){if(!WYMeditor.STRINGS[this._options.lang]){try{eval(jQuery.ajax({url:this._options.langPath+this._options.lang+".js",async:false}).responseText)}catch(e){WYMeditor.console.error("WYMeditor: error while parsing language file.");return sVal}}for(var key in WYMeditor.STRINGS[this._options.lang]){sVal=WYMeditor.Helper.replaceAll(sVal,this._options.stringDelimiterLeft+key+this._options.stringDelimiterRight,WYMeditor.STRINGS[this._options.lang][key])}return(sVal)};WYMeditor.editor.prototype.encloseString=function(a){return(this._options.stringDelimiterLeft+a+this._options.stringDelimiterRight)};WYMeditor.editor.prototype.status=function(a){jQuery(this._box).find(this._options.statusSelector).html(a)};WYMeditor.editor.prototype.update=function(){var a=this.xhtml();jQuery(this._element).val(a);jQuery(this._box).find(this._options.htmlValSelector).not(".hasfocus").val(a)};WYMeditor.editor.prototype.dialog=function(j,c,f){var a=c||this._wym._options.dialogFeatures;var d=window.open("","dialog",a);if(d){var b="";switch(j){case (WYMeditor.DIALOG_LINK):b=this._options.dialogLinkHtml;break;case (WYMeditor.DIALOG_IMAGE):b=this._options.dialogImageHtml;break;case (WYMeditor.DIALOG_TABLE):b=this._options.dialogTableHtml;break;case (WYMeditor.DIALOG_PASTE):b=this._options.dialogPasteHtml;break;case (WYMeditor.PREVIEW):b=this._options.dialogPreviewHtml;break;default:b=f}var e=WYMeditor.Helper;var g=this._options.dialogHtml;g=e.replaceAll(g,WYMeditor.BASE_PATH,this._options.basePath);g=e.replaceAll(g,WYMeditor.DIRECTION,this._options.direction);g=e.replaceAll(g,WYMeditor.CSS_PATH,this._options.skinPath+WYMeditor.SKINS_DEFAULT_CSS);g=e.replaceAll(g,WYMeditor.WYM_PATH,this._options.wymPath);g=e.replaceAll(g,WYMeditor.JQUERY_PATH,this._options.jQueryPath);g=e.replaceAll(g,WYMeditor.DIALOG_TITLE,this.encloseString(j));g=e.replaceAll(g,WYMeditor.DIALOG_BODY,b);g=e.replaceAll(g,WYMeditor.INDEX,this._index);g=this.replaceStrings(g);var i=d.document;i.write(g);i.close()}};WYMeditor.editor.prototype.toggleHtml=function(){jQuery(this._box).find(this._options.htmlSelector).toggle()};WYMeditor.editor.prototype.uniqueStamp=function(){var a=new Date();return("wym-"+a.getTime())};WYMeditor.editor.prototype.paste=function(b){var e;var a=this.selected();var c=b.split(this._newLine+this._newLine);var d=new RegExp(this._newLine,"g");if(a&&a.tagName.toLowerCase()!=WYMeditor.BODY){for(x=c.length-1;x>=0;x--){e=c[x];e=e.replace(d,"<br />");jQuery(a).after("<p>"+e+"</p>")}}else{for(x=0;x<c.length;x++){e=c[x];e=e.replace(d,"<br />");jQuery(this._doc.body).append("<p>"+e+"</p>")}}};WYMeditor.editor.prototype.insert=function(a){if(this._iframe.contentWindow.getSelection().focusNode!=null){this._exec(WYMeditor.INSERT_HTML,a)}else{this.paste(a)}};WYMeditor.editor.prototype.wrap=function(b,a){if(this._iframe.contentWindow.getSelection().focusNode!=null){this._exec(WYMeditor.INSERT_HTML,b+this._iframe.contentWindow.getSelection().toString()+a)}};WYMeditor.editor.prototype.unwrap=function(){if(this._iframe.contentWindow.getSelection().focusNode!=null){this._exec(WYMeditor.INSERT_HTML,this._iframe.contentWindow.getSelection().toString())}};WYMeditor.editor.prototype.setFocusToNode=function(d,b){var a=this._doc.createRange(),c=this._iframe.contentWindow.getSelection();b=b?0:1;a.selectNodeContents(d);c.addRange(a);c.collapse(d,b);this._iframe.contentWindow.focus()};WYMeditor.editor.prototype.addCssRules=function(e,c){var b=e.styleSheets[0];if(b){for(var a=0;a<c.length;a++){var d=c[a];if(d.name&&d.css){this.addCssRule(b,d)}}}};WYMeditor.editor.prototype.computeBasePath=function(){return jQuery(jQuery.grep(jQuery("script"),function(a){return(a.src&&a.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/))})).attr("src").replace(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/,"")};WYMeditor.editor.prototype.computeWymPath=function(){return jQuery(jQuery.grep(jQuery("script"),function(a){return(a.src&&a.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/))})).attr("src")};WYMeditor.editor.prototype.computeJqueryPath=function(){return jQuery(jQuery.grep(jQuery("script"),function(a){return(a.src&&a.src.match(/jquery(-(.*)){0,1}(\.pack|\.min|\.packed)?\.js(\?.*)?$/))})).attr("src")};WYMeditor.editor.prototype.computeCssPath=function(){return jQuery(jQuery.grep(jQuery("link"),function(a){return(a.href&&a.href.match(/wymeditor\/skins\/(.*)screen\.css(\?.*)?$/))})).attr("href")};WYMeditor.editor.prototype.configureEditorUsingRawCss=function(){var a=new WYMeditor.WymCssParser();if(this._options.stylesheet){a.parse(jQuery.ajax({url:this._options.stylesheet,async:false}).responseText)}else{a.parse(this._options.styles,false)}if(this._options.classesItems.length==0){this._options.classesItems=a.css_settings.classesItems}if(this._options.editorStyles.length==0){this._options.editorStyles=a.css_settings.editorStyles}if(this._options.dialogStyles.length==0){this._options.dialogStyles=a.css_settings.dialogStyles}};WYMeditor.editor.prototype.listen=function(){jQuery(this._doc.body).bind("mousedown",this.mousedown)};WYMeditor.editor.prototype.mousedown=function(a){var b=WYMeditor.INSTANCES[this.ownerDocument.title];b._selected_image=(a.target.tagName.toLowerCase()==WYMeditor.IMG)?a.target:null};WYMeditor.loadCss=function(a){var c=document.createElement("link");c.rel="stylesheet";c.href=a;var b=jQuery("head").get(0);b.appendChild(c)};WYMeditor.editor.prototype.loadSkin=function(){if(this._options.loadSkin&&!WYMeditor.SKINS[this._options.skin]){var found=false;var rExp=new RegExp(this._options.skin+"/"+WYMeditor.SKINS_DEFAULT_CSS+"$");jQuery("link").each(function(){if(this.href.match(rExp)){found=true}});if(!found){WYMeditor.loadCss(this._options.skinPath+WYMeditor.SKINS_DEFAULT_CSS)}}jQuery(this._box).addClass("wym_skin_"+this._options.skin);if(this._options.initSkin&&!WYMeditor.SKINS[this._options.skin]){eval(jQuery.ajax({url:this._options.skinPath+WYMeditor.SKINS_DEFAULT_JS,async:false}).responseText)}if(WYMeditor.SKINS[this._options.skin]&&WYMeditor.SKINS[this._options.skin].init){WYMeditor.SKINS[this._options.skin].init(this)}};WYMeditor.INIT_DIALOG=function(index){var wym=window.opener.WYMeditor.INSTANCES[index];var doc=window.document;var selected=wym.selected();var dialogType=jQuery(wym._options.dialogTypeSelector).val();var sStamp=wym.uniqueStamp();switch(dialogType){case WYMeditor.DIALOG_LINK:if(selected&&selected.tagName&&selected.tagName.toLowerCase!=WYMeditor.A){selected=jQuery(selected).parentsOrSelf(WYMeditor.A)}if(!selected&&wym._selected_image){selected=jQuery(wym._selected_image).parentsOrSelf(WYMeditor.A)}break}if(jQuery.isFunction(wym._options.preInitDialog)){wym._options.preInitDialog(wym,window)}var styles=doc.styleSheets[0];var aCss=eval(wym._options.dialogStyles);wym.addCssRules(doc,aCss);if(selected){jQuery(wym._options.hrefSelector).val(jQuery(selected).attr(WYMeditor.HREF));jQuery(wym._options.srcSelector).val(jQuery(selected).attr(WYMeditor.SRC));jQuery(wym._options.titleSelector).val(jQuery(selected).attr(WYMeditor.TITLE));jQuery(wym._options.altSelector).val(jQuery(selected).attr(WYMeditor.ALT))}if(wym._selected_image){jQuery(wym._options.dialogImageSelector+" "+wym._options.srcSelector).val(jQuery(wym._selected_image).attr(WYMeditor.SRC));jQuery(wym._options.dialogImageSelector+" "+wym._options.titleSelector).val(jQuery(wym._selected_image).attr(WYMeditor.TITLE));jQuery(wym._options.dialogImageSelector+" "+wym._options.altSelector).val(jQuery(wym._selected_image).attr(WYMeditor.ALT))}jQuery(wym._options.dialogLinkSelector+" "+wym._options.submitSelector).click(function(){var sUrl=jQuery(wym._options.hrefSelector).val();if(sUrl.length>0){var link;if(selected[0]&&selected[0].tagName.toLowerCase()==WYMeditor.A){link=selected}else{wym._exec(WYMeditor.CREATE_LINK,sStamp);link=jQuery("a[href="+sStamp+"]",wym._doc.body)}link.attr(WYMeditor.HREF,sUrl).attr(WYMeditor.TITLE,jQuery(wym._options.titleSelector).val())}window.close()});jQuery(wym._options.dialogImageSelector+" "+wym._options.submitSelector).click(function(){var sUrl=jQuery(wym._options.srcSelector).val();if(sUrl.length>0){wym._exec(WYMeditor.INSERT_IMAGE,sStamp);jQuery("img[src$="+sStamp+"]",wym._doc.body).attr(WYMeditor.SRC,sUrl).attr(WYMeditor.TITLE,jQuery(wym._options.titleSelector).val()).attr(WYMeditor.ALT,jQuery(wym._options.altSelector).val())}window.close()});jQuery(wym._options.dialogTableSelector+" "+wym._options.submitSelector).click(function(){var iRows=jQuery(wym._options.rowsSelector).val();var iCols=jQuery(wym._options.colsSelector).val();if(iRows>0&&iCols>0){var table=wym._doc.createElement(WYMeditor.TABLE);var newRow=null;var newCol=null;var sCaption=jQuery(wym._options.captionSelector).val();var newCaption=table.createCaption();newCaption.innerHTML=sCaption;for(x=0;x<iRows;x++){newRow=table.insertRow(x);for(y=0;y<iCols;y++){newRow.insertCell(y)}}jQuery(table).attr("summary",jQuery(wym._options.summarySelector).val());var node=jQuery(wym.findUp(wym.container(),WYMeditor.MAIN_CONTAINERS)).get(0);if(!node||!node.parentNode){jQuery(wym._doc.body).append(table)}else{jQuery(node).after(table)}}window.close()});jQuery(wym._options.dialogPasteSelector+" "+wym._options.submitSelector).click(function(){var sText=jQuery(wym._options.textSelector).val();wym.paste(sText);window.close()});jQuery(wym._options.dialogPreviewSelector+" "+wym._options.previewSelector).html(wym.xhtml());jQuery(wym._options.cancelSelector).mousedown(function(){window.close()});if(jQuery.isFunction(wym._options.postInitDialog)){wym._options.postInitDialog(wym,window)}};WYMeditor.XmlHelper=function(){this._entitiesDiv=document.createElement("div");return this};WYMeditor.XmlHelper.prototype.tag=function(c,b,a){b=b||false;a=a||false;return"<"+c+(b?this.tagOptions(b):"")+(a?">":" />")};WYMeditor.XmlHelper.prototype.contentTag=function(b,c,a){a=a||false;return"<"+b+(a?this.tagOptions(a):"")+">"+c+"</"+b+">"};WYMeditor.XmlHelper.prototype.cdataSection=function(a){return"<![CDATA["+a+"]]>"};WYMeditor.XmlHelper.prototype.escapeOnce=function(a){return this._fixDoubleEscape(this.escapeEntities(a))};WYMeditor.XmlHelper.prototype._fixDoubleEscape=function(a){return a.replace(/&amp;([a-z]+|(#\d+));/ig,"&$1;")};WYMeditor.XmlHelper.prototype.tagOptions=function(b){var a=this;a._formated_options="";for(var c in b){var d="";var e=b[c];if(typeof e!="function"&&e.length>0){if(parseInt(c)==c&&typeof e=="object"){c=e.shift();e=e.pop()}if(c!=""&&e!=""){a._formated_options+=" "+c+'="'+a.escapeOnce(e)+'"'}}}return a._formated_options};WYMeditor.XmlHelper.prototype.escapeEntities=function(c,b){this._entitiesDiv.innerHTML=c;this._entitiesDiv.textContent=c;var a=this._entitiesDiv.innerHTML;if(typeof b=="undefined"){if(b!=false){a=a.replace('"',"&quot;")}if(b==true){a=a.replace('"',"&#039;")}}return a};WYMeditor.XmlHelper.prototype.parseAttributes=function(h){var a=[];var g=h.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g);if(g.toString()!=h){for(var d in g){var c=g[d];if(typeof c!="function"&&c.length!=0){var e=new RegExp("(\\w+)\\s*"+c);if(match=h.match(e)){var f=c.replace(/^[\s=]+/,"");var b=f.charAt(0);b=b=='"'?'"':(b=="'"?"'":"");if(b!=""){f=b=='"'?f.replace(/^"|"+$/g,""):f.replace(/^'|'+$/g,"")}h=h.replace(match[0],"");a.push([match[1],f])}}}}return a};WYMeditor.XhtmlValidator={_attributes:{core:{except:["base","head","html","meta","param","script","style","title"],attributes:["class","id","style","title","accesskey","tabindex"]},language:{except:["base","br","hr","iframe","param","script"],attributes:{dir:["ltr","rtl"],"0":"lang","1":"xml:lang"}},keyboard:{attributes:{accesskey:/^(\w){1}$/,tabindex:/^(\d)+$/}}},_events:{window:{only:["body"],attributes:["onload","onunload"]},form:{only:["form","input","textarea","select","a","label","button"],attributes:["onchange","onsubmit","onreset","onselect","onblur","onfocus"]},keyboard:{except:["base","bdo","br","frame","frameset","head","html","iframe","meta","param","script","style","title"],attributes:["onkeydown","onkeypress","onkeyup"]},mouse:{except:["base","bdo","br","head","html","meta","param","script","style","title"],attributes:["onclick","ondblclick","onmousedown","onmousemove","onmouseover","onmouseout","onmouseup"]}},_tags:{a:{attributes:{"0":"charset","1":"coords","2":"href","3":"hreflang","4":"name",rel:/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,rev:/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,shape:/^(rect|rectangle|circ|circle|poly|polygon)$/,"5":"type"}},"0":"abbr","1":"acronym","2":"address",area:{attributes:{"0":"alt","1":"coords","2":"href",nohref:/^(true|false)$/,shape:/^(rect|rectangle|circ|circle|poly|polygon)$/},required:["alt"]},"3":"b",base:{attributes:["href"],required:["href"]},bdo:{attributes:{dir:/^(ltr|rtl)$/},required:["dir"]},"4":"big",blockquote:{attributes:["cite"]},"5":"body","6":"br",button:{attributes:{disabled:/^(disabled)$/,type:/^(button|reset|submit)$/,"0":"value"},inside:"form"},"7":"caption","8":"cite","9":"code",col:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",span:/^(\d)+$/,valign:/^(top|middle|bottom|baseline)$/,"2":"width"},inside:"colgroup"},colgroup:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",span:/^(\d)+$/,valign:/^(top|middle|bottom|baseline)$/,"2":"width"}},"10":"dd",del:{attributes:{"0":"cite",datetime:/^([0-9]){8}/}},"11":"div","12":"dfn","13":"dl","14":"dt","15":"em",fieldset:{inside:"form"},form:{attributes:{"0":"action","1":"accept","2":"accept-charset","3":"enctype",method:/^(get|post)$/},required:["action"]},head:{attributes:["profile"]},"16":"h1","17":"h2","18":"h3","19":"h4","20":"h5","21":"h6","22":"hr",html:{attributes:["xmlns"]},"23":"i",img:{attributes:["alt","src","height","ismap","longdesc","usemap","width"],required:["alt","src"]},input:{attributes:{"0":"accept","1":"alt",checked:/^(checked)$/,disabled:/^(disabled)$/,maxlength:/^(\d)+$/,"2":"name",readonly:/^(readonly)$/,size:/^(\d)+$/,"3":"src",type:/^(button|checkbox|file|hidden|image|password|radio|reset|submit|text)$/,"4":"value"},inside:"form"},ins:{attributes:{"0":"cite",datetime:/^([0-9]){8}/}},"24":"kbd",label:{attributes:["for"],inside:"form"},"25":"legend","26":"li",link:{attributes:{"0":"charset","1":"href","2":"hreflang",media:/^(all|braille|print|projection|screen|speech|,|;| )+$/i,rel:/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,rev:/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,"3":"type"},inside:"head"},map:{attributes:["id","name"],required:["id"]},meta:{attributes:{"0":"content","http-equiv":/^(content\-type|expires|refresh|set\-cookie)$/i,"1":"name","2":"scheme"},required:["content"]},"27":"noscript",object:{attributes:["archive","classid","codebase","codetype","data","declare","height","name","standby","type","usemap","width"]},"28":"ol",optgroup:{attributes:{"0":"label",disabled:/^(disabled)$/},required:["label"]},option:{attributes:{"0":"label",disabled:/^(disabled)$/,selected:/^(selected)$/,"1":"value"},inside:"select"},"29":"p",param:{attributes:{"0":"type",valuetype:/^(data|ref|object)$/,"1":"valuetype","2":"value"},required:["name"]},"30":"pre",q:{attributes:["cite"]},"31":"samp",script:{attributes:{type:/^(text\/ecmascript|text\/javascript|text\/jscript|text\/vbscript|text\/vbs|text\/xml)$/,"0":"charset",defer:/^(defer)$/,"1":"src"},required:["type"]},select:{attributes:{disabled:/^(disabled)$/,multiple:/^(multiple)$/,"0":"name","1":"size"},inside:"form"},"32":"small","33":"span","34":"strong",style:{attributes:{"0":"type",media:/^(screen|tty|tv|projection|handheld|print|braille|aural|all)$/},required:["type"]},"35":"sub","36":"sup",table:{attributes:{"0":"border","1":"cellpadding","2":"cellspacing",frame:/^(void|above|below|hsides|lhs|rhs|vsides|box|border)$/,rules:/^(none|groups|rows|cols|all)$/,"3":"summary","4":"width"}},tbody:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",valign:/^(top|middle|bottom|baseline)$/}},td:{attributes:{"0":"abbr",align:/^(left|right|center|justify|char)$/,"1":"axis","2":"char","3":"charoff",colspan:/^(\d)+$/,"4":"headers",rowspan:/^(\d)+$/,scope:/^(col|colgroup|row|rowgroup)$/,valign:/^(top|middle|bottom|baseline)$/}},textarea:{attributes:["cols","rows","disabled","name","readonly"],required:["cols","rows"],inside:"form"},tfoot:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",valign:/^(top|middle|bottom)$/,"2":"baseline"}},th:{attributes:{"0":"abbr",align:/^(left|right|center|justify|char)$/,"1":"axis","2":"char","3":"charoff",colspan:/^(\d)+$/,"4":"headers",rowspan:/^(\d)+$/,scope:/^(col|colgroup|row|rowgroup)$/,valign:/^(top|middle|bottom|baseline)$/}},thead:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",valign:/^(top|middle|bottom|baseline)$/}},"37":"title",tr:{attributes:{align:/^(right|left|center|justify|char)$/,"0":"char","1":"charoff",valign:/^(top|middle|bottom|baseline)$/}},"38":"tt","39":"ul","40":"var"},skiped_attributes:[],skiped_attribute_values:[],getValidTagAttributes:function(a,b){var c={};var g=this.getPossibleTagAttributes(a);for(var e in b){var f=b[e];var d=WYMeditor.Helper;if(!d.contains(this.skiped_attributes,e)&&!d.contains(this.skiped_attribute_values,f)){if(typeof f!="function"&&d.contains(g,e)){if(this.doesAttributeNeedsValidation(a,e)){if(this.validateAttribute(a,e,f)){c[e]=f}}else{c[e]=f}}}}return c},getUniqueAttributesAndEventsForTag:function(b){var a=[];if(this._tags[b]&&this._tags[b]["attributes"]){for(k in this._tags[b]["attributes"]){a.push(parseInt(k)==k?this._tags[b]["attributes"][k]:k)}}return a},getDefaultAttributesAndEventsForTags:function(){var a=[];for(var b in this._events){a.push(this._events[b])}for(var b in this._attributes){a.push(this._attributes[b])}return a},isValidTag:function(a){if(this._tags[a]){return true}for(var b in this._tags){if(this._tags[b]==a){return true}}return false},getDefaultAttributesAndEventsForTag:function(a){var i=[];if(this.isValidTag(a)){var g=this.getDefaultAttributesAndEventsForTags();for(var d in g){var f=g[d];if(typeof f=="object"){var e=WYMeditor.Helper;if((f.except&&e.contains(f.except,a))||(f.only&&!e.contains(f.only,a))){continue}var c=f.attributes?f.attributes:f.events;for(var b in c){i.push(typeof c[b]!="string"?b:c[b])}}}}return i},doesAttributeNeedsValidation:function(a,b){return this._tags[a]&&((this._tags[a]["attributes"]&&this._tags[a]["attributes"][b])||(this._tags[a]["required"]&&WYMeditor.Helper.contains(this._tags[a]["required"],b)))},validateAttribute:function(a,b,c){if(this._tags[a]&&(this._tags[a]["attributes"]&&this._tags[a]["attributes"][b]&&c.length>0&&!c.match(this._tags[a]["attributes"][b]))||(this._tags[a]&&this._tags[a]["required"]&&WYMeditor.Helper.contains(this._tags[a]["required"],b)&&c.length==0)){return false}return typeof this._tags[a]!="undefined"},getPossibleTagAttributes:function(a){if(!this._possible_tag_attributes){this._possible_tag_attributes={}}if(!this._possible_tag_attributes[a]){this._possible_tag_attributes[a]=this.getUniqueAttributesAndEventsForTag(a).concat(this.getDefaultAttributesAndEventsForTag(a))}return this._possible_tag_attributes[a]}};WYMeditor.ParallelRegex=function(a){this._case=a;this._patterns=[];this._labels=[];this._regex=null;return this};WYMeditor.ParallelRegex.prototype.addPattern=function(c,a){a=a||true;var b=this._patterns.length;this._patterns[b]=c;this._labels[b]=a;this._regex=null};WYMeditor.ParallelRegex.prototype.match=function(c){if(this._patterns.length==0){return[false,""]}var d=c.match(this._getCompoundedRegex());if(!d){return[false,""]}var a=d[0];for(var b=1;b<d.length;b++){if(d[b]){return[this._labels[b-1],a]}}return[true,d[0]]};WYMeditor.ParallelRegex.prototype._getCompoundedRegex=function(){if(this._regex==null){for(var a=0,b=this._patterns.length;a<b;a++){this._patterns[a]="("+this._untokenizeRegex(this._tokenizeRegex(this._patterns[a]).replace(/([\/\(\)])/g,"\\$1"))+")"}this._regex=new RegExp(this._patterns.join("|"),this._getPerlMatchingFlags())}return this._regex};WYMeditor.ParallelRegex.prototype._tokenizeRegex=function(a){return a.replace(/\(\?(i|m|s|x|U)\)/,"~~~~~~Tk1$1~~~~~~").replace(/\(\?(\-[i|m|s|x|U])\)/,"~~~~~~Tk2$1~~~~~~").replace(/\(\?\=(.*)\)/,"~~~~~~Tk3$1~~~~~~").replace(/\(\?\!(.*)\)/,"~~~~~~Tk4$1~~~~~~").replace(/\(\?\<\=(.*)\)/,"~~~~~~Tk5$1~~~~~~").replace(/\(\?\<\!(.*)\)/,"~~~~~~Tk6$1~~~~~~").replace(/\(\?\:(.*)\)/,"~~~~~~Tk7$1~~~~~~")};WYMeditor.ParallelRegex.prototype._untokenizeRegex=function(a){return a.replace(/~~~~~~Tk1(.{1})~~~~~~/,"(?$1)").replace(/~~~~~~Tk2(.{2})~~~~~~/,"(?$1)").replace(/~~~~~~Tk3(.*)~~~~~~/,"(?=$1)").replace(/~~~~~~Tk4(.*)~~~~~~/,"(?!$1)").replace(/~~~~~~Tk5(.*)~~~~~~/,"(?<=$1)").replace(/~~~~~~Tk6(.*)~~~~~~/,"(?<!$1)").replace(/~~~~~~Tk7(.*)~~~~~~/,"(?:$1)")};WYMeditor.ParallelRegex.prototype._getPerlMatchingFlags=function(){return(this._case?"m":"mi")};WYMeditor.StateStack=function(a){this._stack=[a];return this};WYMeditor.StateStack.prototype.getCurrent=function(){return this._stack[this._stack.length-1]};WYMeditor.StateStack.prototype.enter=function(a){this._stack.push(a)};WYMeditor.StateStack.prototype.leave=function(){if(this._stack.length==1){return false}this._stack.pop();return true};WYMeditor.LEXER_ENTER=1;WYMeditor.LEXER_MATCHED=2;WYMeditor.LEXER_UNMATCHED=3;WYMeditor.LEXER_EXIT=4;WYMeditor.LEXER_SPECIAL=5;WYMeditor.Lexer=function(c,b,a){b=b||"accept";this._case=a||false;this._regexes={};this._parser=c;this._mode=new WYMeditor.StateStack(b);this._mode_handlers={};this._mode_handlers[b]=b;return this};WYMeditor.Lexer.prototype.addPattern=function(a,b){var b=b||"accept";if(typeof this._regexes[b]=="undefined"){this._regexes[b]=new WYMeditor.ParallelRegex(this._case)}this._regexes[b].addPattern(a);if(typeof this._mode_handlers[b]=="undefined"){this._mode_handlers[b]=b}};WYMeditor.Lexer.prototype.addEntryPattern=function(b,c,a){if(typeof this._regexes[c]=="undefined"){this._regexes[c]=new WYMeditor.ParallelRegex(this._case)}this._regexes[c].addPattern(b,a);if(typeof this._mode_handlers[a]=="undefined"){this._mode_handlers[a]=a}};WYMeditor.Lexer.prototype.addExitPattern=function(a,b){if(typeof this._regexes[b]=="undefined"){this._regexes[b]=new WYMeditor.ParallelRegex(this._case)}this._regexes[b].addPattern(a,"__exit");if(typeof this._mode_handlers[b]=="undefined"){this._mode_handlers[b]=b}};WYMeditor.Lexer.prototype.addSpecialPattern=function(b,c,a){if(typeof this._regexes[c]=="undefined"){this._regexes[c]=new WYMeditor.ParallelRegex(this._case)}this._regexes[c].addPattern(b,"_"+a);if(typeof this._mode_handlers[a]=="undefined"){this._mode_handlers[a]=a}};WYMeditor.Lexer.prototype.mapHandler=function(b,a){this._mode_handlers[b]=a};WYMeditor.Lexer.prototype.parse=function(d){if(typeof this._parser=="undefined"){return false}var e=d.length;var c;while(typeof(c=this._reduce(d))=="object"){var d=c[0];var b=c[1];var a=c[2];var f=c[3];if(!this._dispatchTokens(b,a,f)){return false}if(d==""){return true}if(d.length==e){return false}e=d.length}if(!c){return false}return this._invokeParser(d,WYMeditor.LEXER_UNMATCHED)};WYMeditor.Lexer.prototype._dispatchTokens=function(b,a,c){c=c||false;if(!this._invokeParser(b,WYMeditor.LEXER_UNMATCHED)){return false}if(typeof c=="boolean"){return this._invokeParser(a,WYMeditor.LEXER_MATCHED)}if(this._isModeEnd(c)){if(!this._invokeParser(a,WYMeditor.LEXER_EXIT)){return false}return this._mode.leave()}if(this._isSpecialMode(c)){this._mode.enter(this._decodeSpecial(c));if(!this._invokeParser(a,WYMeditor.LEXER_SPECIAL)){return false}return this._mode.leave()}this._mode.enter(c);return this._invokeParser(a,WYMeditor.LEXER_ENTER)};WYMeditor.Lexer.prototype._isModeEnd=function(a){return(a==="__exit")};WYMeditor.Lexer.prototype._isSpecialMode=function(a){return(a.substring(0,1)=="_")};WYMeditor.Lexer.prototype._decodeSpecial=function(a){return a.substring(1)};WYMeditor.Lexer.prototype._invokeParser=function(content,is_match){if(content===""){return true}var current=this._mode.getCurrent();var handler=this._mode_handlers[current];var result;eval("result = this._parser."+handler+"(content, is_match);");return result};WYMeditor.Lexer.prototype._reduce=function(c){var a=this._regexes[this._mode.getCurrent()].match(c);var b=a[1];var e=a[0];if(e){var f=c.indexOf(b);var d=c.substr(0,f);c=c.substring(f+b.length);return[c,d,b,e]}return true};WYMeditor.XhtmlLexer=function(a){jQuery.extend(this,new WYMeditor.Lexer(a,"Text"));this.mapHandler("Text","Text");this.addTokens();this.init();return this};WYMeditor.XhtmlLexer.prototype.init=function(){};WYMeditor.XhtmlLexer.prototype.addTokens=function(){this.addCommentTokens("Text");this.addScriptTokens("Text");this.addCssTokens("Text");this.addTagTokens("Text")};WYMeditor.XhtmlLexer.prototype.addCommentTokens=function(a){this.addEntryPattern("<!--",a,"Comment");this.addExitPattern("-->","Comment")};WYMeditor.XhtmlLexer.prototype.addScriptTokens=function(a){this.addEntryPattern("<script",a,"Script");this.addExitPattern("<\/script>","Script")};WYMeditor.XhtmlLexer.prototype.addCssTokens=function(a){this.addEntryPattern("<style",a,"Css");this.addExitPattern("</style>","Css")};WYMeditor.XhtmlLexer.prototype.addTagTokens=function(a){this.addSpecialPattern("<\\s*[a-z0-9:-]+\\s*>",a,"OpeningTag");this.addEntryPattern("<[a-z0-9:-]+[\\/ \\>]+",a,"OpeningTag");this.addInTagDeclarationTokens("OpeningTag");this.addSpecialPattern("</\\s*[a-z0-9:-]+\\s*>",a,"ClosingTag")};WYMeditor.XhtmlLexer.prototype.addInTagDeclarationTokens=function(a){this.addSpecialPattern("\\s+",a,"Ignore");this.addAttributeTokens(a);this.addExitPattern("/>",a);this.addExitPattern(">",a)};WYMeditor.XhtmlLexer.prototype.addAttributeTokens=function(a){this.addSpecialPattern("\\s*[a-z-_0-9]*:?[a-z-_0-9]+\\s*(?==)\\s*",a,"TagAttributes");this.addEntryPattern('=\\s*"',a,"DoubleQuotedAttribute");this.addPattern('\\\\"',"DoubleQuotedAttribute");this.addExitPattern('"',"DoubleQuotedAttribute");this.addEntryPattern("=\\s*'",a,"SingleQuotedAttribute");this.addPattern("\\\\'","SingleQuotedAttribute");this.addExitPattern("'","SingleQuotedAttribute");this.addSpecialPattern("=\\s*[^>\\s]*",a,"UnquotedAttribute")};WYMeditor.XhtmlParser=function(a,b){var b=b||"Text";this._Lexer=new WYMeditor.XhtmlLexer(this);this._Listener=a;this._mode=b;this._matches=[];this._last_match="";this._current_match="";return this};WYMeditor.XhtmlParser.prototype.parse=function(a){this._Lexer.parse(this.beforeParsing(a));return this.afterParsing(this._Listener.getResult())};WYMeditor.XhtmlParser.prototype.beforeParsing=function(a){if(a.match(/class="MsoNormal"/)||a.match(/ns = "urn:schemas-microsoft-com/)){this._Listener.avoidStylingTagsAndAttributes()}return this._Listener.beforeParsing(a)};WYMeditor.XhtmlParser.prototype.afterParsing=function(a){if(this._Listener._avoiding_tags_implicitly){this._Listener.allowStylingTagsAndAttributes()}return this._Listener.afterParsing(a)};WYMeditor.XhtmlParser.prototype.Ignore=function(a,b){return true};WYMeditor.XhtmlParser.prototype.Text=function(a){this._Listener.addContent(a);return true};WYMeditor.XhtmlParser.prototype.Comment=function(b,a){return this._addNonTagBlock(b,a,"addComment")};WYMeditor.XhtmlParser.prototype.Script=function(b,a){return this._addNonTagBlock(b,a,"addScript")};WYMeditor.XhtmlParser.prototype.Css=function(b,a){return this._addNonTagBlock(b,a,"addCss")};WYMeditor.XhtmlParser.prototype._addNonTagBlock=function(a,c,b){switch(c){case WYMeditor.LEXER_ENTER:this._non_tag=a;break;case WYMeditor.LEXER_UNMATCHED:this._non_tag+=a;break;case WYMeditor.LEXER_EXIT:switch(b){case"addComment":this._Listener.addComment(this._non_tag+a);break;case"addScript":this._Listener.addScript(this._non_tag+a);break;case"addCss":this._Listener.addCss(this._non_tag+a);break}}return true};WYMeditor.XhtmlParser.prototype.OpeningTag=function(a,b){switch(b){case WYMeditor.LEXER_ENTER:this._tag=this.normalizeTag(a);this._tag_attributes={};break;case WYMeditor.LEXER_SPECIAL:this._callOpenTagListener(this.normalizeTag(a));break;case WYMeditor.LEXER_EXIT:this._callOpenTagListener(this._tag,this._tag_attributes)}return true};WYMeditor.XhtmlParser.prototype.ClosingTag=function(a,b){this._callCloseTagListener(this.normalizeTag(a));return true};WYMeditor.XhtmlParser.prototype._callOpenTagListener=function(a,b){var b=b||{};this.autoCloseUnclosedBeforeNewOpening(a);if(this._Listener.isBlockTag(a)){this._Listener._tag_stack.push(a);this._Listener.fixNestingBeforeOpeningBlockTag(a,b);this._Listener.openBlockTag(a,b);this._increaseOpenTagCounter(a)}else{if(this._Listener.isInlineTag(a)){this._Listener.inlineTag(a,b)}else{this._Listener.openUnknownTag(a,b);this._increaseOpenTagCounter(a)}}this._Listener.last_tag=a;this._Listener.last_tag_opened=true;this._Listener.last_tag_attributes=b};WYMeditor.XhtmlParser.prototype._callCloseTagListener=function(a){if(this._decreaseOpenTagCounter(a)){this.autoCloseUnclosedBeforeTagClosing(a);if(this._Listener.isBlockTag(a)){var b=this._Listener._tag_stack.pop();if(b==false){return}else{if(b!=a){a=b}}this._Listener.closeBlockTag(a)}else{this._Listener.closeUnknownTag(a)}}else{this._Listener.closeUnopenedTag(a)}this._Listener.last_tag=a;this._Listener.last_tag_opened=false};WYMeditor.XhtmlParser.prototype._increaseOpenTagCounter=function(a){this._Listener._open_tags[a]=this._Listener._open_tags[a]||0;this._Listener._open_tags[a]++};WYMeditor.XhtmlParser.prototype._decreaseOpenTagCounter=function(a){if(this._Listener._open_tags[a]){this._Listener._open_tags[a]--;if(this._Listener._open_tags[a]==0){this._Listener._open_tags[a]=undefined}return true}return false};WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeNewOpening=function(a){this._autoCloseUnclosed(a,false)};WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeTagClosing=function(a){this._autoCloseUnclosed(a,true)};WYMeditor.XhtmlParser.prototype._autoCloseUnclosed=function(c,d){var d=d||false;if(this._Listener._open_tags){for(var a in this._Listener._open_tags){var b=this._Listener._open_tags[a];if(b>0&&this._Listener.shouldCloseTagAutomatically(a,c,d)){this._callCloseTagListener(a,true)}}}};WYMeditor.XhtmlParser.prototype.getTagReplacements=function(){return this._Listener.getTagReplacements()};WYMeditor.XhtmlParser.prototype.normalizeTag=function(a){a=a.replace(/^([\s<\/>]*)|([\s<\/>]*)$/gm,"").toLowerCase();var b=this._Listener.getTagReplacements();if(b[a]){return b[a]}return a};WYMeditor.XhtmlParser.prototype.TagAttributes=function(a,b){if(WYMeditor.LEXER_SPECIAL==b){this._current_attribute=a}return true};WYMeditor.XhtmlParser.prototype.DoubleQuotedAttribute=function(a,b){if(WYMeditor.LEXER_UNMATCHED==b){this._tag_attributes[this._current_attribute]=a}return true};WYMeditor.XhtmlParser.prototype.SingleQuotedAttribute=function(a,b){if(WYMeditor.LEXER_UNMATCHED==b){this._tag_attributes[this._current_attribute]=a}return true};WYMeditor.XhtmlParser.prototype.UnquotedAttribute=function(a,b){this._tag_attributes[this._current_attribute]=a.replace(/^=/,"");return true};WYMeditor.XhtmlSaxListener=function(){this.output="";this.helper=new WYMeditor.XmlHelper();this._open_tags={};this.validator=WYMeditor.XhtmlValidator;this._tag_stack=[];this.avoided_tags=[];this.entities={"&nbsp;":"&#160;","&iexcl;":"&#161;","&cent;":"&#162;","&pound;":"&#163;","&curren;":"&#164;","&yen;":"&#165;","&brvbar;":"&#166;","&sect;":"&#167;","&uml;":"&#168;","&copy;":"&#169;","&ordf;":"&#170;","&laquo;":"&#171;","&not;":"&#172;","&shy;":"&#173;","&reg;":"&#174;","&macr;":"&#175;","&deg;":"&#176;","&plusmn;":"&#177;","&sup2;":"&#178;","&sup3;":"&#179;","&acute;":"&#180;","&micro;":"&#181;","&para;":"&#182;","&middot;":"&#183;","&cedil;":"&#184;","&sup1;":"&#185;","&ordm;":"&#186;","&raquo;":"&#187;","&frac14;":"&#188;","&frac12;":"&#189;","&frac34;":"&#190;","&iquest;":"&#191;","&Agrave;":"&#192;","&Aacute;":"&#193;","&Acirc;":"&#194;","&Atilde;":"&#195;","&Auml;":"&#196;","&Aring;":"&#197;","&AElig;":"&#198;","&Ccedil;":"&#199;","&Egrave;":"&#200;","&Eacute;":"&#201;","&Ecirc;":"&#202;","&Euml;":"&#203;","&Igrave;":"&#204;","&Iacute;":"&#205;","&Icirc;":"&#206;","&Iuml;":"&#207;","&ETH;":"&#208;","&Ntilde;":"&#209;","&Ograve;":"&#210;","&Oacute;":"&#211;","&Ocirc;":"&#212;","&Otilde;":"&#213;","&Ouml;":"&#214;","&times;":"&#215;","&Oslash;":"&#216;","&Ugrave;":"&#217;","&Uacute;":"&#218;","&Ucirc;":"&#219;","&Uuml;":"&#220;","&Yacute;":"&#221;","&THORN;":"&#222;","&szlig;":"&#223;","&agrave;":"&#224;","&aacute;":"&#225;","&acirc;":"&#226;","&atilde;":"&#227;","&auml;":"&#228;","&aring;":"&#229;","&aelig;":"&#230;","&ccedil;":"&#231;","&egrave;":"&#232;","&eacute;":"&#233;","&ecirc;":"&#234;","&euml;":"&#235;","&igrave;":"&#236;","&iacute;":"&#237;","&icirc;":"&#238;","&iuml;":"&#239;","&eth;":"&#240;","&ntilde;":"&#241;","&ograve;":"&#242;","&oacute;":"&#243;","&ocirc;":"&#244;","&otilde;":"&#245;","&ouml;":"&#246;","&divide;":"&#247;","&oslash;":"&#248;","&ugrave;":"&#249;","&uacute;":"&#250;","&ucirc;":"&#251;","&uuml;":"&#252;","&yacute;":"&#253;","&thorn;":"&#254;","&yuml;":"&#255;","&OElig;":"&#338;","&oelig;":"&#339;","&Scaron;":"&#352;","&scaron;":"&#353;","&Yuml;":"&#376;","&fnof;":"&#402;","&circ;":"&#710;","&tilde;":"&#732;","&Alpha;":"&#913;","&Beta;":"&#914;","&Gamma;":"&#915;","&Delta;":"&#916;","&Epsilon;":"&#917;","&Zeta;":"&#918;","&Eta;":"&#919;","&Theta;":"&#920;","&Iota;":"&#921;","&Kappa;":"&#922;","&Lambda;":"&#923;","&Mu;":"&#924;","&Nu;":"&#925;","&Xi;":"&#926;","&Omicron;":"&#927;","&Pi;":"&#928;","&Rho;":"&#929;","&Sigma;":"&#931;","&Tau;":"&#932;","&Upsilon;":"&#933;","&Phi;":"&#934;","&Chi;":"&#935;","&Psi;":"&#936;","&Omega;":"&#937;","&alpha;":"&#945;","&beta;":"&#946;","&gamma;":"&#947;","&delta;":"&#948;","&epsilon;":"&#949;","&zeta;":"&#950;","&eta;":"&#951;","&theta;":"&#952;","&iota;":"&#953;","&kappa;":"&#954;","&lambda;":"&#955;","&mu;":"&#956;","&nu;":"&#957;","&xi;":"&#958;","&omicron;":"&#959;","&pi;":"&#960;","&rho;":"&#961;","&sigmaf;":"&#962;","&sigma;":"&#963;","&tau;":"&#964;","&upsilon;":"&#965;","&phi;":"&#966;","&chi;":"&#967;","&psi;":"&#968;","&omega;":"&#969;","&thetasym;":"&#977;","&upsih;":"&#978;","&piv;":"&#982;","&ensp;":"&#8194;","&emsp;":"&#8195;","&thinsp;":"&#8201;","&zwnj;":"&#8204;","&zwj;":"&#8205;","&lrm;":"&#8206;","&rlm;":"&#8207;","&ndash;":"&#8211;","&mdash;":"&#8212;","&lsquo;":"&#8216;","&rsquo;":"&#8217;","&sbquo;":"&#8218;","&ldquo;":"&#8220;","&rdquo;":"&#8221;","&bdquo;":"&#8222;","&dagger;":"&#8224;","&Dagger;":"&#8225;","&bull;":"&#8226;","&hellip;":"&#8230;","&permil;":"&#8240;","&prime;":"&#8242;","&Prime;":"&#8243;","&lsaquo;":"&#8249;","&rsaquo;":"&#8250;","&oline;":"&#8254;","&frasl;":"&#8260;","&euro;":"&#8364;","&image;":"&#8465;","&weierp;":"&#8472;","&real;":"&#8476;","&trade;":"&#8482;","&alefsym;":"&#8501;","&larr;":"&#8592;","&uarr;":"&#8593;","&rarr;":"&#8594;","&darr;":"&#8595;","&harr;":"&#8596;","&crarr;":"&#8629;","&lArr;":"&#8656;","&uArr;":"&#8657;","&rArr;":"&#8658;","&dArr;":"&#8659;","&hArr;":"&#8660;","&forall;":"&#8704;","&part;":"&#8706;","&exist;":"&#8707;","&empty;":"&#8709;","&nabla;":"&#8711;","&isin;":"&#8712;","&notin;":"&#8713;","&ni;":"&#8715;","&prod;":"&#8719;","&sum;":"&#8721;","&minus;":"&#8722;","&lowast;":"&#8727;","&radic;":"&#8730;","&prop;":"&#8733;","&infin;":"&#8734;","&ang;":"&#8736;","&and;":"&#8743;","&or;":"&#8744;","&cap;":"&#8745;","&cup;":"&#8746;","&int;":"&#8747;","&there4;":"&#8756;","&sim;":"&#8764;","&cong;":"&#8773;","&asymp;":"&#8776;","&ne;":"&#8800;","&equiv;":"&#8801;","&le;":"&#8804;","&ge;":"&#8805;","&sub;":"&#8834;","&sup;":"&#8835;","&nsub;":"&#8836;","&sube;":"&#8838;","&supe;":"&#8839;","&oplus;":"&#8853;","&otimes;":"&#8855;","&perp;":"&#8869;","&sdot;":"&#8901;","&lceil;":"&#8968;","&rceil;":"&#8969;","&lfloor;":"&#8970;","&rfloor;":"&#8971;","&lang;":"&#9001;","&rang;":"&#9002;","&loz;":"&#9674;","&spades;":"&#9824;","&clubs;":"&#9827;","&hearts;":"&#9829;","&diams;":"&#9830;"};this.block_tags=["a","abbr","acronym","address","area","b","base","bdo","big","blockquote","body","button","caption","cite","code","col","colgroup","dd","del","div","dfn","dl","dt","em","fieldset","form","head","h1","h2","h3","h4","h5","h6","html","i","ins","kbd","label","legend","li","map","noscript","object","ol","optgroup","option","p","param","pre","q","samp","script","select","small","span","strong","style","sub","sup","table","tbody","td","textarea","tfoot","th","thead","title","tr","tt","ul","var","extends"];this.inline_tags=["br","hr","img","input"];return this};WYMeditor.XhtmlSaxListener.prototype.shouldCloseTagAutomatically=function(a,c,b){var b=b||false;if(a=="td"){if((b&&c=="tr")||(!b&&c=="td")){return true}}if(a=="option"){if((b&&c=="select")||(!b&&c=="option")){return true}}return false};WYMeditor.XhtmlSaxListener.prototype.beforeParsing=function(a){this.output="";return a};WYMeditor.XhtmlSaxListener.prototype.afterParsing=function(a){a=this.replaceNamedEntities(a);a=this.joinRepeatedEntities(a);a=this.removeEmptyTags(a);a=this.removeBrInPre(a);return a};WYMeditor.XhtmlSaxListener.prototype.replaceNamedEntities=function(b){for(var a in this.entities){b=b.replace(new RegExp(a,"g"),this.entities[a])}return b};WYMeditor.XhtmlSaxListener.prototype.joinRepeatedEntities=function(b){var a="em|strong|sub|sup|acronym|pre|del|address";return b.replace(new RegExp("</("+a+")><\\1>",""),"").replace(new RegExp("(s*<("+a+")>s*){2}(.*)(s*</\\2>s*){2}",""),"<$2>$3<$2>")};WYMeditor.XhtmlSaxListener.prototype.removeEmptyTags=function(a){return a.replace(new RegExp("<("+this.block_tags.join("|").replace(/\|td/,"").replace(/\|th/,"")+")>(<br />|&#160;|&nbsp;|\\s)*</\\1>","g"),"")};WYMeditor.XhtmlSaxListener.prototype.removeBrInPre=function(c){var b=c.match(new RegExp("<pre[^>]*>(.*?)</pre>","gmi"));if(b){for(var a=0;a<b.length;a++){c=c.replace(b[a],b[a].replace(new RegExp("<br />","g"),String.fromCharCode(13,10)))}}return c};WYMeditor.XhtmlSaxListener.prototype.getResult=function(){return this.output};WYMeditor.XhtmlSaxListener.prototype.getTagReplacements=function(){return{b:"strong",i:"em"}};WYMeditor.XhtmlSaxListener.prototype.addContent=function(a){this.output+=a};WYMeditor.XhtmlSaxListener.prototype.addComment=function(a){if(this.remove_comments){this.output+=a}};WYMeditor.XhtmlSaxListener.prototype.addScript=function(a){if(!this.remove_scripts){this.output+=a}};WYMeditor.XhtmlSaxListener.prototype.addCss=function(a){if(!this.remove_embeded_styles){this.output+=a}};WYMeditor.XhtmlSaxListener.prototype.openBlockTag=function(a,b){this.output+=this.helper.tag(a,this.validator.getValidTagAttributes(a,b),true)};WYMeditor.XhtmlSaxListener.prototype.inlineTag=function(a,b){this.output+=this.helper.tag(a,this.validator.getValidTagAttributes(a,b))};WYMeditor.XhtmlSaxListener.prototype.openUnknownTag=function(a,b){};WYMeditor.XhtmlSaxListener.prototype.closeBlockTag=function(a){this.output=this.output.replace(/<br \/>$/,"")+this._getClosingTagContent("before",a)+"</"+a+">"+this._getClosingTagContent("after",a)};WYMeditor.XhtmlSaxListener.prototype.closeUnknownTag=function(a){};WYMeditor.XhtmlSaxListener.prototype.closeUnopenedTag=function(a){this.output+="</"+a+">"};WYMeditor.XhtmlSaxListener.prototype.avoidStylingTagsAndAttributes=function(){this.avoided_tags=["div","span"];this.validator.skiped_attributes=["style"];this.validator.skiped_attribute_values=["MsoNormal","main1"];this._avoiding_tags_implicitly=true};WYMeditor.XhtmlSaxListener.prototype.allowStylingTagsAndAttributes=function(){this.avoided_tags=[];this.validator.skiped_attributes=[];this.validator.skiped_attribute_values=[];this._avoiding_tags_implicitly=false};WYMeditor.XhtmlSaxListener.prototype.isBlockTag=function(a){return !WYMeditor.Helper.contains(this.avoided_tags,a)&&WYMeditor.Helper.contains(this.block_tags,a)};WYMeditor.XhtmlSaxListener.prototype.isInlineTag=function(a){return !WYMeditor.Helper.contains(this.avoided_tags,a)&&WYMeditor.Helper.contains(this.inline_tags,a)};WYMeditor.XhtmlSaxListener.prototype.insertContentAfterClosingTag=function(a,b){this._insertContentWhenClosingTag("after",a,b)};WYMeditor.XhtmlSaxListener.prototype.insertContentBeforeClosingTag=function(a,b){this._insertContentWhenClosingTag("before",a,b)};WYMeditor.XhtmlSaxListener.prototype.fixNestingBeforeOpeningBlockTag=function(a,b){if(a!="li"&&(a=="ul"||a=="ol")&&this.last_tag&&!this.last_tag_opened&&this.last_tag=="li"){this.output=this.output.replace(/<\/li>$/,"");this.insertContentAfterClosingTag(a,"</li>")}};WYMeditor.XhtmlSaxListener.prototype._insertContentWhenClosingTag=function(b,a,c){if(!this["_insert_"+b+"_closing"]){this["_insert_"+b+"_closing"]=[]}if(!this["_insert_"+b+"_closing"][a]){this["_insert_"+b+"_closing"][a]=[]}this["_insert_"+b+"_closing"][a].push(c)};WYMeditor.XhtmlSaxListener.prototype._getClosingTagContent=function(b,a){if(this["_insert_"+b+"_closing"]&&this["_insert_"+b+"_closing"][a]&&this["_insert_"+b+"_closing"][a].length>0){return this["_insert_"+b+"_closing"][a].pop()}return""};WYMeditor.WymCssLexer=function(b,a){var a=(typeof a=="undefined"?true:a);jQuery.extend(this,new WYMeditor.Lexer(b,(a?"Ignore":"WymCss")));this.mapHandler("WymCss","Ignore");if(a==true){this.addEntryPattern("/\\\x2a[<\\s]*WYMeditor[>\\s]*\\\x2a/","Ignore","WymCss");this.addExitPattern("/\\\x2a[</\\s]*WYMeditor[>\\s]*\\\x2a/","WymCss")}this.addSpecialPattern("[\\sa-z1-6]*\\\x2e[a-z-_0-9]+","WymCss","WymCssStyleDeclaration");this.addEntryPattern("/\\\x2a","WymCss","WymCssComment");this.addExitPattern("\\\x2a/","WymCssComment");this.addEntryPattern("\x7b","WymCss","WymCssStyle");this.addExitPattern("\x7d","WymCssStyle");this.addEntryPattern("/\\\x2a","WymCssStyle","WymCssFeedbackStyle");this.addExitPattern("\\\x2a/","WymCssFeedbackStyle");return this};WYMeditor.WymCssParser=function(){this._in_style=false;this._has_title=false;this.only_wym_blocks=true;this.css_settings={classesItems:[],editorStyles:[],dialogStyles:[]};return this};WYMeditor.WymCssParser.prototype.parse=function(a,b){var b=(typeof b=="undefined"?this.only_wym_blocks:b);this._Lexer=new WYMeditor.WymCssLexer(this,b);this._Lexer.parse(a)};WYMeditor.WymCssParser.prototype.Ignore=function(a,b){return true};WYMeditor.WymCssParser.prototype.WymCssComment=function(b,a){if(b.match(/end[a-z0-9\s]*wym[a-z0-9\s]*/mi)){return false}if(a==WYMeditor.LEXER_UNMATCHED){if(!this._in_style){this._has_title=true;this._current_item={title:WYMeditor.Helper.trim(b)}}else{if(this._current_item[this._current_element]){if(!this._current_item[this._current_element].expressions){this._current_item[this._current_element].expressions=[b]}else{this._current_item[this._current_element].expressions.push(b)}}}this._in_style=true}return true};WYMeditor.WymCssParser.prototype.WymCssStyle=function(b,a){if(a==WYMeditor.LEXER_UNMATCHED){b=WYMeditor.Helper.trim(b);if(b!=""){this._current_item[this._current_element].style=b}}else{if(a==WYMeditor.LEXER_EXIT){this._in_style=false;this._has_title=false;this.addStyleSetting(this._current_item)}}return true};WYMeditor.WymCssParser.prototype.WymCssFeedbackStyle=function(b,a){if(a==WYMeditor.LEXER_UNMATCHED){this._current_item[this._current_element].feedback_style=b.replace(/^([\s\/\*]*)|([\s\/\*]*)$/gm,"")}return true};WYMeditor.WymCssParser.prototype.WymCssStyleDeclaration=function(b){b=b.replace(/^([\s\.]*)|([\s\.*]*)$/gm,"");var a="";if(b.indexOf(".")>0){var c=b.split(".");this._current_element=c[1];var a=c[0]}else{this._current_element=b}if(!this._has_title){this._current_item={title:(!a?"":a.toUpperCase()+": ")+this._current_element};this._has_title=true}if(!this._current_item[this._current_element]){this._current_item[this._current_element]={name:this._current_element}}if(a){if(!this._current_item[this._current_element].tags){this._current_item[this._current_element].tags=[a]}else{this._current_item[this._current_element].tags.push(a)}}return true};WYMeditor.WymCssParser.prototype.addStyleSetting=function(a){for(var b in a){var c=a[b];if(typeof c=="object"&&b!="title"){this.css_settings.classesItems.push({name:WYMeditor.Helper.trim(c.name),title:a.title,expr:WYMeditor.Helper.trim((c.expressions||c.tags).join(", "))});if(c.feedback_style){this.css_settings.editorStyles.push({name:"."+WYMeditor.Helper.trim(c.name),css:c.feedback_style})}if(c.style){this.css_settings.dialogStyles.push({name:"."+WYMeditor.Helper.trim(c.name),css:c.style})}}}};jQuery.fn.isPhantomNode=function(){if(this[0].nodeType==3){return !(/[^\t\n\r ]/.test(this[0].data))}return false};WYMeditor.isPhantomNode=function(a){if(a.nodeType==3){return !(/[^\t\n\r ]/.test(a.data))}return false};WYMeditor.isPhantomString=function(a){return !(/[^\t\n\r ]/.test(a))};jQuery.fn.parentsOrSelf=function(b){var a=this;if(a[0].nodeType==3){a=a.parents().slice(0,1)}if(a.filter(b).size()==1){return a}else{return a.parents(b).slice(0,1)}};WYMeditor.Helper={replaceAll:function(d,a,c){var b=new RegExp(a,"g");return(d.replace(b,c))},insertAt:function(b,a,c){return(b.substr(0,c)+a+b.substring(c))},trim:function(a){return a.replace(/^(\s*)|(\s*)$/gm,"")},contains:function(a,c){for(var b=0;b<a.length;b++){if(a[b]===c){return true}}return false},indexOf:function(a,d){var b=-1;for(var c=0;c<a.length;c++){if(a[c]==d){b=c;break}}return(b)},findByName:function(a,b){for(var c=0;c<a.length;c++){var d=a[c];if(d.name==b){return(d)}}return(null)}};WYMeditor.WymClassExplorer=function(a){this._wym=a;this._class="className";this._newLine="\r\n"};WYMeditor.WymClassExplorer.prototype.initIframe=function(iframe){this._iframe=iframe;this._doc=iframe.contentWindow.document;var styles=this._doc.styleSheets[0];var aCss=eval(this._options.editorStyles);this.addCssRules(this._doc,aCss);this._doc.title=this._wym._index;jQuery("html",this._doc).attr("dir",this._options.direction);jQuery(this._doc.body).html(this._wym._html);var wym=this;this._doc.body.onfocus=function(){wym._doc.designMode="on";wym._doc=iframe.contentWindow.document};this._doc.onbeforedeactivate=function(){wym.saveCaret()};this._doc.onkeyup=function(){wym.saveCaret();wym.keyup()};this._doc.onclick=function(){wym.saveCaret()};this._doc.body.onbeforepaste=function(){wym._iframe.contentWindow.event.returnValue=false};this._doc.body.onpaste=function(){wym._iframe.contentWindow.event.returnValue=false;wym.paste(window.clipboardData.getData("Text"))};if(this._initialized){if(jQuery.isFunction(this._options.preBind)){this._options.preBind(this)}this._wym.bindEvents();if(jQuery.isFunction(this._options.postInit)){this._options.postInit(this)}this.listen()}this._initialized=true;this._doc.designMode="on";try{this._doc=iframe.contentWindow.document}catch(e){}};WYMeditor.WymClassExplorer.prototype._exec=function(c,d){switch(c){case WYMeditor.INDENT:case WYMeditor.OUTDENT:var a=this.findUp(this.container(),WYMeditor.LI);if(a){var b=a.parentNode.parentNode;if(a.parentNode.childNodes.length>1||b.tagName.toLowerCase()==WYMeditor.OL||b.tagName.toLowerCase()==WYMeditor.UL){this._doc.execCommand(c)}}break;default:if(d){this._doc.execCommand(c,false,d)}else{this._doc.execCommand(c)}break}};WYMeditor.WymClassExplorer.prototype.selected=function(){var a=this._iframe.contentWindow.document.caretPos;if(a!=null){if(a.parentElement!=undefined){return(a.parentElement())}}};WYMeditor.WymClassExplorer.prototype.saveCaret=function(){this._doc.caretPos=this._doc.selection.createRange()};WYMeditor.WymClassExplorer.prototype.addCssRule=function(a,b){a.addRule(b.name,b.css)};WYMeditor.WymClassExplorer.prototype.insert=function(b){var a=this._doc.selection.createRange();if(jQuery(a.parentElement()).parents(this._options.iframeBodySelector).is("*")){try{a.pasteHTML(b)}catch(c){}}else{this.paste(b)}};WYMeditor.WymClassExplorer.prototype.wrap=function(d,b){var a=this._doc.selection.createRange();if(jQuery(a.parentElement()).parents(this._options.iframeBodySelector).is("*")){try{a.pasteHTML(d+a.text+b)}catch(c){}}};WYMeditor.WymClassExplorer.prototype.unwrap=function(){var a=this._doc.selection.createRange();if(jQuery(a.parentElement()).parents(this._options.iframeBodySelector).is("*")){try{var c=a.text;this._exec("Cut");a.pasteHTML(c)}catch(b){}}};WYMeditor.WymClassExplorer.prototype.keyup=function(){this._selected_image=null};WYMeditor.WymClassExplorer.prototype.setFocusToNode=function(c,b){var a=this._doc.selection.createRange();b=b?true:false;a.moveToElementText(c);a.collapse(b);a.select();c.focus()};WYMeditor.WymClassMozilla=function(a){this._wym=a;this._class="class";this._newLine="\n"};WYMeditor.WymClassMozilla.prototype.initIframe=function(iframe){var wym=this;this._iframe=iframe;this._doc=iframe.contentDocument;var styles=this._doc.styleSheets[0];var aCss=eval(this._options.editorStyles);this.addCssRules(this._doc,aCss);this._doc.title=this._wym._index;jQuery("html",this._doc).attr("dir",this._options.direction);this.html(this._wym._html);this.enableDesignMode();if(jQuery.isFunction(this._options.preBind)){this._options.preBind(this)}this._wym.bindEvents();jQuery(this._doc).bind("keydown",this.keydown);jQuery(this._doc).bind("keyup",this.keyup);jQuery(this._doc).bind("focus",function(){wym.enableDesignMode.call(wym)});if(jQuery.isFunction(this._options.postInit)){this._options.postInit(this)}this.listen()};WYMeditor.WymClassMozilla.prototype.html=function(a){if(typeof a==="string"){try{this._doc.designMode="off"}catch(b){}a=a.replace(/<em(\b[^>]*)>/gi,"<i$1>").replace(/<\/em>/gi,"</i>").replace(/<strong(\b[^>]*)>/gi,"<b$1>").replace(/<\/strong>/gi,"</b>");jQuery(this._doc.body).html(a);this.enableDesignMode()}else{return(jQuery(this._doc.body).html())}};WYMeditor.WymClassMozilla.prototype._exec=function(e,g){if(!this.selected()){return(false)}switch(e){case WYMeditor.INDENT:case WYMeditor.OUTDENT:var f=this.selected();var d=this._iframe.contentWindow.getSelection();var b=d.anchorNode;if(b.nodeName=="#text"){b=b.parentNode}f=this.findUp(f,WYMeditor.BLOCKS);b=this.findUp(b,WYMeditor.BLOCKS);if(f&&f==b&&f.tagName.toLowerCase()==WYMeditor.LI){var c=f.parentNode.parentNode;if(f.parentNode.childNodes.length>1||c.tagName.toLowerCase()==WYMeditor.OL||c.tagName.toLowerCase()==WYMeditor.UL){this._doc.execCommand(e,"",null)}}break;default:if(g){this._doc.execCommand(e,"",g)}else{this._doc.execCommand(e,"",null)}}var a=this.selected();if(a.tagName.toLowerCase()==WYMeditor.BODY){this._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}};WYMeditor.WymClassMozilla.prototype.selected=function(){var b=this._iframe.contentWindow.getSelection();var a=b.focusNode;if(a){if(a.nodeName=="#text"){return(a.parentNode)}else{return(a)}}else{return(null)}};WYMeditor.WymClassMozilla.prototype.addCssRule=function(a,b){a.insertRule(b.name+" {"+b.css+"}",a.cssRules.length)};WYMeditor.WymClassMozilla.prototype.keydown=function(b){var c=WYMeditor.INSTANCES[this.title];var a=null;if(b.ctrlKey){if(b.keyCode==66){c._exec(WYMeditor.BOLD);return false}if(b.keyCode==73){c._exec(WYMeditor.ITALIC);return false}}else{if(b.keyCode==13){if(!b.shiftKey){a=c.selected();if(a&&a.tagName.toLowerCase()==WYMeditor.PRE){b.preventDefault();c.insert("<p></p>")}}}}};WYMeditor.WymClassMozilla.prototype.keyup=function(b){var d=WYMeditor.INSTANCES[this.title];d._selected_image=null;var a=null;if(b.keyCode==13&&!b.shiftKey){jQuery(d._doc.body).children(WYMeditor.BR).remove()}if(b.keyCode!=8&&b.keyCode!=17&&b.keyCode!=46&&b.keyCode!=224&&!b.metaKey&&!b.ctrlKey){a=d.selected();var c=a.tagName.toLowerCase();if(c=="strong"||c=="b"||c=="em"||c=="i"||c=="sub"||c=="sup"||c=="a"){c=a.parentNode.tagName.toLowerCase()}if(c==WYMeditor.BODY){d._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}}};WYMeditor.WymClassMozilla.prototype.enableDesignMode=function(){if(this._doc.designMode=="off"){try{this._doc.designMode="on";this._doc.execCommand("styleWithCSS","",false)}catch(a){}}};WYMeditor.WymClassMozilla.prototype.openBlockTag=function(a,c){var c=this.validator.getValidTagAttributes(a,c);if(a=="span"&&c.style){var b=this.getTagForStyle(c.style);if(b){this._tag_stack.pop();var a=b;this._tag_stack.push(b);c.style=""}else{return}}this.output+=this.helper.tag(a,c,true)};WYMeditor.WymClassMozilla.prototype.getTagForStyle=function(a){if(/bold/.test(a)){return"strong"}if(/italic/.test(a)){return"em"}if(/sub/.test(a)){return"sub"}if(/super/.test(a)){return"sup"}return false};WYMeditor.WymClassOpera=function(a){this._wym=a;this._class="class";this._newLine="\r\n"};WYMeditor.WymClassOpera.prototype.initIframe=function(iframe){this._iframe=iframe;this._doc=iframe.contentWindow.document;var styles=this._doc.styleSheets[0];var aCss=eval(this._options.editorStyles);this.addCssRules(this._doc,aCss);this._doc.title=this._wym._index;jQuery("html",this._doc).attr("dir",this._options.direction);this._doc.designMode="on";this.html(this._wym._html);if(jQuery.isFunction(this._options.preBind)){this._options.preBind(this)}this._wym.bindEvents();jQuery(this._doc).bind("keydown",this.keydown);jQuery(this._doc).bind("keyup",this.keyup);if(jQuery.isFunction(this._options.postInit)){this._options.postInit(this)}this.listen()};WYMeditor.WymClassOpera.prototype._exec=function(a,b){if(b){this._doc.execCommand(a,false,b)}else{this._doc.execCommand(a)}};WYMeditor.WymClassOpera.prototype.selected=function(){var b=this._iframe.contentWindow.getSelection();var a=b.focusNode;if(a){if(a.nodeName=="#text"){return(a.parentNode)}else{return(a)}}else{return(null)}};WYMeditor.WymClassOpera.prototype.addCssRule=function(a,b){a.insertRule(b.name+" {"+b.css+"}",a.cssRules.length)};WYMeditor.WymClassOpera.prototype.keydown=function(a){var c=WYMeditor.INSTANCES[this.title];var b=c._iframe.contentWindow.getSelection();startNode=b.getRangeAt(0).startContainer;if(!jQuery(startNode).parentsOrSelf(WYMeditor.MAIN_CONTAINERS.join(","))[0]&&!jQuery(startNode).parentsOrSelf("li")&&a.keyCode!=WYMeditor.KEY.ENTER&&a.keyCode!=WYMeditor.KEY.LEFT&&a.keyCode!=WYMeditor.KEY.UP&&a.keyCode!=WYMeditor.KEY.RIGHT&&a.keyCode!=WYMeditor.KEY.DOWN&&a.keyCode!=WYMeditor.KEY.BACKSPACE&&a.keyCode!=WYMeditor.KEY.DELETE){c._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}};WYMeditor.WymClassOpera.prototype.keyup=function(a){var b=WYMeditor.INSTANCES[this.title];b._selected_image=null};WYMeditor.WymClassSafari=function(a){this._wym=a;this._class="class";this._newLine="\n"};WYMeditor.WymClassSafari.prototype.initIframe=function(iframe){this._iframe=iframe;this._doc=iframe.contentDocument;var styles=this._doc.styleSheets[0];var aCss=eval(this._options.editorStyles);this.addCssRules(this._doc,aCss);this._doc.title=this._wym._index;jQuery("html",this._doc).attr("dir",this._options.direction);this._doc.designMode="on";this.html(this._wym._html);if(jQuery.isFunction(this._options.preBind)){this._options.preBind(this)}this._wym.bindEvents();jQuery(this._doc).bind("keydown",this.keydown);jQuery(this._doc).bind("keyup",this.keyup);if(jQuery.isFunction(this._options.postInit)){this._options.postInit(this)}this.listen()};WYMeditor.WymClassSafari.prototype._exec=function(e,g){if(!this.selected()){return(false)}switch(e){case WYMeditor.INDENT:case WYMeditor.OUTDENT:var f=this.selected();var d=this._iframe.contentWindow.getSelection();var b=d.anchorNode;if(b.nodeName=="#text"){b=b.parentNode}f=this.findUp(f,WYMeditor.BLOCKS);b=this.findUp(b,WYMeditor.BLOCKS);if(f&&f==b&&f.tagName.toLowerCase()==WYMeditor.LI){var c=f.parentNode.parentNode;if(f.parentNode.childNodes.length>1||c.tagName.toLowerCase()==WYMeditor.OL||c.tagName.toLowerCase()==WYMeditor.UL){this._doc.execCommand(e,"",null)}}break;case WYMeditor.INSERT_ORDEREDLIST:case WYMeditor.INSERT_UNORDEREDLIST:this._doc.execCommand(e,"",null);var f=this.selected();var a=this.findUp(f,WYMeditor.MAIN_CONTAINERS);if(a){jQuery(a).replaceWith(jQuery(a).html())}break;default:if(g){this._doc.execCommand(e,"",g)}else{this._doc.execCommand(e,"",null)}}var a=this.selected();if(a&&a.tagName.toLowerCase()==WYMeditor.BODY){this._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}};WYMeditor.WymClassSafari.prototype.selected=function(){var b=this._iframe.contentWindow.getSelection();var a=b.focusNode;if(a){if(a.nodeName=="#text"){return(a.parentNode)}else{return(a)}}else{return(null)}};WYMeditor.WymClassSafari.prototype.addCssRule=function(a,b){a.insertRule(b.name+" {"+b.css+"}",a.cssRules.length)};WYMeditor.WymClassSafari.prototype.keydown=function(a){var b=WYMeditor.INSTANCES[this.title];if(a.ctrlKey){if(a.keyCode==66){b._exec(WYMeditor.BOLD);return false}if(a.keyCode==73){b._exec(WYMeditor.ITALIC);return false}}};WYMeditor.WymClassSafari.prototype.keyup=function(b){var d=WYMeditor.INSTANCES[this.title];d._selected_image=null;var a=null;if(b.keyCode==13&&!b.shiftKey){jQuery(d._doc.body).children(WYMeditor.BR).remove();a=d.selected();if(a&&a.tagName.toLowerCase()==WYMeditor.PRE){d._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}}if(b.keyCode==13&&b.shiftKey){d._exec("InsertLineBreak")}if(b.keyCode!=8&&b.keyCode!=17&&b.keyCode!=46&&b.keyCode!=224&&!b.metaKey&&!b.ctrlKey){a=d.selected();var c=a.tagName.toLowerCase();if(c=="strong"||c=="b"||c=="em"||c=="i"||c=="sub"||c=="sup"||c=="a"||c=="span"){c=a.parentNode.tagName.toLowerCase()}if(c==WYMeditor.BODY||c==WYMeditor.DIV){d._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}}};WYMeditor.WymClassSafari.prototype.openBlockTag=function(a,c){var c=this.validator.getValidTagAttributes(a,c);if(a=="span"&&c.style){var b=this.getTagForStyle(c.style);if(b){this._tag_stack.pop();var a=b;this._tag_stack.push(b);c.style="";if(typeof c["class"]=="string"){c["class"]=c["class"].replace(/apple-style-span/gi,"")}}else{return}}this.output+=this.helper.tag(a,c,true)};WYMeditor.WymClassSafari.prototype.getTagForStyle=function(a){if(/bold/.test(a)){return"strong"}if(/italic/.test(a)){return"em"}if(/sub/.test(a)){return"sub"}if(/super/.test(a)){return"sup"}return false}; \ No newline at end of file
diff --git a/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.pack.js b/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.pack.js
new file mode 100644
index 0000000..17b3268
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/jquery.wymeditor.pack.js
@@ -0,0 +1 @@
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('if(!f)l f={};(j(){if(!2l.4O||!4O.rD){l aj=["rC","rB","rA","rz","9w","ry","3z","rx","rw","rv","ru","rt","58","rs","cK","rr"];f.4O={};1h(l i=0;i<aj.Y;++i)f.4O[aj[i]]=j(){}}V f.4O=2l.4O})();D.53(f,{rq:"0.5-rp",2w:[],7U:[],5h:[],7Z:"F",3N:"{ro}",a2:"rn",dE:"{rm}",9s:"{rl}",9r:"{rk}",eX:"dw/",7E:"3a.1b",dm:"3a.js",eW:"3H/",a1:"{rj}",eV:"1K/3R/",9q:"{ri}",9t:"{rh}",a0:"{rg}",9Z:"{rf}",9R:"{rd}",9T:"{rc}",81:"{rb}",9S:"{ra}",9X:"{r9}",9O:"{r8}",80:"{r7}",9P:"{r6}",9Y:"{r5}",9I:"{r4}",9L:"{r3}",9K:"{r2}",9J:"{r1}",82:"{r0}",9W:"{qZ}",9V:"{qY}",9p:"{qX}",9o:"{qW}",dO:"3Q",3t:"1j",an:"N",P:"p",9F:"h1",9E:"h2",9D:"h3",9C:"h4",9B:"h5",9A:"h6",5q:"41",6b:"2J",A:"a",88:"br",du:"6Q",de:"3j",6d:"4F",5l:"5I",6o:"2I",6p:"ol",6r:"li",dP:"I",9c:"1P",7C:"1U",61:"M",7B:"5b",64:"aa",7R:"a9",7Q:"a7",7P:"a5",8a:"eU",89:"eT",9d:"eM",9b:"eL",dU:"eK",7L:"qV",dT:"eJ",6t:"ai",6s:"ah",dS:"eI",4q:"qU",7O:"af",qT:"ag",ar:"eP",as:"eQ",6n:1f 6c("p","h1","h2","h3","h4","h5","h6","41","2J"),5t:1f 6c("6L","2J","N","dl","2U","1M","h1","h2","h3","h4","h5","h6","hr","8E","ol","p","41","3j","2I","dd","dt","li","8D","4F","8C","5I","8B","6P"),3T:{av:8,aA:13,qS:35,qR:36,az:37,ay:38,ax:39,aw:40,qQ:1f 6c(37,38,39,40),au:46},qP:{qO:1,qN:2,qM:3},1g:j(5C,1Q){c.3f=f.2w.1X(c)-1;c.7T=5C;c.B=1Q;c.4R=D(5C).1H();if(c.B.O)c.4R=c.B.O;c.B.4L=c.B.4L||c.dz();c.B.5i=c.B.5i||c.B.4L+f.eX+c.B.3a+\'/\';c.B.7N=c.B.7N||c.dy();c.B.9x=c.B.9x||c.B.4L+f.eW;c.B.83=c.B.83||c.B.4L+f.eV;c.B.7M=c.B.7M||c.dx();c.56()}});D.fn.5k=j(1Q){1Q=D.53({O:"",4L:J,5i:J,7N:J,83:J,7M:J,1D:J,3p:J,3a:"3R",dn:R,7F:R,3H:"en",4S:"92",1u:"<N I=\'eH\'>"+"<N I=\'qL\'>"+f.9Z+"</N>"+"<N I=\'qK\'></N>"+"<N I=\'qJ\'>"+f.9Y+f.9X+"</N>"+"<N I=\'qI\'>"+f.82+f.9W+f.9V+"</N>"+"<N I=\'qH\'>"+f.a0+"</N>"+"</N>",ec:"<a I=\'qG\' "+"1P=\'91://ex.5k.ew/\'>f</a>",4n:"<N I=\'ae 5o\'>"+"<1K "+"1U=\'"+f.a1+"qF.O\' "+"4K=\'c.1B.qE.f.2w["+f.3N+"].5w(c)\'"+"></1K>"+"</N>",3g:[],eb:"<N I=\'ad 5o\'>"+"<h2>{qD}</h2>"+"<2I>"+f.9R+"</2I>"+"</N>",e5:"<li I=\'"+f.9S+"\'><a 1P=\'#\' F=\'"+f.9T+"\' M=\'"+f.81+"\'>"+f.81+"</a></li>",e6:[{\'F\':\'eU\',\'M\':\'qC\',\'1b\':\'qB\'},{\'F\':\'eT\',\'M\':\'qA\',\'1b\':\'qz\'},{\'F\':\'eS\',\'M\':\'eS\',\'1b\':\'qy\'},{\'F\':\'eR\',\'M\':\'eR\',\'1b\':\'qx\'},{\'F\':\'eQ\',\'M\':\'qw\',\'1b\':\'qv\'},{\'F\':\'eP\',\'M\':\'qu\',\'1b\':\'qt\'},{\'F\':\'ai\',\'M\':\'ai\',\'1b\':\'qs\'},{\'F\':\'ah\',\'M\':\'ah\',\'1b\':\'qr\'},{\'F\':\'eO\',\'M\':\'eO\',\'1b\':\'qq\'},{\'F\':\'eN\',\'M\':\'eN\',\'1b\':\'qp\'},{\'F\':\'eM\',\'M\':\'aa\',\'1b\':\'qo\'},{\'F\':\'ag\',\'M\':\'ag\',\'1b\':\'qn\'},{\'F\':\'eL\',\'M\':\'a9\',\'1b\':\'qm\'},{\'F\':\'eK\',\'M\':\'a7\',\'1b\':\'ql\'},{\'F\':\'eJ\',\'M\':\'a5\',\'1b\':\'qk\'},{\'F\':\'eI\',\'M\':\'82\',\'1b\':\'qj\'},{\'F\':\'af\',\'M\':\'af\',\'1b\':\'qi\'}],ea:"<N I=\'ac 5o\'>"+"<h2>{qh}</h2>"+"<2I>"+f.9I+"</2I>"+"</N>",e2:"<li I=\'"+f.9J+"\'>"+"<a 1P=\'#\' F=\'"+f.9L+"\'>"+f.9K+"</a></li>",e3:[{\'F\':\'P\',\'M\':\'qg\',\'1b\':\'qf\'},{\'F\':\'9F\',\'M\':\'qe\',\'1b\':\'qd\'},{\'F\':\'9E\',\'M\':\'qc\',\'1b\':\'qb\'},{\'F\':\'9D\',\'M\':\'qa\',\'1b\':\'q9\'},{\'F\':\'9C\',\'M\':\'q8\',\'1b\':\'q7\'},{\'F\':\'9B\',\'M\':\'q6\',\'1b\':\'q5\'},{\'F\':\'9A\',\'M\':\'q4\',\'1b\':\'q3\'},{\'F\':\'5q\',\'M\':\'q2\',\'1b\':\'q1\'},{\'F\':\'6b\',\'M\':\'q0\',\'1b\':\'pZ\'},{\'F\':\'5l\',\'M\':\'pY\',\'1b\':\'pX\'}],e9:"<N I=\'ab 5o\'>"+"<h2>{pW}</h2><2I>"+f.9O+"</2I></N>",e4:"<li I=\'pV"+f.80+"\'><a 1P=\'#\' F=\'"+f.80+"\'>"+f.9P+"</a></li>",3W:[],e7:"<N I=\'eF 5o\'>"+"<h2>{pU}</h2>"+"</N>",e8:"<N I=\'eG 5o\'>"+"<h2>{pT}</h2>"+"<4G I=\'eE\'></4G>"+"</N>",pS:".eH",pR:".ad",pQ:" 2I",pP:".ac",pO:".ab",9l:".eG",pN:".ae 1K",6w:".ae",dN:".eF",e0:".ad a",dZ:".ac a",dY:".ab a",9v:".eE",9e:".es",7D:".ep",60:".a8",7A:".eo",d6:".ee",dg:".eh",df:".eg",dc:".ej",d9:".ei",5Z:".6h",d2:".6g",d3:"",dk:".6i",di:".et",63:".er",dh:".ek",d7:".ef",d4:".ed",dX:".pM",dW:"4e",7S:"eD=no,eC=no,eB=no,eA=no"+",4J=ez,7h=ey,3o=0,2k=0",dR:"eD=no,eC=no,eB=no,eA=no"+",pL=pK,4J=ez,7h=ey,3o=0,2k=0",1I:"<!pJ O pI \'-//pH//ev pG 1.0 pF//pE\'"+" \'91://ex.pD.ew/pC/eu/ev/eu-pB.pA\'>"+"<O 3z=\'"+f.9t+"\'><2T>"+"<2u 7w=\'3p\' 1e=\'X/1b\' 8Z=\'7f\'"+" 1P=\'"+f.9s+"\' />"+"<M>"+f.9p+"</M>"+"<2h 1e=\'X/90\'"+" 1U=\'"+f.9q+"\'></2h>"+"<2h 1e=\'X/90\'"+" 1U=\'"+f.9r+"\'></2h>"+"</2T>"+f.9o+"</O>",dK:"<1j I=\'6f et\'"+" 4K=\'f.5g("+f.3N+")\'"+">"+"<1M>"+"<2U>"+"<1y 1e=\'5V\' I=\'6i\' Q=\'"+f.64+"\' />"+"<3k>{aa}</3k>"+"<N I=\'1O\'>"+"<1p>{eq}</1p>"+"<1y 1e=\'X\' I=\'es\' Q=\'\' 2G=\'40\' />"+"</N>"+"<N I=\'1O\'>"+"<1p>{el}</1p>"+"<1y 1e=\'X\' I=\'a8\' Q=\'\' 2G=\'40\' />"+"</N>"+"<N I=\'1O 1O-a6\'>"+"<1y I=\'6h\' 1e=\'2t\'"+" Q=\'{86}\' />"+"<1y I=\'6g\' 1e=\'2t\'"+"Q=\'{85}\' />"+"</N>"+"</2U>"+"</1M>"+"</1j>",dJ:"<1j I=\'6f er\'"+" 4K=\'f.5g("+f.3N+")\'"+">"+"<1M>"+"<2U>"+"<1y 1e=\'5V\' I=\'6i\' Q=\'"+f.7R+"\' />"+"<3k>{a9}</3k>"+"<N I=\'1O\'>"+"<1p>{eq}</1p>"+"<1y 1e=\'X\' I=\'ep\' Q=\'\' 2G=\'40\' />"+"</N>"+"<N I=\'1O\'>"+"<1p>{pz}</1p>"+"<1y 1e=\'X\' I=\'eo\' Q=\'\' 2G=\'40\' />"+"</N>"+"<N I=\'1O\'>"+"<1p>{el}</1p>"+"<1y 1e=\'X\' I=\'a8\' Q=\'\' 2G=\'40\' />"+"</N>"+"<N I=\'1O 1O-a6\'>"+"<1y I=\'6h\' 1e=\'2t\'"+" Q=\'{86}\' />"+"<1y I=\'6g\' 1e=\'2t\'"+"Q=\'{85}\' />"+"</N>"+"</2U>"+"</1M>"+"</1j>",dI:"<1j I=\'6f ek\'"+" 4K=\'f.5g("+f.3N+")\'"+">"+"<1M>"+"<2U>"+"<1y 1e=\'5V\' I=\'6i\' Q=\'"+f.7Q+"\' />"+"<3k>{a7}</3k>"+"<N I=\'1O\'>"+"<1p>{py}</1p>"+"<1y 1e=\'X\' I=\'ej\' Q=\'\' 2G=\'40\' />"+"</N>"+"<N I=\'1O\'>"+"<1p>{px}</1p>"+"<1y 1e=\'X\' I=\'ei\' Q=\'\' 2G=\'40\' />"+"</N>"+"<N I=\'1O\'>"+"<1p>{pw}</1p>"+"<1y 1e=\'X\' I=\'eh\' Q=\'3\' 2G=\'3\' />"+"</N>"+"<N I=\'1O\'>"+"<1p>{pv}</1p>"+"<1y 1e=\'X\' I=\'eg\' Q=\'2\' 2G=\'3\' />"+"</N>"+"<N I=\'1O 1O-a6\'>"+"<1y I=\'6h\' 1e=\'2t\'"+" Q=\'{86}\' />"+"<1y I=\'6g\' 1e=\'2t\'"+"Q=\'{85}\' />"+"</N>"+"</2U>"+"</1M>"+"</1j>",dH:"<1j I=\'6f ef\'"+" 4K=\'f.5g("+f.3N+")\'"+">"+"<1M>"+"<1y 1e=\'5V\' I=\'6i\' Q=\'"+f.7P+"\' />"+"<2U>"+"<3k>{a5}</3k>"+"<N I=\'1O\'>"+"<4G I=\'ee\' 7d=\'10\' 7e=\'50\'></4G>"+"</N>"+"<N I=\'1O\'>"+"<1y I=\'6h\' 1e=\'2t\'"+" Q=\'{86}\' />"+"<1y I=\'6g\' 1e=\'2t\'"+"Q=\'{85}\' />"+"</N>"+"</2U>"+"</1M>"+"</1j>",dG:"<1j I=\'6f ed\'"+" 4K=\'f.5g("+f.3N+")\'"+"></1j>",4A:[],69:"{",68:"}",a4:1a,3y:1a,3w:1a,9f:1a,97:1a},1Q);o c.dq(j(){1f f.1g(D(c),1Q)})};D.53({pu:j(i){o(f.2w[i])}});f.1g.u.56=j(){if(D.84.pt){l 4o=1f f.2D(c)}V if(D.84.ps){l 4o=1f f.2C(c)}V if(D.84.pr){l 4o=1f f.3S(c)}V if(D.84.pq){l 4o=1f f.3d(c)}if(4o){if(D.2A(c.B.a4))c.B.a4(c);l a3=1f f.1l();D.53(a3,4o);c.3Z=1f f.1t(a3);if(c.B.1D||c.B.3p){c.dv()}c.4P=1f f.2W();1h(l 6U in 4o){c[6U]=4o[6U]}c.2p=D(c.7T).e1().5G(c.B.1u).5U().do(\'pp\'+c.3f);if(D.2A(D.fn.4z)){D.4z(c.2p.5W(0),f.a2,c.3f);D.4z(c.7T.5W(0),f.a2,c.3f)}l h=f.1L;l 4n=c.B.4n;4n=h.1k(4n,f.3N,c.3f);4n=h.1k(4n,f.a1,c.B.83);l 1u=D(c.2p).O();1u=h.1k(1u,f.a0,c.B.ec);1u=h.1k(1u,f.9Z,c.B.eb);1u=h.1k(1u,f.9Y,c.B.ea);1u=h.1k(1u,f.9X,c.B.e9);1u=h.1k(1u,f.82,c.B.e8);1u=h.1k(1u,f.9W,4n);1u=h.1k(1u,f.9V,c.B.e7);l 9U=2B(c.B.e6);l 9Q="";1h(l i=0;i<9U.Y;i++){l 5n=9U[i];if(5n.F&&5n.M)l 4m=c.B.e5;l 4m=h.1k(4m,f.9T,5n.F);4m=h.1k(4m,f.81,c.B.69+5n.M+c.B.68);4m=h.1k(4m,f.9S,5n.1b);9Q+=4m}1u=h.1k(1u,f.9R,9Q);l 6e=2B(c.B.3W);l 9N="";1h(l i=0;i<6e.Y;i++){l 4k=6e[i];if(4k.F&&4k.M)l 4j=c.B.e4;4j=h.1k(4j,f.80,4k.F);4j=h.1k(4j,f.9P,4k.M);9N+=4j}1u=h.1k(1u,f.9O,9N);l 9M=2B(c.B.e3);l 9H="";1h(l i=0;i<9M.Y;i++){l 5m=9M[i];if(5m.F&&5m.M)l 4l=c.B.e2;4l=h.1k(4l,f.9L,5m.F);4l=h.1k(4l,f.9K,c.B.69+5m.M+c.B.68);4l=h.1k(4l,f.9J,5m.1b);9H+=4l}1u=h.1k(1u,f.9I,9H);1u=c.9n(1u);D(c.2p).O(1u);D(c.2p).4g(c.B.9l).e1();c.7F()}};f.1g.u.5v=j(){l E=c;D(c.2p).4g(c.B.e0).4e(j(){E.1E.1B.4W();E.dV(D(c).1v(f.7Z));o(J)});D(c.2p).4g(c.B.dZ).4e(j(){E.L(D(c).1v(f.7Z));o(J)});D(c.2p).4g(c.B.9v).2x(j(){D(E.H.1j).O(D(c).1H())}).4W(j(){D(c).6a(\'9u\')}).po(j(){D(c).6a(\'9u\')});D(c.2p).4g(c.B.dY).4e(j(){l 6e=2B(E.B.3W);l 9G=D(c).1v(f.7Z);l 4k=f.1L.aL(6e,9G);if(4k){l 4y=4k.aT;E.6a(9G,4y)}E.1E.1B.4W();o(J)});D(c.B.dX).3x(c.B.dW,j(){E.67()})};f.1g.u.pn=j(){o(c.H!=1a)};f.1g.u.ct=j(){o(c.2p)};f.1g.u.O=j(O){if(1q O===\'3Q\')D(c.H.1j).O(O);V o(D(c.H.1j).O())};f.1g.u.1s=j(){o c.3Z.3X(c.O())};f.1g.u.dV=j(1F){3e(1F){1i f.9d:l L=c.L();if(L||c.2j)c.4M(f.64);1n;1i f.9b:c.4M(f.7R);1n;1i f.dU:c.4M(f.7Q);1n;1i f.dT:c.4M(f.7P);1n;1i f.dS:c.67();c.dC();1n;1i f.7O:c.4M(f.7O,c.B.dR);1n;3R:c.1C(1F);1n}};f.1g.u.L=j(3q){if(3q){l L=1a;if(3q.1o()==f.5l){L=c.L();3e(L.1r.1o()){1i f.6d:1i f.5l:1n;3R:l 7Y=1f 6c(f.6d,f.5l);L=c.2N(c.L(),7Y);1n}if(L!=1a){3q=(L.1r.1o()==f.6d)?f.5l:f.6d;c.9y(L,3q);c.67()}}V{l 7Y=1f 6c(f.P,f.9F,f.9E,f.9D,f.9C,f.9B,f.9A,f.5q,f.6b);L=c.2N(c.L(),7Y);if(L){l 3O=1a;if(3q.1o()==f.6b){l 2J=c.2N(c.L(),f.6b);if(2J==1a){3O=c.H.5Y(3q);L.1z.dQ(3O,L);3O.dr(L);c.5y(3O.pm)}V{l 7X=2J.6q;l 9z=7X.Y;l 7W=1a;if(9z>0)7W=7X.4v(0);1h(l x=0;x<9z;x++){2J.1z.dQ(7X.4v(0),2J)}2J.1z.pl(2J);if(7W)c.5y(7W)}}V c.9y(L,3q);c.67()}}}V o(c.1d())};f.1g.u.6a=j(4j,4y){l L=(c.2j?c.2j:D(c.1d()));L=D(L).4V(4y);D(L).6a(4j);if(!D(L).1v(f.dP))D(L).pk(c.5x)};f.1g.u.2N=j(1c,51){if(1c){l 4i=1c.1r.1o();if(1q(51)==f.dO){77(4i!=51&&4i!=f.3t){1c=1c.1z;4i=1c.1r.1o()}}V{l 7V=J;77(!7V&&4i!=f.3t){1h(l i=0;i<51.Y;i++){if(4i==51[i]){7V=R;1n}}if(!7V){1c=1c.1z;4i=1c.1r.1o()}}}if(4i!=f.3t)o(1c);V o(1a)}V o(1a)};f.1g.u.9y=j(1c,3q){l 3O=c.H.5Y(3q);l O=D(1c).O();1c.1z.pj(3O,1c);D(3O).O(O);c.5y(3O)};f.1g.u.9n=j(4N){if(!f.7U[c.B.3H]){4u{2B(D.9j({9i:c.B.9x+c.B.3H+\'.js\',9h:J}).9g)}4t(e){f.4O.9w("f: 9w 77 ph cX cI.");o 4N}}1h(l 1T in f.7U[c.B.3H]){4N=f.1L.1k(4N,c.B.69+1T+c.B.68,f.7U[c.B.3H][1T])};o(4N)};f.1g.u.dD=j(4N){o(c.B.69+4N+c.B.68)};f.1g.u.2n=j(dM){D(c.2p).4g(c.B.dN).O(dM)};f.1g.u.67=j(){l O=c.1s();D(c.7T).1H(O);D(c.2p).4g(c.B.9v).by(\'.9u\').1H(O);};f.1g.u.4M=j(65,7S,dF){l dL=7S||c.1V.B.7S;l 9m=2l.5X(\'\',\'4M\',dL);if(9m){l 4h="";3e(65){1i(f.64):4h=c.B.dK;1n;1i(f.7R):4h=c.B.dJ;1n;1i(f.7Q):4h=c.B.dI;1n;1i(f.7P):4h=c.B.dH;1n;1i(f.7O):4h=c.B.dG;1n;3R:4h=dF}l h=f.1L;l 1I=c.B.1I;1I=h.1k(1I,f.dE,c.B.4L);1I=h.1k(1I,f.9t,c.B.4S);1I=h.1k(1I,f.9s,c.B.5i+f.7E);1I=h.1k(1I,f.9r,c.B.7N);1I=h.1k(1I,f.9q,c.B.7M);1I=h.1k(1I,f.9p,c.dD(65));1I=h.1k(1I,f.9o,4h);1I=h.1k(1I,f.3N,c.3f);1I=c.9n(1I);l 4f=9m.3A;4f.pg(1I);4f.5e()}};f.1g.u.dC=j(){D(c.2p).4g(c.B.9l).pf()};f.1g.u.dj=j(){l dB=1f pe();o("E-"+dB.pd())};f.1g.u.5B=j(dA){l 3M;l L=c.1d();l aP=dA.8m(c.4s+c.4s);l 4x=1f 2O(c.4s,"g");if(L&&L.1r.1o()!=f.3t){1h(x=aP.Y-1;x>=0;x--){3M=aP[x];3M=3M.S(4x,"<br />");D(L).5G("<p>"+3M+"</p>")}}V{1h(x=0;x<aP.Y;x++){3M=aP[x];3M=3M.S(4x,"<br />");D(c.H.1j).d8("<p>"+3M+"</p>")}}};f.1g.u.8e=j(O){if(c.1E.1B.2z().1x!=1a){c.1C(f.7L,O)}V{c.5B(O)}};f.1g.u.aE=j(2k,2E){if(c.1E.1B.2z().1x!=1a){c.1C(f.7L,2k+c.1E.1B.2z().93()+2E)}};f.1g.u.aD=j(){if(c.1E.1B.2z().1x!=1a){c.1C(f.7L,c.1E.1B.2z().93())}};f.1g.u.5y=j(1c,3U){l 1S=c.H.4Y(),3V=c.1E.1B.2z();3U=3U?0:1;1S.pc(1c);3V.pb(1S);3V.aC(1c,3U);c.1E.1B.4W()};f.1g.u.4T=j(4f,2r){l 1D=4f.4U[0];if(1D){1h(l i=0;i<2r.Y;i++){l 1Y=2r[i];if(1Y.F&&1Y.1b)c.5r(1D,1Y)}}};f.1g.u.dz=j(){o D(D.7G(D(\'2h\'),j(s){o(s.1U&&s.1U.K(/7K\\.5k(\\.7J|\\.7I|\\.7H)?\\.js(\\?.*)?$/))})).1v(\'1U\').S(/7K\\.5k(\\.7J|\\.7I|\\.7H)?\\.js(\\?.*)?$/,\'\')};f.1g.u.dy=j(){o D(D.7G(D(\'2h\'),j(s){o(s.1U&&s.1U.K(/7K\\.5k(\\.7J|\\.7I|\\.7H)?\\.js(\\?.*)?$/))})).1v(\'1U\')};f.1g.u.dx=j(){o D(D.7G(D(\'2h\'),j(s){o(s.1U&&s.1U.K(/7K(-(.*)){0,1}(\\.7J|\\.7I|\\.7H)?\\.js(\\?.*)?$/))})).1v(\'1U\')};f.1g.u.pa=j(){o D(D.7G(D(\'2u\'),j(s){o(s.1P&&s.1P.K(/5k\\/dw\\/(.*)7f\\.1b(\\?.*)?$/))})).1v(\'1P\')};f.1g.u.dv=j(){l 5j=1f f.3D();if(c.B.3p){5j.3X(D.9j({9i:c.B.3p,9h:J}).9g)}V{5j.3X(c.B.1D,J)}if(c.B.3W.Y==0){c.B.3W=5j.4B.3W}if(c.B.3g.Y==0){c.B.3g=5j.4B.3g}if(c.B.4A.Y==0){c.B.4A=5j.4B.4A}};f.1g.u.5u=j(){D(c.H.1j).3x("7z",c.7z)};f.1g.u.7z=j(W){l E=f.2w[c.p9.M];E.2j=(W.ds.1r.1o()==f.du)?W.ds:1a};f.dp=j(1P){l 2u=3A.5Y(\'2u\');2u.7w=\'3p\';2u.1P=1P;l 2T=D(\'2T\').5W(0);2T.dr(2u)};f.1g.u.7F=j(){if(c.B.7F&&!f.5h[c.B.3a]){l 9k=J;l 4x=1f 2O(c.B.3a+\'\\/\'+f.7E+\'$\');D(\'2u\').dq(j(){if(c.1P.K(4x))9k=R});if(!9k)f.dp(c.B.5i+f.7E)}D(c.2p).do("p8"+c.B.3a);if(c.B.dn&&!f.5h[c.B.3a]){2B(D.9j({9i:c.B.5i+f.dm,9h:J}).9g)}if(f.5h[c.B.3a]&&f.5h[c.B.3a].56)f.5h[c.B.3a].56(c)};f.5g=j(5a){l E=2l.p7.f.2w[5a];l 4f=2l.3A;l 1d=E.1d();l 65=D(E.B.dk).1H();l 62=E.dj();3e(65){1i f.64:if(1d&&1d.1r&&1d.1r.1o!=f.A)1d=D(1d).4V(f.A);if(!1d&&E.2j)1d=D(E.2j).4V(f.A);1n}if(D.2A(E.B.9f))E.B.9f(E,2l);l 1D=4f.4U[0];l 2r=2B(E.B.4A);E.4T(4f,2r);if(1d){D(E.B.9e).1H(D(1d).1v(f.9c));D(E.B.7D).1H(D(1d).1v(f.7C));D(E.B.60).1H(D(1d).1v(f.61));D(E.B.7A).1H(D(1d).1v(f.7B))}if(E.2j){D(E.B.63+" "+E.B.7D).1H(D(E.2j).1v(f.7C));D(E.B.63+" "+E.B.60).1H(D(E.2j).1v(f.61));D(E.B.63+" "+E.B.7A).1H(D(E.2j).1v(f.7B))}D(E.B.di+" "+E.B.5Z).4e(j(){l 5f=D(E.B.9e).1H();if(5f.Y>0){l 2u;if(1d[0]&&1d[0].1r.1o()==f.A){2u=1d}V{E.1C(f.9d,62);2u=D("a[1P="+62+"]",E.H.1j)}2u.1v(f.9c,5f).1v(f.61,D(E.B.60).1H())}2l.5e()});D(E.B.63+" "+E.B.5Z).4e(j(){l 5f=D(E.B.7D).1H();if(5f.Y>0){E.1C(f.9b,62);D("6Q[1U$="+62+"]",E.H.1j).1v(f.7C,5f).1v(f.61,D(E.B.60).1H()).1v(f.7B,D(E.B.7A).1H())}2l.5e()});D(E.B.dh+" "+E.B.5Z).4e(j(){l 9a=D(E.B.dg).1H();l 99=D(E.B.df).1H();if(9a>0&&99>0){l 3j=E.H.5Y(f.de);l 98=1a;l p6=1a;l da=D(E.B.dc).1H();l db=3j.p5();db.94=da;1h(x=0;x<9a;x++){98=3j.p4(x);1h(y=0;y<99;y++){98.p3(y)}}D(3j).1v(\'cr\',D(E.B.d9).1H());l 1c=D(E.2N(E.L(),f.6n)).5W(0);if(!1c||!1c.1z)D(E.H.1j).d8(3j);V D(1c).5G(3j)}2l.5e()});D(E.B.d7+" "+E.B.5Z).4e(j(){l d5=D(E.B.d6).1H();E.5B(d5);2l.5e()});D(E.B.d4+" "+E.B.d3).O(E.1s());D(E.B.d2).7z(j(){2l.5e()});if(D.2A(E.B.97))E.B.97(E,2l)};f.2W=j(){c.7y=3A.5Y(\'N\');o c};f.2W.u.C=j(F,1Q,5X){1Q=1Q||J;5X=5X||J;o\'<\'+F+(1Q?c.96(1Q):\'\')+(5X?\'>\':\' />\')};f.2W.u.p2=j(F,2b,1Q){1Q=1Q||J;o\'<\'+F+(1Q?c.96(1Q):\'\')+\'>\'+2b+\'</\'+F+\'>\'};f.2W.u.p1=j(2b){o\'<![p0[\'+2b+\']]>\'};f.2W.u.cZ=j(3L){o c.d1(c.cY(3L))};f.2W.u.d1=j(d0){o d0.S(/&oZ;([a-z]+|(#\\d+));/ig,"&$1;")};f.2W.u.96=j(1Q){l 3L=c;3L.95=\'\';1h(l 1T in 1Q){l oY=\'\';l Q=1Q[1T];if(1q Q!=\'j\'&&Q.Y>0){if(ck(1T)==1T&&1q Q==\'4C\'){1T=Q.oX();Q=Q.4Q()}if(1T!=\'\'&&Q!=\'\'){3L.95+=\' \'+1T+\'="\'+3L.cZ(Q)+\'"\'}}}o 3L.95};f.2W.u.cY=j(3Q,7x){c.7y.94=3Q;c.7y.oW=3Q;l 1N=c.7y.94;if(1q 7x==\'2d\'){if(7x!=J)1N=1N.S(\'"\',\'&oV;\');if(7x==R)1N=1N.S(\'"\',\'&#oU;\')}o 1N};f.2W.u.oT=j(5c){l 1N=[];l 2g=5c.8m(/((=\\s*")(")("))|((=\\s*\\\')(\\\')(\\\'))|((=\\s*[^>\\s]*))/g);if(2g.93()!=5c){1h(l k in 2g){l v=2g[k];if(1q v!=\'j\'&&v.Y!=0){l re=1f 2O(\'(\\\\w+)\\\\s*\'+v);if(K=5c.K(re)){l Q=v.S(/^[\\s=]+/,"");l 5d=Q.oS(0);5d=5d==\'"\'?\'"\':(5d=="\'"?"\'":\'\');if(5d!=\'\'){Q=5d==\'"\'?Q.S(/^"|"+$/g,\'\'):Q.S(/^\'|\'+$/g,\'\')}5c=5c.S(K[0],\'\');1N.1X([K[1],Q])}}}}o 1N};f.bz={"8V":{"oR":{"59":["55","2T","O","7i","1R","2h","1m","M"],"G":["I","id","1m","M","cW","cV"]},"cX":{"59":["55","br","hr","1K","1R","2h"],"G":{"3z":["92","cL"],"0":"3H","1":"3L:3H"}},"cU":{"G":{"cW":/^(\\w){1}$/,"cV":/^(\\d)+$/}}},"8W":{"2l":{"7b":["1j"],"G":["4K","oQ"]},"1M":{"7b":["1M","1y","4G","4X","a","1p","2t"],"G":["oP","oO","oN","oM","oL","aK"]},"cU":{"59":["55","6S","br","cu","oK","2T","O","1K","7i","1R","2h","1m","M"],"G":["oJ","oI","aJ"]},"oH":{"59":["55","6S","br","2T","O","7i","1R","2h","1m","M"],"G":["aI","oG","oF","oE","oD","oC","oB"]}},"1J":{"a":{"G":{"0":"7g","1":"cS","2":"1P","3":"cE","4":"F","7w":/^(7v|cT|3p|2L|5U|7n|7r|5a|7p|7q|7s|7m|7l|7u|7o|7t||7k|7j)+$/,"cD":/^(7v|cT|3p|2L|5U|7n|7r|5a|7p|7q|7s|7m|7l|7u|7o|7t||7k|7j)+$/,"cR":/^(cQ|cP|8F|cO|cN|cM)$/,"5":"1e"}},"0":"6T","1":"8y","2":"6L","bv":{"G":{"0":"5b","1":"cS","2":"1P","oA":/^(R|J)$/,"cR":/^(cQ|cP|8F|cO|cN|cM)$/},"2c":["5b"]},"3":"b","55":{"G":["1P"],"2c":["1P"]},"6S":{"G":{"3z":/^(92|cL)$/},"2c":["3z"]},"4":"bu","2J":{"G":["54"]},"5":"1j","6":"br","2t":{"G":{"2Z":/^(2Z)$/,"1e":/^(2t|cH|cG)$/,"0":"Q"},"3K":"1M"},"7":"bt","8":"54","9":"bs","6R":{"G":{"4d":/^(2E|2k|4c|4b)$/,"0":"2Y","1":"4a","3r":/^(\\d)+$/,"49":/^(3o|48|47|45)$/,"2":"4J"},"3K":"5K"},"5K":{"G":{"4d":/^(2E|2k|4c|4b)$/,"0":"2Y","1":"4a","3r":/^(\\d)+$/,"49":/^(3o|48|47|45)$/,"2":"4J"}},"10":"dd","8x":{"G":{"0":"54","cF":/^([0-9]){8}/}},"11":"N","12":"bq","13":"dl","14":"dt","15":"em","2U":{"3K":"1M"},"1M":{"G":{"0":"5N","1":"5Q","2":"5Q-7g","3":"oz","oy":/^(5W|ox)$/},"2c":["5N"]},"2T":{"G":["cK"]},"16":"h1","17":"h2","18":"h3","19":"h4","20":"h5","21":"h6","22":"hr","O":{"G":["ow"]},"23":"i","6Q":{"G":["5b","1U","7h","ov","ou","cB","4J"],"2c":["5b","1U"]},"1y":{"G":{"0":"5Q","1":"5b","cJ":/^(cJ)$/,"2Z":/^(2Z)$/,"ot":/^(\\d)+$/,"2":"F","8X":/^(8X)$/,"2G":/^(\\d)+$/,"3":"1U","1e":/^(2t|os|cI|5V|bx|oq|op|cH|cG|X)$/,"4":"Q"},"3K":"1M"},"bp":{"G":{"0":"54","cF":/^([0-9]){8}/}},"24":"bo","1p":{"G":["1h"],"3K":"1M"},"25":"3k","26":"li","2u":{"G":{"0":"7g","1":"1P","2":"cE","8Z":/^(8Y|cv|cw|cx|7f|oo|,|;|)+$/i,"7w":/^(7v|7u|7t|7s|7r|7q|7p|7o|cC|5a|5U|7n|7m|2L|3p|7l||7k|7j)+$/i,"cD":/^(7v|7u|7t|7s|7r|7q|7p|7o|cC|5a|5U|7n|7m|2L|3p|7l||7k|7j)+$/i,"3":"1e"},"3K":"2T"},"bn":{"G":["id","F"],"2c":["id"]},"7i":{"G":{"0":"2b","91-bw":/^(2b\\-1e|om|ok|oj\\-oi)$/i,"1":"F","2":"oh"},"2c":["2b"]},"27":"8E","4C":{"G":["og","of","oe","od","4z","oc","7h","F","ob","1e","cB","4J"]},"28":"ol","bm":{"G":{"0":"1p","2Z":/^(2Z)$/},"2c":["1p"]},"6O":{"G":{"0":"1p","2Z":/^(2Z)$/,"1d":/^(1d)$/,"1":"Q"},"3K":"4X"},"29":"p","1R":{"G":{"0":"1e","cA":/^(4z|oa|4C)$/,"1":"cA","2":"Q"},"2c":["F"]},"30":"41","q":{"G":["54"]},"31":"bl","2h":{"G":{"1e":/^(X\\/o9|X\\/90|X\\/o8|X\\/o7|X\\/o6|X\\/3L)$/,"0":"7g","cz":/^(cz)$/,"1":"1U"},"2c":["1e"]},"4X":{"G":{"2Z":/^(2Z)$/,"cy":/^(cy)$/,"0":"F","1":"2G"},"3K":"1M"},"32":"bk","33":"3r","34":"3c","1m":{"G":{"0":"1e","8Z":/^(7f|o5|o4|cx|o3|cw|cv|o2|8Y)$/},"2c":["1e"]},"35":"3b","36":"3P","3j":{"G":{"0":"cs","1":"o1","2":"o0","cu":/^(nZ|nY|nX|nW|nV|nU|nT|ct|cs)$/,"nS":/^(nR|nQ|7d|7e|8Y)$/,"3":"cr","4":"4J"}},"8D":{"G":{"4d":/^(2E|2k|4c|4b)$/,"0":"2Y","1":"4a","49":/^(3o|48|47|45)$/}},"4F":{"G":{"0":"6T","4d":/^(2k|2E|4c|4b|2Y)$/,"1":"cq","2":"2Y","3":"4a","cp":/^(\\d)+$/,"4":"co","cn":/^(\\d)+$/,"1G":/^(6R|5K|1O|cm)$/,"49":/^(3o|48|47|45)$/}},"4G":{"G":["7e","7d","2Z","F","8X"],"2c":["7e","7d"],"3K":"1M"},"8C":{"G":{"4d":/^(2E|2k|4c|4b)$/,"0":"2Y","1":"4a","49":/^(3o|48|47)$/,"2":"45"}},"5I":{"G":{"0":"6T","4d":/^(2k|2E|4c|4b|2Y)$/,"1":"cq","2":"2Y","3":"4a","cp":/^(\\d)+$/,"4":"co","cn":/^(\\d)+$/,"1G":/^(6R|5K|1O|cm)$/,"49":/^(3o|48|47|45)$/}},"8B":{"G":{"4d":/^(2E|2k|4c|4b)$/,"0":"2Y","1":"4a","49":/^(3o|48|47|45)$/}},"37":"M","6P":{"G":{"4d":/^(2E|2k|4c|4b|2Y)$/,"0":"2Y","1":"4a","49":/^(3o|48|47|45)$/}},"38":"bj","39":"2I","40":"l"},6G:[],6F:[],5p:j(C,G){l 7c={};l cl=c.cf(C);1h(l 2i in G){l Q=G[2i];l h=f.1L;if(!h.2F(c.6G,2i)&&!h.2F(c.6F,Q)){if(1q Q!=\'j\'&&h.2F(cl,2i)){if(c.ch(C,2i)){if(c.cg(C,2i,Q)){7c[2i]=Q}}V{7c[2i]=Q}}}}o 7c},ce:j(C){l 1N=[];if(c.1J[C]&&c.1J[C][\'G\']){1h(k in c.1J[C][\'G\']){1N.1X(ck(k)==k?c.1J[C][\'G\'][k]:k)}}o 1N},ci:j(){l 1N=[];1h(l 1T in c.8W){1N.1X(c.8W[1T])}1h(l 1T in c.8V){1N.1X(c.8V[1T])}o 1N},cj:j(C){if(c.1J[C]){o R}1h(l 1T in c.1J){if(c.1J[1T]==C){o R}}o J},cd:j(C){l 8T=[];if(c.cj(C)){l 8U=c.ci();1h(l 1T in 8U){l 3J=8U[1T];if(1q 3J==\'4C\'){l h=f.1L;if((3J[\'59\']&&h.2F(3J[\'59\'],C))||(3J[\'7b\']&&!h.2F(3J[\'7b\'],C))){nP}l 7a=3J[\'G\']?3J[\'G\']:3J[\'nO\'];1h(l k in 7a){8T.1X(1q 7a[k]!=\'3Q\'?k:7a[k])}}}}o 8T},ch:j(C,2i){o c.1J[C]&&((c.1J[C][\'G\']&&c.1J[C][\'G\'][2i])||(c.1J[C][\'2c\']&&f.1L.2F(c.1J[C][\'2c\'],2i)))},cg:j(C,2i,Q){if(c.1J[C]&&(c.1J[C][\'G\']&&c.1J[C][\'G\'][2i]&&Q.Y>0&&!Q.K(c.1J[C][\'G\'][2i]))||(c.1J[C]&&c.1J[C][\'2c\']&&f.1L.2F(c.1J[C][\'2c\'],2i)&&Q.Y==0)){o J}o 1q c.1J[C]!=\'2d\'},cf:j(C){if(!c.5T){c.5T={}}if(!c.5T[C]){c.5T[C]=c.ce(C).nN(c.cd(C))}o c.5T[C]}};f.2X=j(78){c.4I=78;c.44=[];c.8S=[];c.5S=1a;o c};f.2X.u.43=j(3n,1p){1p=1p||R;l 58=c.44.Y;c.44[58]=3n;c.8S[58]=1p;c.5S=1a};f.2X.u.K=j(cc){if(c.44.Y==0){o[J,\'\']}l 2g=cc.K(c.cb());if(!2g){o[J,\'\']}l K=2g[0];1h(l i=1;i<2g.Y;i++){if(2g[i]){o[c.8S[i-1],K]}}o[R,2g[0]]};f.2X.u.cb=j(){if(c.5S==1a){1h(l i=0,58=c.44.Y;i<58;i++){c.44[i]=\'(\'+c.c9(c.ca(c.44[i]).S(/([\\/\\(\\)])/g,\'\\\\$1\'))+\')\'}c.5S=1f 2O(c.44.6u("|"),c.c1())}o c.5S};f.2X.u.ca=j(79){o 79.S(/\\(\\?(i|m|s|x|U)\\)/,\'~~~~~~c8\\$1~~~~~~\').S(/\\(\\?(\\-[i|m|s|x|U])\\)/,\'~~~~~~c7\\$1~~~~~~\').S(/\\(\\?\\=(.*)\\)/,\'~~~~~~c6\\$1~~~~~~\').S(/\\(\\?\\!(.*)\\)/,\'~~~~~~c5\\$1~~~~~~\').S(/\\(\\?\\<\\=(.*)\\)/,\'~~~~~~c4\\$1~~~~~~\').S(/\\(\\?\\<\\!(.*)\\)/,\'~~~~~~c3\\$1~~~~~~\').S(/\\(\\?\\:(.*)\\)/,\'~~~~~~c2\\$1~~~~~~\')};f.2X.u.c9=j(79){o 79.S(/~~~~~~c8(.{1})~~~~~~/,"(?\\$1)").S(/~~~~~~c7(.{2})~~~~~~/,"(?\\$1)").S(/~~~~~~c6(.*)~~~~~~/,"(?=\\$1)").S(/~~~~~~c5(.*)~~~~~~/,"(?!\\$1)").S(/~~~~~~c4(.*)~~~~~~/,"(?<=\\$1)").S(/~~~~~~c3(.*)~~~~~~/,"(?<!\\$1)").S(/~~~~~~c2(.*)~~~~~~/,"(?:\\$1)")};f.2X.u.c1=j(){o(c.4I?"m":"mi")};f.5R=j(2L){c.57=[2L];o c};f.5R.u.8O=j(){o c.57[c.57.Y-1]};f.5R.u.8Q=j(2a){c.57.1X(2a)};f.5R.u.8R=j(){if(c.57.Y==1){o J}c.57.4Q();o R};f.71=1;f.bZ=2;f.3E=3;f.5E=4;f.6Y=5;f.2f=j(3Z,2L,78){2L=2L||\'5Q\';c.4I=78||J;c.2o={};c.8P=3Z;c.42=1f f.5R(2L);c.2K={};c.2K[2L]=2L;o c};f.2f.u.43=j(3n,T){l T=T||"5Q";if(1q c.2o[T]==\'2d\'){c.2o[T]=1f f.2X(c.4I)}c.2o[T].43(3n);if(1q c.2K[T]==\'2d\'){c.2K[T]=T}};f.2f.u.2Q=j(3n,T,5P){if(1q c.2o[T]==\'2d\'){c.2o[T]=1f f.2X(c.4I)}c.2o[T].43(3n,5P);if(1q c.2K[5P]==\'2d\'){c.2K[5P]=5P}};f.2f.u.2H=j(3n,T){if(1q c.2o[T]==\'2d\'){c.2o[T]=1f f.2X(c.4I)}c.2o[T].43(3n,"bX");if(1q c.2K[T]==\'2d\'){c.2K[T]=T}};f.2f.u.4E=j(3n,T,5O){if(1q c.2o[T]==\'2d\'){c.2o[T]=1f f.2X(c.4I)}c.2o[T].43(3n,\'bV\'+5O);if(1q c.2K[5O]==\'2d\'){c.2K[5O]=5O}};f.2f.u.8q=j(T,75){c.2K[T]=75};f.2f.u.3X=j(1w){if(1q c.8P==\'2d\'){o J}l Y=1w.Y;l 3I;77(1q(3I=c.bR(1w))==\'4C\'){l 1w=3I[0];l 76=3I[1];l 3m=3I[2];l T=3I[3];if(!c.c0(76,3m,T)){o J}if(1w==\'\'){o R}if(1w.Y==Y){o J}Y=1w.Y}if(!3I){o J}o c.4H(1w,f.3E)};f.2f.u.c0=j(76,3m,T){T=T||J;if(!c.4H(76,f.3E)){o J}if(1q T==\'nM\'){o c.4H(3m,f.bZ)}if(c.bY(T)){if(!c.4H(3m,f.5E)){o J}o c.42.8R()}if(c.bW(T)){c.42.8Q(c.bU(T));if(!c.4H(3m,f.6Y)){o J}o c.42.8R()}c.42.8Q(T);o c.4H(3m,f.71)};f.2f.u.bY=j(T){o(T==="bX")};f.2f.u.bW=j(T){o(T.6y(0,1)=="bV")};f.2f.u.bU=j(T){o T.6y(1)};f.2f.u.4H=j(2b,bS){if(2b===\'\'){o R}l bT=c.42.8O();l 75=c.2K[bT];l 1N;2B(\'1N = c.8P.\'+75+\'(2b, bS);\');o 1N};f.2f.u.bR=j(1w){l 3m=c.2o[c.42.8O()].K(1w);l K=3m[1];l 5N=3m[0];if(5N){l 8N=1w.8h(K);l bQ=1w.aN(0,8N);1w=1w.6y(8N+K.Y);o[1w,bQ,K,5N]}o R};f.3l=j(3Z){D.53(c,1f f.2f(3Z,\'3h\'));c.8q(\'3h\',\'3h\');c.bP();c.56();o c};f.3l.u.56=j(){};f.3l.u.bP=j(){c.bO(\'3h\');c.bN(\'3h\');c.bM(\'3h\');c.bL(\'3h\')};f.3l.u.bO=j(1G){c.2Q("<!--",1G,\'8M\');c.2H("-->",\'8M\')};f.3l.u.bN=j(1G){c.2Q("<2h",1G,\'8L\');c.2H("</2h>",\'8L\')};f.3l.u.bM=j(1G){c.2Q("<1m",1G,\'8K\');c.2H("</1m>",\'8K\')};f.3l.u.bL=j(1G){c.4E("<\\\\s*[a-5F-9:\\-]+\\\\s*>",1G,\'72\');c.2Q("<[a-5F-9:\\-]+"+\'[\\\\\\/ \\\\\\>]+\',1G,\'72\');c.bK(\'72\');c.4E("</\\\\s*[a-5F-9:\\-]+\\\\s*>",1G,\'bG\')};f.3l.u.bK=j(1G){c.4E(\'\\\\s+\',1G,\'52\');c.bJ(1G);c.2H(\'/>\',1G);c.2H(\'>\',1G)};f.3l.u.bJ=j(1G){c.4E("\\\\s*[a-z-8p-9]*:?[a-z-8p-9]+\\\\s*(?=\\=)\\\\s*",1G,\'bB\');c.2Q(\'=\\\\s*"\',1G,\'6X\');c.43("\\\\\\\\\\"",\'6X\');c.2H(\'"\',\'6X\');c.2Q("=\\\\s*\'",1G,\'6W\');c.43("\\\\\\\\\'",\'6W\');c.2H("\'",\'6W\');c.4E(\'=\\\\s*[^>\\\\s]*\',1G,\'bA\')};f.1t=j(bI,T){l T=T||\'3h\';c.6D=1f f.3l(c);c.Z=bI;c.42=T;c.nL=[];c.nK=\'\';c.nJ=\'\';o c};f.1t.u.3X=j(1w){c.6D.3X(c.6N(1w));o c.6M(c.Z.bb())};f.1t.u.6N=j(1w){if(1w.K(/I="b3"/)||1w.K(/ns = "nI:nH-nG-nF/)){c.Z.b4()}o c.Z.6N(1w)};f.1t.u.6M=j(3I){if(c.Z.8w){c.Z.b2()}o c.Z.6M(3I)};f.1t.u.52=j(K,2a){o R};f.1t.u.3h=j(X){c.Z.ba(X);o R};f.1t.u.8M=j(K,2n){o c.74(K,2n,\'6J\')};f.1t.u.8L=j(K,2n){o c.74(K,2n,\'6I\')};f.1t.u.8K=j(K,2n){o c.74(K,2n,\'6H\')};f.1t.u.74=j(K,2a,1e){3e(2a){1i f.71:c.5M=K;1n;1i f.3E:c.5M+=K;1n;1i f.5E:3e(1e){1i\'6J\':c.Z.6J(c.5M+K);1n;1i\'6I\':c.Z.6I(c.5M+K);1n;1i\'6H\':c.Z.6H(c.5M+K);1n}}o R};f.1t.u.72=j(K,2a){3e(2a){1i f.71:c.bH=c.6Z(K);c.5L={};1n;1i f.6Y:c.8J(c.6Z(K));1n;1i f.5E:c.8J(c.bH,c.5L)}o R};f.1t.u.bG=j(K,2a){c.8G(c.6Z(K));o R};f.1t.u.8J=j(C,G){l G=G||{};c.bE(C);if(c.Z.8v(C)){c.Z.4p.1X(C);c.Z.aY(C,G);c.Z.6k(C,G);c.8I(C)}V if(c.Z.b1(C)){c.Z.b9(C,G)}V{c.Z.b8(C,G);c.8I(C)}c.Z.6E=C;c.Z.8t=R;c.Z.nE=G};f.1t.u.8G=j(C){if(c.bF(C)){c.bD(C);if(c.Z.8v(C)){l 70=c.Z.4p.4Q();if(70==J){o}V if(70!=C){C=70}c.Z.b7(C)}V{c.Z.b6(C)}}V{c.Z.b5(C)}c.Z.6E=C;c.Z.8t=J};f.1t.u.8I=j(C){c.Z.2V[C]=c.Z.2V[C]||0;c.Z.2V[C]++};f.1t.u.bF=j(C){if(c.Z.2V[C]){c.Z.2V[C]--;if(c.Z.2V[C]==0){c.Z.2V[C]=2d}o R}o J};f.1t.u.bE=j(2v){c.8H(2v,J)};f.1t.u.bD=j(C){c.8H(C,R)};f.1t.u.8H=j(2v,2S){l 2S=2S||J;if(c.Z.2V){1h(l C in c.Z.2V){l bC=c.Z.2V[C];if(bC>0&&c.Z.bi(C,2v,2S)){c.8G(C,R)}}}};f.1t.u.6K=j(){o c.Z.6K()};f.1t.u.6Z=j(C){C=C.S(/^([\\s<\\/>]*)|([\\s<\\/>]*)$/gm,\'\').1o();l 3i=c.Z.6K();if(3i[C]){o 3i[C]}o C};f.1t.u.bB=j(K,2a){if(f.6Y==2a){c.6V=K}o R};f.1t.u.6X=j(K,2a){if(f.3E==2a){c.5L[c.6V]=K}o R};f.1t.u.6W=j(K,2a){if(f.3E==2a){c.5L[c.6V]=K}o R};f.1t.u.bA=j(K,2a){c.5L[c.6V]=K.S(/^=/,\'\');o R};f.1l=j(){c.1W=\'\';c.4P=1f f.2W();c.2V={};c.3s=f.bz;c.4p=[];c.5H=[];c.8A={\'&bd;\':\'&#be;\',\'&nD;\':\'&#nC;\',\'&nB;\':\'&#nA;\',\'&nz;\':\'&#ny;\',\'&nx;\':\'&#nw;\',\'&nv;\':\'&#nt;\',\'&nr;\':\'&#nq;\',\'&np;\':\'&#nn;\',\'&nm;\':\'&#nl;\',\'&nk;\':\'&#nj;\',\'&nh;\':\'&#ng;\',\'&nf;\':\'&#nd;\',\'&by;\':\'&#nc;\',\'&nb;\':\'&#na;\',\'&n9;\':\'&#n8;\',\'&n7;\':\'&#n6;\',\'&n5;\':\'&#n4;\',\'&n3;\':\'&#n2;\',\'&n1;\':\'&#n0;\',\'&mZ;\':\'&#mY;\',\'&mX;\':\'&#mW;\',\'&mV;\':\'&#mU;\',\'&mT;\':\'&#mS;\',\'&mR;\':\'&#mQ;\',\'&mP;\':\'&#mO;\',\'&mN;\':\'&#mM;\',\'&mL;\':\'&#mK;\',\'&mJ;\':\'&#mI;\',\'&mH;\':\'&#mG;\',\'&mF;\':\'&#mE;\',\'&mD;\':\'&#mC;\',\'&mB;\':\'&#mA;\',\'&mz;\':\'&#my;\',\'&mx;\':\'&#mw;\',\'&mv;\':\'&#mt;\',\'&ms;\':\'&#mr;\',\'&mq;\':\'&#mp;\',\'&mo;\':\'&#mn;\',\'&mm;\':\'&#ml;\',\'&mk;\':\'&#mj;\',\'&mh;\':\'&#mg;\',\'&mf;\':\'&#me;\',\'&md;\':\'&#mc;\',\'&mb;\':\'&#ma;\',\'&m9;\':\'&#m8;\',\'&m7;\':\'&#m6;\',\'&m5;\':\'&#m4;\',\'&m3;\':\'&#m2;\',\'&m1;\':\'&#m0;\',\'&lZ;\':\'&#lY;\',\'&lX;\':\'&#lW;\',\'&lV;\':\'&#lU;\',\'&lT;\':\'&#lS;\',\'&lR;\':\'&#lQ;\',\'&lP;\':\'&#lO;\',\'&lN;\':\'&#lM;\',\'&lL;\':\'&#lK;\',\'&lJ;\':\'&#lI;\',\'&lH;\':\'&#lG;\',\'&lF;\':\'&#lE;\',\'&lD;\':\'&#lC;\',\'&lB;\':\'&#lA;\',\'&lz;\':\'&#ly;\',\'&lx;\':\'&#lw;\',\'&lv;\':\'&#87;\',\'&lu;\':\'&#lt;\',\'&ls;\':\'&#lr;\',\'&lq;\':\'&#lp;\',\'&lo;\':\'&#ln;\',\'&lm;\':\'&#ll;\',\'&lk;\':\'&#lj;\',\'&lh;\':\'&#lg;\',\'&lf;\':\'&#ld;\',\'&lc;\':\'&#lb;\',\'&la;\':\'&#l9;\',\'&l8;\':\'&#l7;\',\'&l6;\':\'&#l5;\',\'&l4;\':\'&#l3;\',\'&l2;\':\'&#l1;\',\'&l0;\':\'&#kZ;\',\'&kY;\':\'&#kX;\',\'&kW;\':\'&#kV;\',\'&kU;\':\'&#kT;\',\'&kS;\':\'&#kR;\',\'&kQ;\':\'&#kP;\',\'&kO;\':\'&#kN;\',\'&kM;\':\'&#kL;\',\'&kK;\':\'&#kJ;\',\'&kI;\':\'&#kH;\',\'&kG;\':\'&#kF;\',\'&kE;\':\'&#kD;\',\'&kC;\':\'&#kB;\',\'&kA;\':\'&#kz;\',\'&ky;\':\'&#kx;\',\'&kw;\':\'&#kv;\',\'&ku;\':\'&#kt;\',\'&ks;\':\'&#kr;\',\'&kq;\':\'&#kp;\',\'&ko;\':\'&#kn;\',\'&km;\':\'&#kl;\',\'&kk;\':\'&#kj;\',\'&ki;\':\'&#kh;\',\'&8F;\':\'&#kg;\',\'&kf;\':\'&#ke;\',\'&kd;\':\'&#kc;\',\'&kb;\':\'&#ka;\',\'&k9;\':\'&#k8;\',\'&k7;\':\'&#k6;\',\'&k5;\':\'&#k4;\',\'&k3;\':\'&#k2;\',\'&k1;\':\'&#k0;\',\'&jZ;\':\'&#jY;\',\'&jX;\':\'&#jW;\',\'&jV;\':\'&#jU;\',\'&jT;\':\'&#jS;\',\'&jR;\':\'&#jQ;\',\'&jP;\':\'&#jO;\',\'&jN;\':\'&#jM;\',\'&jL;\':\'&#jK;\',\'&jJ;\':\'&#jI;\',\'&jH;\':\'&#jG;\',\'&jF;\':\'&#jE;\',\'&jD;\':\'&#jC;\',\'&jB;\':\'&#jA;\',\'&jz;\':\'&#jy;\',\'&jx;\':\'&#jw;\',\'&jv;\':\'&#ju;\',\'&jt;\':\'&#jr;\',\'&jq;\':\'&#jp;\',\'&jo;\':\'&#jn;\',\'&jm;\':\'&#jl;\',\'&jk;\':\'&#jj;\',\'&ji;\':\'&#jh;\',\'&jg;\':\'&#jf;\',\'&je;\':\'&#jd;\',\'&jc;\':\'&#jb;\',\'&ja;\':\'&#j9;\',\'&j8;\':\'&#j7;\',\'&j6;\':\'&#j5;\',\'&mu;\':\'&#j4;\',\'&nu;\':\'&#j3;\',\'&j2;\':\'&#j1;\',\'&j0;\':\'&#iZ;\',\'&pi;\':\'&#iY;\',\'&iX;\':\'&#iW;\',\'&iV;\':\'&#iU;\',\'&iT;\':\'&#iS;\',\'&iR;\':\'&#iQ;\',\'&iP;\':\'&#iO;\',\'&iN;\':\'&#iM;\',\'&iL;\':\'&#iK;\',\'&iJ;\':\'&#iI;\',\'&iH;\':\'&#iG;\',\'&iF;\':\'&#iE;\',\'&iD;\':\'&#iC;\',\'&iB;\':\'&#iA;\',\'&iz;\':\'&#iy;\',\'&ix;\':\'&#iw;\',\'&iv;\':\'&#iu;\',\'&it;\':\'&#ir;\',\'&iq;\':\'&#ip;\',\'&io;\':\'&#im;\',\'&il;\':\'&#ik;\',\'&ij;\':\'&#ii;\',\'&ih;\':\'&#ie;\',\'&ic;\':\'&#ib;\',\'&ia;\':\'&#i9;\',\'&i8;\':\'&#i7;\',\'&i6;\':\'&#i5;\',\'&i4;\':\'&#i3;\',\'&i2;\':\'&#i1;\',\'&i0;\':\'&#hZ;\',\'&hY;\':\'&#hX;\',\'&hW;\':\'&#hV;\',\'&hU;\':\'&#hT;\',\'&hS;\':\'&#hR;\',\'&hQ;\':\'&#hP;\',\'&hO;\':\'&#hN;\',\'&hM;\':\'&#hL;\',\'&hK;\':\'&#hJ;\',\'&hI;\':\'&#hH;\',\'&hG;\':\'&#hF;\',\'&hE;\':\'&#hD;\',\'&bx;\':\'&#hC;\',\'&hB;\':\'&#hA;\',\'&hz;\':\'&#hy;\',\'&hx;\':\'&#hw;\',\'&hv;\':\'&#hu;\',\'&ht;\':\'&#hs;\',\'&hq;\':\'&#hp;\',\'&ho;\':\'&#hn;\',\'&hm;\':\'&#hl;\',\'&hk;\':\'&#hj;\',\'&hi;\':\'&#hh;\',\'&hg;\':\'&#hf;\',\'&he;\':\'&#hd;\',\'&hc;\':\'&#hb;\',\'&ha;\':\'&#h9;\',\'&h8;\':\'&#h7;\',\'&h0;\':\'&#gZ;\',\'&gY;\':\'&#gX;\',\'&gW;\':\'&#gV;\',\'&gU;\':\'&#gT;\',\'&gS;\':\'&#gR;\',\'&gQ;\':\'&#gP;\',\'&gO;\':\'&#gN;\',\'&ni;\':\'&#gM;\',\'&gL;\':\'&#gK;\',\'&gJ;\':\'&#gI;\',\'&gH;\':\'&#gG;\',\'&gF;\':\'&#gE;\',\'&gD;\':\'&#gC;\',\'&6U;\':\'&#gB;\',\'&gA;\':\'&#gz;\',\'&gy;\':\'&#gx;\',\'&gw;\':\'&#gv;\',\'&or;\':\'&#gu;\',\'&gt;\':\'&#gs;\',\'&gr;\':\'&#gq;\',\'&gp;\':\'&#go;\',\'&gn;\':\'&#gl;\',\'&gk;\':\'&#gj;\',\'&gh;\':\'&#gg;\',\'&gf;\':\'&#gd;\',\'&ne;\':\'&#gc;\',\'&bw;\':\'&#gb;\',\'&le;\':\'&#ga;\',\'&ge;\':\'&#g9;\',\'&3b;\':\'&#g8;\',\'&3P;\':\'&#g7;\',\'&g6;\':\'&#g5;\',\'&g4;\':\'&#g3;\',\'&g2;\':\'&#g1;\',\'&g0;\':\'&#fZ;\',\'&fY;\':\'&#fX;\',\'&fW;\':\'&#fV;\',\'&fU;\':\'&#fT;\',\'&fS;\':\'&#fR;\',\'&fQ;\':\'&#fP;\',\'&fO;\':\'&#fN;\',\'&fM;\':\'&#fL;\',\'&3H;\':\'&#fK;\',\'&fJ;\':\'&#fI;\',\'&fH;\':\'&#fG;\',\'&fF;\':\'&#fE;\',\'&fD;\':\'&#fC;\',\'&fB;\':\'&#fA;\',\'&fz;\':\'&#fy;\'};c.8u=["a","6T","8y","6L","bv","b","55","6S","bu","2J","1j","2t","bt","54","bs","6R","5K","dd","8x","N","bq","dl","dt","em","2U","1M","2T","h1","h2","h3","h4","h5","h6","O","i","bp","bo","1p","3k","li","bn","8E","4C","ol","bm","6O","p","1R","41","q","bl","2h","4X","bk","3r","3c","1m","3b","3P","3j","8D","4F","4G","8C","5I","8B","M","6P","bj","2I","l","fx"];c.b0=["br","hr","6Q","1y"];o c};f.1l.u.bi=j(C,5J,2S){l 2S=2S||J;if(C==\'4F\'){if((2S&&5J==\'6P\')||(!2S&&5J==\'4F\')){o R}}if(C==\'6O\'){if((2S&&5J==\'4X\')||(!2S&&5J==\'6O\')){o R}}o J};f.1l.u.6N=j(1w){c.1W=\'\';o 1w};f.1l.u.6M=j(1s){1s=c.bh(1s);1s=c.bg(1s);1s=c.bf(1s);1s=c.bc(1s);o 1s};f.1l.u.bh=j(1s){1h(l 8z in c.8A){1s=1s.S(1f 2O(8z,\'g\'),c.8A[8z])}o 1s};f.1l.u.bg=j(1s){l 3i=\'em|3c|3b|3P|8y|41|8x|6L\';o 1s.S(1f 2O(\'<\\/(\'+3i+\')><\\\\1>\',\'\'),\'\').S(1f 2O(\'(\\s*<(\'+3i+\')>\\s*){2}(.*)(\\s*<\\/\\\\2>\\s*){2}\',\'\'),\'<\\$2>\\$3<\\$2>\')};f.1l.u.bf=j(1s){o 1s.S(1f 2O(\'<(\'+c.8u.6u("|").S(/\\|4F/,\'\').S(/\\|5I/,\'\')+\')>(<br \\/>|&#be;|&bd;|\\\\s)*<\\/\\\\1>\',\'g\'),\'\')};f.1l.u.bc=j(1s){l 2g=1s.K(1f 2O(\'<41[^>]*>(.*?)<\\/41>\',\'fw\'));if(2g){1h(l i=0;i<2g.Y;i++){1s=1s.S(2g[i],2g[i].S(1f 2O(\'<br \\/>\',\'g\'),fv.fu(13,10)))}}o 1s};f.1l.u.bb=j(){o c.1W};f.1l.u.6K=j(){o{\'b\':\'3c\',\'i\':\'em\'}};f.1l.u.ba=j(X){c.1W+=X};f.1l.u.6J=j(X){if(c.ft){c.1W+=X}};f.1l.u.6I=j(X){if(!c.fs){c.1W+=X}};f.1l.u.6H=j(X){if(!c.fr){c.1W+=X}};f.1l.u.6k=j(C,G){c.1W+=c.4P.C(C,c.3s.5p(C,G),R)};f.1l.u.b9=j(C,G){c.1W+=c.4P.C(C,c.3s.5p(C,G))};f.1l.u.b8=j(C,G){};f.1l.u.b7=j(C){c.1W=c.1W.S(/<br \\/>$/,\'\')+c.8r(\'aZ\',C)+"</"+C+">"+c.8r(\'5G\',C)};f.1l.u.b6=j(C){};f.1l.u.b5=j(C){c.1W+="</"+C+">"};f.1l.u.b4=j(){c.5H=[\'N\',\'3r\'];c.3s.6G=[\'1m\'];c.3s.6F=[\'b3\',\'fq\'];c.8w=R};f.1l.u.b2=j(){c.5H=[];c.3s.6G=[];c.3s.6F=[];c.8w=J};f.1l.u.8v=j(C){o!f.1L.2F(c.5H,C)&&f.1L.2F(c.8u,C)};f.1l.u.b1=j(C){o!f.1L.2F(c.5H,C)&&f.1L.2F(c.b0,C)};f.1l.u.aX=j(C,2b){c.8s(\'5G\',C,2b)};f.1l.u.fp=j(C,2b){c.8s(\'aZ\',C,2b)};f.1l.u.aY=j(C,G){if(C!=\'li\'&&(C==\'2I\'||C==\'ol\')&&c.6E&&!c.8t&&c.6E==\'li\'){c.1W=c.1W.S(/<\\/li>$/,\'\');c.aX(C,\'</li>\')}};f.1l.u.8s=j(2R,C,2b){if(!c[\'3G\'+2R+\'3F\']){c[\'3G\'+2R+\'3F\']=[]}if(!c[\'3G\'+2R+\'3F\'][C]){c[\'3G\'+2R+\'3F\'][C]=[]}c[\'3G\'+2R+\'3F\'][C].1X(2b)};f.1l.u.8r=j(2R,C){if(c[\'3G\'+2R+\'3F\']&&c[\'3G\'+2R+\'3F\'][C]&&c[\'3G\'+2R+\'3F\'][C].Y>0){o c[\'3G\'+2R+\'3F\'][C].4Q()}o\'\'};f.aW=j(3Z,2s){l 2s=(1q 2s==\'2d\'?R:2s);D.53(c,1f f.2f(3Z,(2s?\'52\':\'4D\')));c.8q(\'4D\',\'52\');if(2s==R){c.2Q("/\\\\\\3Y[<\\\\s]*f[>\\\\s]*\\\\\\3Y/",\'52\',\'4D\');c.2H("/\\\\\\3Y[<\\/\\\\s]*f[>\\\\s]*\\\\\\3Y/",\'4D\')}c.4E("[\\\\fo-fm-6]*\\\\\\fl[a-z-8p-9]+",\'4D\',\'aV\');c.2Q("/\\\\\\3Y",\'4D\',\'8o\');c.2H("\\\\\\3Y/",\'8o\');c.2Q("\\fk",\'4D\',\'6C\');c.2H("\\fj",\'6C\');c.2Q("/\\\\\\3Y",\'6C\',\'8n\');c.2H("\\\\\\3Y/",\'8n\');o c};f.3D=j(){c.6B=J;c.5D=J;c.2s=R;c.4B={\'3W\':[],\'3g\':[],\'4A\':[]};o c};f.3D.u.3X=j(1w,2s){l 2s=(1q 2s==\'2d\'?c.2s:2s);c.6D=1f f.aW(c,2s);c.6D.3X(1w)};f.3D.u.52=j(K,2a){o R};f.3D.u.8o=j(X,2n){if(X.K(/fi[a-5F-9\\s]*E[a-5F-9\\s]*/mi)){o J}if(2n==f.3E){if(!c.6B){c.5D=R;c.2m={\'M\':f.1L.4w(X)}}V{if(c.2m[c.2e]){if(!c.2m[c.2e].6z){c.2m[c.2e].6z=[X]}V{c.2m[c.2e].6z.1X(X)}}}c.6B=R}o R};f.3D.u.6C=j(K,2n){if(2n==f.3E){K=f.1L.4w(K);if(K!=\'\'){c.2m[c.2e].1m=K}}V if(2n==f.5E){c.6B=J;c.5D=J;c.aU(c.2m)}o R};f.3D.u.8n=j(K,2n){if(2n==f.3E){c.2m[c.2e].8k=K.S(/^([\\s\\/\\*]*)|([\\s\\/\\*]*)$/gm,\'\')}o R};f.3D.u.aV=j(K){K=K.S(/^([\\s\\.]*)|([\\s\\.*]*)$/gm,\'\');l C=\'\';if(K.8h(\'.\')>0){l 8l=K.8m(\'.\');c.2e=8l[1];l C=8l[0]}V{c.2e=K}if(!c.5D){c.2m={\'M\':(!C?\'\':C.fh()+\': \')+c.2e};c.5D=R}if(!c.2m[c.2e]){c.2m[c.2e]={\'F\':c.2e}}if(C){if(!c.2m[c.2e].3i){c.2m[c.2e].3i=[C]}V{c.2m[c.2e].3i.1X(C)}}o R};f.3D.u.aU=j(6A){1h(l F in 6A){l 2P=6A[F];if(1q 2P==\'4C\'&&F!=\'M\'){c.4B.3W.1X({\'F\':f.1L.4w(2P.F),\'M\':6A.M,\'aT\':f.1L.4w((2P.6z||2P.3i).6u(\', \'))});if(2P.8k){c.4B.3g.1X({\'F\':\'.\'+f.1L.4w(2P.F),\'1b\':2P.8k})}if(2P.1m){c.4B.4A.1X({\'F\':\'.\'+f.1L.4w(2P.F),\'1b\':2P.1m})}}}};D.fn.aS=j(){if(c[0].8j==3)o!(/[^\\t\\n\\r ]/.2M(c[0].4z));o J};f.aS=j(n){if(n.8j==3)o!(/[^\\t\\n\\r ]/.2M(n.4z));o J};f.fg=j(3C){o!(/[^\\t\\n\\r ]/.2M(3C))};D.fn.4V=j(4y){l n=c;if(n[0].8j==3)n=n.5z().aR(0,1);if(n.51(4y).2G()==1)o n;V o n.5z(4y).aR(0,1)};f.1L={1k:j(3C,aQ,aO){l 4x=1f 2O(aQ,"g");o(3C.S(4x,aO))},ff:j(3C,aM,8i){o(3C.aN(0,8i)+aM+3C.6y(8i))},4w:j(3C){o 3C.S(/^(\\s*)|(\\s*)$/gm,\'\')},2F:j(3B,5C){1h(l i=0;i<3B.Y;i++){if(3B[i]===5C)o R}o J},8h:j(3B,4v){l 8g=-1;1h(l i=0;i<3B.Y;i++){if(3B[i]==4v){8g=i;1n}}o(8g)},aL:j(3B,F){1h(l i=0;i<3B.Y;i++){l 4v=3B[i];if(4v.F==F)o(4v)}o(1a)}};f.2D=j(E){c.1V=E;c.5x="fe";c.4s="\\r\\n"};f.2D.u.5w=j(1K){c.1E=1K;c.H=1K.1B.3A;l 1D=c.H.4U[0];l 2r=2B(c.B.3g);c.4T(c.H,2r);c.H.M=c.1V.3f;D(\'O\',c.H).1v(\'3z\',c.B.4S);D(c.H.1j).O(c.1V.4R);l E=c;c.H.1j.aK=j(){E.H.4r="on";E.H=1K.1B.3A};c.H.fd=j(){E.6x()};c.H.aJ=j(){E.6x();E.2x()};c.H.aI=j(){E.6x()};c.H.1j.fc=j(){E.1E.1B.aH.aG=J};c.H.1j.fb=j(){E.1E.1B.aH.aG=J;E.5B(2l.fa.f9("3h"))};if(c.aF){if(D.2A(c.B.3y))c.B.3y(c);c.1V.5v();if(D.2A(c.B.3w))c.B.3w(c);c.5u()}c.aF=R;c.H.4r="on";4u{c.H=1K.1B.3A}4t(e){}};f.2D.u.1C=j(1F,1R){3e(1F){1i f.6t:1i f.6s:l L=c.2N(c.L(),f.6r);if(L){l 3v=L.1z.1z;if(L.1z.6q.Y>1||3v.1r.1o()==f.6p||3v.1r.1o()==f.6o)c.H.2q(1F)}1n;3R:if(1R)c.H.2q(1F,J,1R);V c.H.2q(1F);1n}};f.2D.u.1d=j(){l 4Z=c.1E.1B.3A.4Z;if(4Z!=1a){if(4Z.5A!=2d)o(4Z.5A())}};f.2D.u.6x=j(){c.H.4Z=c.H.3V.4Y()};f.2D.u.5r=j(1D,1Y){1D.f8(1Y.F,1Y.1b)};f.2D.u.8e=j(O){l 1S=c.H.3V.4Y();if(D(1S.5A()).5z(c.B.6w).is(\'*\')){4u{1S.8f(O)}4t(e){}}V{c.5B(O)}};f.2D.u.aE=j(2k,2E){l 1S=c.H.3V.4Y();if(D(1S.5A()).5z(c.B.6w).is(\'*\')){4u{1S.8f(2k+1S.X+2E)}4t(e){}}};f.2D.u.aD=j(){l 1S=c.H.3V.4Y();if(D(1S.5A()).5z(c.B.6w).is(\'*\')){4u{l X=1S.X;c.1C(\'f7\');1S.8f(X)}4t(e){}}};f.2D.u.2x=j(){c.2j=1a};f.2D.u.5y=j(1c,3U){l 1S=c.H.3V.4Y();3U=3U?R:J;1S.f6(1c);1S.aC(3U);1S.4X();1c.4W()};f.2C=j(E){c.1V=E;c.5x="I";c.4s="\\n"};f.2C.u.5w=j(1K){l E=c;c.1E=1K;c.H=1K.at;l 1D=c.H.4U[0];l 2r=2B(c.B.3g);c.4T(c.H,2r);c.H.M=c.1V.3f;D(\'O\',c.H).1v(\'3z\',c.B.4S);c.O(c.1V.4R);c.6v();if(D.2A(c.B.3y))c.B.3y(c);c.1V.5v();D(c.H).3x("3u",c.3u);D(c.H).3x("2x",c.2x);D(c.H).3x("4W",j(){E.6v.f5(E)});if(D.2A(c.B.3w))c.B.3w(c);c.5u()};f.2C.u.O=j(O){if(1q O===\'3Q\'){4u{c.H.4r="aB"}4t(e){};O=O.S(/<em(\\b[^>]*)>/gi,"<i$1>").S(/<\\/em>/gi,"</i>").S(/<3c(\\b[^>]*)>/gi,"<b$1>").S(/<\\/3c>/gi,"</b>");D(c.H.1j).O(O);c.6v()}V o(D(c.H.1j).O())};f.2C.u.1C=j(1F,1R){if(!c.1d())o(J);3e(1F){1i f.6t:1i f.6s:l 1x=c.1d();l 2y=c.1E.1B.2z();l 1Z=2y.1Z;if(1Z.5s=="#X")1Z=1Z.1z;1x=c.2N(1x,f.5t);1Z=c.2N(1Z,f.5t);if(1x&&1x==1Z&&1x.1r.1o()==f.6r){l 3v=1x.1z.1z;if(1x.1z.6q.Y>1||3v.1r.1o()==f.6p||3v.1r.1o()==f.6o)c.H.2q(1F,\'\',1a)}1n;3R:if(1R)c.H.2q(1F,\'\',1R);V c.H.2q(1F,\'\',1a)}l L=c.1d();if(L.1r.1o()==f.3t)c.1C(f.4q,f.P)};f.2C.u.1d=j(){l 2y=c.1E.1B.2z();l 1c=2y.1x;if(1c){if(1c.5s=="#X")o(1c.1z);V o(1c)}V o(1a)};f.2C.u.5r=j(1D,1Y){1D.8c(1Y.F+" {"+1Y.1b+"}",1D.8b.Y)};f.2C.u.3u=j(W){l E=f.2w[c.M];l L=1a;if(W.6l){if(W.1A==66){E.1C(f.8a);o J}if(W.1A==73){E.1C(f.89);o J}}V if(W.1A==13){if(!W.6m){L=E.1d();if(L&&L.1r.1o()==f.5q){W.f4();E.8e(\'<p></p>\')}}}};f.2C.u.2x=j(W){l E=f.2w[c.M];E.2j=1a;l L=1a;if(W.1A==13&&!W.6m){D(E.H.1j).aq(f.88).ap()}if(W.1A!=8&&W.1A!=17&&W.1A!=46&&W.1A!=87&&!W.ao&&!W.6l){L=E.1d();l F=L.1r.1o();if(F=="3c"||F=="b"||F=="em"||F=="i"||F=="3b"||F=="3P"||F=="a")F=L.1z.1r.1o();if(F==f.3t)E.1C(f.4q,f.P)}};f.2C.u.6v=j(){if(c.H.4r=="aB"){4u{c.H.4r="on";c.H.2q("f3",\'\',J)}4t(e){}}};f.2C.u.6k=j(C,G){l G=c.3s.5p(C,G);if(C==\'3r\'&&G.1m){l 2v=c.6j(G.1m);if(2v){c.4p.4Q();l C=2v;c.4p.1X(2v);G.1m=\'\'}V{o}}c.1W+=c.4P.C(C,G,R)};f.2C.u.6j=j(1m){if(/am/.2M(1m))o\'3c\';if(/al/.2M(1m))o\'em\';if(/3b/.2M(1m))o\'3b\';if(/ak/.2M(1m))o\'3P\';o J};f.3S=j(E){c.1V=E;c.5x="I";c.4s="\\r\\n"};f.3S.u.5w=j(1K){c.1E=1K;c.H=1K.1B.3A;l 1D=c.H.4U[0];l 2r=2B(c.B.3g);c.4T(c.H,2r);c.H.M=c.1V.3f;D(\'O\',c.H).1v(\'3z\',c.B.4S);c.H.4r="on";c.O(c.1V.4R);if(D.2A(c.B.3y))c.B.3y(c);c.1V.5v();D(c.H).3x("3u",c.3u);D(c.H).3x("2x",c.2x);if(D.2A(c.B.3w))c.B.3w(c);c.5u()};f.3S.u.1C=j(1F,1R){if(1R)c.H.2q(1F,J,1R);V c.H.2q(1F)};f.3S.u.1d=j(){l 2y=c.1E.1B.2z();l 1c=2y.1x;if(1c){if(1c.5s=="#X")o(1c.1z);V o(1c)}V o(1a)};f.3S.u.5r=j(1D,1Y){1D.8c(1Y.F+" {"+1Y.1b+"}",1D.8b.Y)};f.3S.u.3u=j(W){l E=f.2w[c.M];l 2y=E.1E.1B.2z();8d=2y.f2(0).f1;if(!D(8d).4V(f.6n.6u(","))[0]&&!D(8d).4V(\'li\')&&W.1A!=f.3T.aA&&W.1A!=f.3T.az&&W.1A!=f.3T.ay&&W.1A!=f.3T.ax&&W.1A!=f.3T.aw&&W.1A!=f.3T.av&&W.1A!=f.3T.au)E.1C(f.4q,f.P)};f.3S.u.2x=j(W){l E=f.2w[c.M];E.2j=1a};f.3d=j(E){c.1V=E;c.5x="I";c.4s="\\n"};f.3d.u.5w=j(1K){c.1E=1K;c.H=1K.at;l 1D=c.H.4U[0];l 2r=2B(c.B.3g);c.4T(c.H,2r);c.H.M=c.1V.3f;D(\'O\',c.H).1v(\'3z\',c.B.4S);c.H.4r="on";c.O(c.1V.4R);if(D.2A(c.B.3y))c.B.3y(c);c.1V.5v();D(c.H).3x("3u",c.3u);D(c.H).3x("2x",c.2x);if(D.2A(c.B.3w))c.B.3w(c);c.5u()};f.3d.u.1C=j(1F,1R){if(!c.1d())o(J);3e(1F){1i f.6t:1i f.6s:l 1x=c.1d();l 2y=c.1E.1B.2z();l 1Z=2y.1Z;if(1Z.5s=="#X")1Z=1Z.1z;1x=c.2N(1x,f.5t);1Z=c.2N(1Z,f.5t);if(1x&&1x==1Z&&1x.1r.1o()==f.6r){l 3v=1x.1z.1z;if(1x.1z.6q.Y>1||3v.1r.1o()==f.6p||3v.1r.1o()==f.6o)c.H.2q(1F,\'\',1a)}1n;1i f.as:1i f.ar:c.H.2q(1F,\'\',1a);l 1x=c.1d();l L=c.2N(1x,f.6n);if(L)D(L).f0(D(L).O());1n;3R:if(1R)c.H.2q(1F,\'\',1R);V c.H.2q(1F,\'\',1a)}l L=c.1d();if(L&&L.1r.1o()==f.3t)c.1C(f.4q,f.P)};f.3d.u.1d=j(){l 2y=c.1E.1B.2z();l 1c=2y.1x;if(1c){if(1c.5s=="#X")o(1c.1z);V o(1c)}V o(1a)};f.3d.u.5r=j(1D,1Y){1D.8c(1Y.F+" {"+1Y.1b+"}",1D.8b.Y)};f.3d.u.3u=j(W){l E=f.2w[c.M];if(W.6l){if(W.1A==66){E.1C(f.8a);o J}if(W.1A==73){E.1C(f.89);o J}}};f.3d.u.2x=j(W){l E=f.2w[c.M];E.2j=1a;l L=1a;if(W.1A==13&&!W.6m){D(E.H.1j).aq(f.88).ap();L=E.1d();if(L&&L.1r.1o()==f.5q)E.1C(f.4q,f.P);}if(W.1A==13&&W.6m){E.1C(\'eZ\')}if(W.1A!=8&&W.1A!=17&&W.1A!=46&&W.1A!=87&&!W.ao&&!W.6l){L=E.1d();l F=L.1r.1o();if(F=="3c"||F=="b"||F=="em"||F=="i"||F=="3b"||F=="3P"||F=="a"||F=="3r")F=L.1z.1r.1o();if(F==f.3t||F==f.an)E.1C(f.4q,f.P);}};f.3d.u.6k=j(C,G){l G=c.3s.5p(C,G);if(C==\'3r\'&&G.1m){l 2v=c.6j(G.1m);if(2v){c.4p.4Q();l C=2v;c.4p.1X(2v);G.1m=\'\';if(1q G[\'I\']==\'3Q\')G[\'I\']=G[\'I\'].S(/eY-1m-3r/gi,\'\')}V{o}}c.1W+=c.4P.C(C,G,R)};f.3d.u.6j=j(1m){if(/am/.2M(1m))o\'3c\';if(/al/.2M(1m))o\'em\';if(/3b/.2M(1m))o\'3b\';if(/ak/.2M(1m))o\'3P\';o J};',62,1714,'||||||||||||this|||WYMeditor||||function||var|||return||||||prototype|||||||_options|tag|jQuery|wym|name|attributes|_doc|class|false|match|container|title|div|html||value|true|replace|mode||else|evt|text|length|_Listener|||||||||||null|css|node|selected|type|new|editor|for|case|body|replaceAll|XhtmlSaxListener|style|break|toLowerCase|label|typeof|tagName|xhtml|XhtmlParser|boxHtml|attr|raw|focusNode|input|parentNode|keyCode|contentWindow|_exec|styles|_iframe|cmd|scope|val|dialogHtml|_tags|iframe|Helper|form|result|row|href|options|param|range|key|src|_wym|output|push|oCss|anchorNode|||||||||||state|content|required|undefined|_current_element|Lexer|matches|script|attribute|_selected_image|left|window|_current_item|status|_regexes|_box|execCommand|aCss|only_wym_blocks|button|link|new_tag|INSTANCES|keyup|sel|getSelection|isFunction|eval|WymClassMozilla|WymClassExplorer|right|contains|size|addExitPattern|ul|blockquote|_mode_handlers|start|test|findUp|RegExp|details|addEntryPattern|position|closing|head|fieldset|_open_tags|XmlHelper|ParallelRegex|char|disabled|||||||||||skin|sub|strong|WymClassSafari|switch|_index|editorStyles|Text|tags|table|legend|XhtmlLexer|matched|pattern|top|stylesheet|sType|span|validator|BODY|keydown|ancestor|postInit|bind|preBind|dir|document|arr|str|WymCssParser|LEXER_UNMATCHED|_closing|_insert_|lang|parsed|defaults|inside|xml|sTmp|INDEX|newNode|sup|string|default|WymClassOpera|KEY|toStart|selection|classesItems|parse|x2a|parser||pre|_mode|addPattern|_patterns|baseline||bottom|middle|valign|charoff|justify|center|align|click|doc|find|sBodyHtml|tagname|sClass|oClass|sContainer|sTool|iframeHtml|WymClass|_tag_stack|FORMAT_BLOCK|designMode|_newLine|catch|try|item|trim|rExp|jqexpr|data|dialogStyles|css_settings|object|WymCss|addSpecialPattern|td|textarea|_invokeParser|_case|width|onload|basePath|dialog|sVal|console|helper|pop|_html|direction|addCssRules|styleSheets|parentsOrSelf|focus|select|createRange|caretPos||filter|Ignore|extend|cite|base|init|_stack|count|except|index|alt|tag_attributes|delimiter|close|sUrl|INIT_DIALOG|SKINS|skinPath|CssParser|wymeditor|TH|oContainer|oTool|wym_section|getValidTagAttributes|PRE|addCssRule|nodeName|BLOCKS|listen|bindEvents|initIframe|_class|setFocusToNode|parents|parentElement|paste|elem|_has_title|LEXER_EXIT|z0|after|avoided_tags|th|now_on_tag|colgroup|_tag_attributes|_non_tag|action|special|new_mode|accept|StateStack|_regex|_possible_tag_attributes|next|hidden|get|open|createElement|submitSelector|titleSelector|TITLE|sStamp|dialogImageSelector|DIALOG_LINK|dialogType||update|stringDelimiterRight|stringDelimiterLeft|toggleClass|BLOCKQUOTE|Array|TD|aClasses|wym_dialog|wym_cancel|wym_submit|wym_dialog_type|getTagForStyle|openBlockTag|ctrlKey|shiftKey|MAIN_CONTAINERS|UL|OL|childNodes|LI|OUTDENT|INDENT|join|enableDesignMode|iframeBodySelector|saveCaret|substring|expressions|style_details|_in_style|WymCssStyle|_Lexer|last_tag|skiped_attribute_values|skiped_attributes|addCss|addScript|addComment|getTagReplacements|address|afterParsing|beforeParsing|option|tr|img|col|bdo|abbr|prop|_current_attribute|SingleQuotedAttribute|DoubleQuotedAttribute|LEXER_SPECIAL|normalizeTag|expected_tag|LEXER_ENTER|OpeningTag||_addNonTagBlock|handler|unmatched|while|case_sensitive|regex|tag_defaults|only|valid_attributes|rows|cols|screen|charset|height|meta|icon|shortcut|subsection|section|prev|help|glossary|copyright|contents|chapter|bookmark|appendix|alternate|rel|escape_quotes|_entitiesDiv|mousedown|altSelector|ALT|SRC|srcSelector|SKINS_DEFAULT_CSS|loadSkin|grep|packed|min|pack|jquery|INSERT_HTML|jQueryPath|wymPath|PREVIEW|DIALOG_PASTE|DIALOG_TABLE|DIALOG_IMAGE|dialogFeatures|_element|STRINGS|bFound|firstNode|nodes|aTypes|NAME|CLASS_NAME|TOOL_TITLE|HTML|iframeBasePath|browser|Cancel|Submit|224|BR|ITALIC|BOLD|cssRules|insertRule|startNode|insert|pasteHTML|ret|indexOf|pos|nodeType|feedback_style|parts|split|WymCssFeedbackStyle|WymCssComment|_0|mapHandler|_getClosingTagContent|_insertContentWhenClosingTag|last_tag_opened|block_tags|isBlockTag|_avoiding_tags_implicitly|del|acronym|entity|entities|thead|tfoot|tbody|noscript|circ|_callCloseTagListener|_autoCloseUnclosed|_increaseOpenTagCounter|_callOpenTagListener|Css|Script|Comment|unparsed_character_count|getCurrent|_parser|enter|leave|_labels|default_attributes|default_attributes_and_events|_attributes|_events|readonly|all|media|javascript|http|ltr|toString|innerHTML|_formated_options|tagOptions|postInitDialog|newRow|iCols|iRows|INSERT_IMAGE|HREF|CREATE_LINK|hrefSelector|preInitDialog|responseText|async|url|ajax|found|htmlSelector|wDialog|replaceStrings|DIALOG_BODY|DIALOG_TITLE|JQUERY_PATH|WYM_PATH|CSS_PATH|DIRECTION|hasfocus|htmlValSelector|error|langPath|switchTo|lgt|H6|H5|H4|H3|H2|H1|sName|sContainers|CONTAINERS_ITEMS|CONTAINER_CLASS|CONTAINER_TITLE|CONTAINER_NAME|aContainers|sClasses|CLASSES_ITEMS|CLASS_TITLE|sTools|TOOLS_ITEMS|TOOL_CLASS|TOOL_NAME|aTools|STATUS|IFRAME|CLASSES|CONTAINERS|TOOLS|LOGO|IFRAME_BASE_PATH|WYM_INDEX|SaxListener|preInit|Paste_From_Word|indent|Table|wym_title|Image|Link|wym_classes|wym_containers|wym_tools|wym_iframe|Preview|Unlink|Outdent|Indent|names|super|italic|bold|DIV|metaKey|remove|children|INSERT_UNORDEREDLIST|INSERT_ORDEREDLIST|contentDocument|DELETE|BACKSPACE|DOWN|RIGHT|UP|LEFT|ENTER|off|collapse|unwrap|wrap|_initialized|returnValue|event|onclick|onkeyup|onfocus|findByName|inserted|substr|rep||old|slice|isPhantomNode|expr|addStyleSetting|WymCssStyleDeclaration|WymCssLexer|insertContentAfterClosingTag|fixNestingBeforeOpeningBlockTag|before|inline_tags|isInlineTag|allowStylingTagsAndAttributes|MsoNormal|avoidStylingTagsAndAttributes|closeUnopenedTag|closeUnknownTag|closeBlockTag|openUnknownTag|inlineTag|addContent|getResult|removeBrInPre|nbsp|160|removeEmptyTags|joinRepeatedEntities|replaceNamedEntities|shouldCloseTagAutomatically|tt|small|samp|optgroup|map|kbd|ins|dfn||code|caption|big|area|equiv|image|not|XhtmlValidator|UnquotedAttribute|TagAttributes|counter|autoCloseUnclosedBeforeTagClosing|autoCloseUnclosedBeforeNewOpening|_decreaseOpenTagCounter|ClosingTag|_tag|Listener|addAttributeTokens|addInTagDeclarationTokens|addTagTokens|addCssTokens|addScriptTokens|addCommentTokens|addTokens|unparsed|_reduce|is_match|current|_decodeSpecial|_|_isSpecialMode|__exit|_isModeEnd|LEXER_MATCHED|_dispatchTokens|_getPerlMatchingFlags|Tk7|Tk6|Tk5|Tk4|Tk3|Tk2|Tk1|_untokenizeRegex|_tokenizeRegex|_getCompoundedRegex|subject|getDefaultAttributesAndEventsForTag|getUniqueAttributesAndEventsForTag|getPossibleTagAttributes|validateAttribute|doesAttributeNeedsValidation|getDefaultAttributesAndEventsForTags|isValidTag|parseInt|possible_attributes|rowgroup|rowspan|headers|colspan|axis|summary|border|box|frame|braille|print|projection|multiple|defer|valuetype|usemap|home|rev|hreflang|datetime|submit|reset|file|checked|profile|rtl|polygon|poly|circle|rectangle|rect|shape|coords|designates|keyboard|tabindex|accesskey|language|escapeEntities|escapeOnce|escaped|_fixDoubleEscape|cancelSelector|previewSelector|dialogPreviewSelector|sText|textSelector|dialogPasteSelector|append|summarySelector|sCaption|newCaption|captionSelector||TABLE|colsSelector|rowsSelector|dialogTableSelector|dialogLinkSelector|uniqueStamp|dialogTypeSelector||SKINS_DEFAULT_JS|initSkin|addClass|loadCss|each|appendChild|target||IMG|configureEditorUsingRawCss|skins|computeJqueryPath|computeWymPath|computeBasePath|sData|now|toggleHtml|encloseString|BASE_PATH|bodyHtml|dialogPreviewHtml|dialogPasteHtml|dialogTableHtml|dialogImageHtml|dialogLinkHtml|features|sMessage|statusSelector|STRING|CLASS|insertBefore|dialogFeaturesPreview|TOGGLE_HTML|PASTE|INSERT_TABLE|exec|updateEvent|updateSelector|classSelector|containerSelector|toolSelector|hide|containersItemHtml|containersItems|classesItemHtml|toolsItemHtml|toolsItems|statusHtml|htmlHtml|classesHtml|containersHtml|toolsHtml|logoHtml|wym_dialog_preview|wym_text|wym_dialog_paste|wym_cols|wym_rows|wym_summary|wym_caption|wym_dialog_table|Title|||wym_alt|wym_src|URL|wym_dialog_image|wym_href|wym_dialog_link|xhtml1|DTD|org|www|300|560|resizable|toolbar|titlebar|menubar|wym_html_val|wym_status|wym_html|wym_box|ToggleHtml|Paste|InsertTable|InsertImage|CreateLink|Redo|Undo|InsertUnorderedList|InsertOrderedList|Subscript|Superscript|Italic|Bold|IFRAME_DEFAULT|LANG_DEFAULT_PATH|SKINS_DEFAULT_PATH|apple|InsertLineBreak|replaceWith|startContainer|getRangeAt|styleWithCSS|preventDefault|call|moveToElementText|Cut|addRule|getData|clipboardData|onpaste|onbeforepaste|onbeforedeactivate|className|insertAt|isPhantomString|toUpperCase|end|x7d|x7b|x2e|z1||sa|insertContentBeforeClosingTag|main1|remove_embeded_styles|remove_scripts|remove_comments|fromCharCode|String|gmi|extends|9830|diams|9829|hearts|9827|clubs|9824|spades|9674|loz|9002|rang|9001|8971|rfloor|8970|lfloor|8969|rceil|8968|lceil|8901|sdot|8869|perp|8855|otimes|8853|oplus|8839|supe|8838|sube|8836|nsub|8835|8834|8805|8804|8801|8800|8776||asymp|8773|cong||8764|sim|8756||there4|8747|int|8746|cup|8745|cap|8744|8743|and|8736|ang|8734|infin|8733|8730|radic|8727|lowast|8722|minus|8721|sum|8719|prod|8715|8713|notin|8712|isin|8711|nabla|8709|empty|8707|exist|8706|part|8704|forall|||||||8660|hArr|8659|dArr|8658|rArr|8657|uArr|8656|lArr|8629|crarr|8596|harr|8595|darr|8594|rarr|8593|uarr||8592|larr|8501|alefsym|8482|trade|8476|real|8472|weierp|8465|8364|euro|8260|frasl|8254|oline|8250|rsaquo|8249|lsaquo|8243|Prime|8242|prime|8240|permil|8230|hellip|8226|bull|8225|Dagger|8224|dagger|8222|bdquo|8221|rdquo|8220|ldquo|8218|sbquo|8217|rsquo|8216|lsquo||8212|||mdash|8211|ndash|8207|rlm|8206||lrm|8205|zwj|8204||zwnj|8201|thinsp|8195|emsp|8194|ensp|982|piv|978|upsih|977|thetasym|969|omega|968|psi|967|chi|966|phi|965|upsilon|964|tau|963|sigma|962|sigmaf|961|rho|960|959|omicron|958|xi|957|956|955|lambda|954|kappa|953|iota|952|theta|951|eta|950|zeta|949|epsilon|948|delta|947|gamma|946|beta|945|alpha|937||Omega|936|Psi|935|Chi|934|Phi|933|Upsilon|932|Tau|931|Sigma|929|Rho|928|Pi|927|Omicron|926|Xi|925|Nu|924|Mu|923|Lambda|922|Kappa|921|Iota|920|Theta|919|Eta|918|Zeta|917|Epsilon|916|Delta|915|Gamma|914|Beta|913|Alpha|732|tilde|710|402|fnof|376|Yuml|353|scaron|352|Scaron|339|oelig|338|OElig|255|yuml|254|thorn|253|yacute|252|uuml|251|ucirc|250|uacute|249|ugrave|248|oslash|247|divide|246|ouml|245|otilde|244|ocirc|243|oacute|242|ograve|241|ntilde|240|eth|239|iuml|238|icirc|237|iacute|236|igrave|235|euml|234|ecirc|233|eacute|232||egrave|231|ccedil||230|aelig|229|aring|228|auml|227|atilde|226|acirc|225|aacute|agrave|223|szlig|222|THORN|221|Yacute|220|Uuml|219|Ucirc|218|Uacute|217|Ugrave|216|Oslash|215|times|214|Ouml|213|Otilde|212|Ocirc|211|Oacute|210|Ograve|209|Ntilde|208|ETH|207|Iuml|206|Icirc|205|Iacute|204|Igrave|203|Euml|202|Ecirc|201|Eacute|200|Egrave||199|Ccedil|198|AElig|197|Aring|196|Auml|195|Atilde|194||Acirc|193|Aacute|192|Agrave|191|iquest|190|frac34|189|frac12|188|frac14|187|raquo|186|ordm|185|sup1|184|cedil|183|middot|182|para|181|micro|180|acute|179|sup3|178|sup2|177|plusmn|176|deg|175|macr|174|reg|173|shy|172|171||laquo|170|ordf||169|copy|168|uml|167||sect|166|brvbar||165||yen|164|curren|163|pound|162|cent|161|iexcl|last_tag_attributes|com|microsoft|schemas|urn|_current_match|_last_match|_matches|boolean|concat|events|continue|groups|none|rules|vsides|rhs|lhs|hsides|below|above|void|cellspacing|cellpadding|aural|handheld|tv|tty|vbs|vbscript|jscript|ecmascript|ref|standby|declare|codetype|codebase|classid|archive|scheme|cookie|set|refresh||expires||speech|radio|password||checkbox|maxlength|longdesc|ismap|xmlns|post|method|enctype|nohref|onmouseup|onmouseout|onmouseover|onmousemove|onmousedown|ondblclick|mouse|onkeypress|onkeydown|frameset|onblur|onselect|onreset|onsubmit|onchange|onunload|core|charAt|parseAttributes|039|quot|textContent|shift|formated_options|amp|CDATA|cdataSection|contentTag|insertCell|insertRow|createCaption|newCol|opener|wym_skin_|ownerDocument|computeCssPath|addRange|selectNodeContents|getTime|Date|toggle|write|parsing||replaceChild|removeAttr|removeChild|firstChild|ready|blur|wym_box_|safari|opera|mozilla|msie|wymeditors|Number_Of_Cols|Number_Of_Rows|Summary|Caption|Alternative_Text|dtd|strict|TR|w3|EN|Strict|XHTML|W3C|PUBLIC|DOCTYPE|yes|scrollbars|wymupdate|iframeSelector|classesSelector|containersSelector|toolsListSelector|toolsSelector|boxSelector|Source_Code|Status|wym_classes_|Classes|wym_containers_th|Table_Header|wym_containers_blockquote|Blockquote|wym_containers_pre|Preformatted|wym_containers_h6|Heading_6|wym_containers_h5|Heading_5|wym_containers_h4|Heading_4|wym_containers_h3|Heading_3|wym_containers_h2|Heading_2|wym_containers_h1|Heading_1|wym_containers_p|Paragraph|Containers|wym_tools_preview|wym_tools_html|wym_tools_paste|wym_tools_table|wym_tools_image|wym_tools_unlink|wym_tools_link|wym_tools_redo|wym_tools_undo|wym_tools_outdent|wym_tools_indent|wym_tools_unordered_list|Unordered_List|wym_tools_ordered_list|Ordered_List|wym_tools_subscript|wym_tools_superscript|wym_tools_emphasis|Emphasis|wym_tools_strong|Strong|Tools|parent|wymiframe|wym_wymeditor_link|wym_area_bottom|wym_area_main|wym_area_right|wym_area_left|wym_area_top|TEXT|ATTRIBUTE|ELEMENT|NODE|CURSOR|HOME|END|UNLINK|FormatBlock|InsertHTML|Wym_Dialog_Body|Wym_Dialog_Title|Wym_Status|Wym_Iframe|Wym_Html|Wym_Container_Class|Wym_Containers_Title|Wym_Container_Name|Wym_Containers_Items|Wym_Containers|Wym_Class_Title|Wym_Class_Name|Wym_Classes_Items|Wym_Classes|Wym_Tool_Class|Wym_Tool_Title|Wym_Tool_Name|Wym_Tools_Items||Wym_Tools|Wym_Logo|Wym_Direction|Wym_Jquery_Path|Wym_Iframe_Base_Path|Wym_Wym_Path|Wym_Css_Path|Wym_Base_Path|wym_index|Wym_Index|rc1|VERSION|profileEnd|trace|timeEnd|time|groupEnd|group|dirxml|assert|warn|info|debug|log|firebug'.split('|'),0,{}))
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/bg.js b/objectapp/static/objectapp/js/wymeditor/lang/bg.js
new file mode 100644
index 0000000..576bca5
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/bg.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['bg'] = {
+ Strong: 'Получер',
+ Emphasis: 'КурÑив',
+ Superscript: 'Горен индекÑ',
+ Subscript: 'Долен индекÑ',
+ Ordered_List: 'Подреден ÑпиÑък',
+ Unordered_List: 'Ðеподреден ÑпиÑък',
+ Indent: 'Блок навътре',
+ Outdent: 'Блок навън',
+ Undo: 'Стъпка назад',
+ Redo: 'Стъпка напред',
+ Link: 'Създай хипервръзка',
+ Unlink: 'Премахни хипервръзката',
+ Image: 'Изображение',
+ Table: 'Таблица',
+ HTML: 'HTML',
+ Paragraph: 'Ðбзац',
+ Heading_1: 'Заглавие 1',
+ Heading_2: 'Заглавие 2',
+ Heading_3: 'Заглавие 3',
+ Heading_4: 'Заглавие 4',
+ Heading_5: 'Заглавие 5',
+ Heading_6: 'Заглавие 6',
+ Preformatted: 'Преформатиран',
+ Blockquote: 'Цитат',
+ Table_Header: 'Заглавие на таблицата',
+ URL: 'URL',
+ Title: 'Заглавие',
+ Alternative_Text: 'Ðлтернативен текÑÑ‚',
+ Caption: 'Етикет',
+ Summary: 'Общо',
+ Number_Of_Rows: 'Брой редове',
+ Number_Of_Cols: 'Брой колони',
+ Submit: 'Изпрати',
+ Cancel: 'Отмени',
+ Choose: 'Затвори',
+ Preview: 'Предварителен преглед',
+ Paste_From_Word: 'Вмъкни от MS WORD',
+ Tools: 'ИнÑтрументи',
+ Containers: 'Контейнери',
+ Classes: 'КлаÑове',
+ Status: 'СтатуÑ',
+ Source_Code: 'Източник, код'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/ca.js b/objectapp/static/objectapp/js/wymeditor/lang/ca.js
new file mode 100644
index 0000000..c342406
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/ca.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['ca'] = {
+ Strong: 'Ressaltar',
+ Emphasis: 'Emfatitzar',
+ Superscript: 'Superindex',
+ Subscript: 'Subindex',
+ Ordered_List: 'Llistat ordenat',
+ Unordered_List: 'Llistat sense ordenar',
+ Indent: 'Indentat',
+ Outdent: 'Sense indentar',
+ Undo: 'Desfer',
+ Redo: 'Refer',
+ Link: 'Enllaçar',
+ Unlink: 'Eliminar enllaç',
+ Image: 'Imatge',
+ Table: 'Taula',
+ HTML: 'HTML',
+ Paragraph: 'Paràgraf',
+ Heading_1: 'Capçalera 1',
+ Heading_2: 'Capçalera 2',
+ Heading_3: 'Capçalera 3',
+ Heading_4: 'Capçalera 4',
+ Heading_5: 'Capçalera 5',
+ Heading_6: 'Capçalera 6',
+ Preformatted: 'Pre-formatejat',
+ Blockquote: 'Cita',
+ Table_Header: 'Capçalera de la taula',
+ URL: 'URL',
+ Title: 'Títol',
+ Alternative_Text: 'Text alternatiu',
+ Caption: 'Llegenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Nombre de files',
+ Number_Of_Cols: 'Nombre de columnes',
+ Submit: 'Enviar',
+ Cancel: 'Cancel·lar',
+ Choose: 'Triar',
+ Preview: 'Vista prèvia',
+ Paste_From_Word: 'Pegar des de Word',
+ Tools: 'Eines',
+ Containers: 'Contenidors',
+ Classes: 'Classes',
+ Status: 'Estat',
+ Source_Code: 'Codi font'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/cs.js b/objectapp/static/objectapp/js/wymeditor/lang/cs.js
new file mode 100644
index 0000000..3939d71
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/cs.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['cs'] = {
+ Strong: 'TuÄné',
+ Emphasis: 'Kurzíva',
+ Superscript: 'Horní index',
+ Subscript: 'Dolní index',
+ Ordered_List: 'Číslovaný seznam',
+ Unordered_List: 'NeÄíslovaný seznam',
+ Indent: 'Zvětšit odsazení',
+ Outdent: 'Zmenšit odsazení',
+ Undo: 'Zpět',
+ Redo: 'Znovu',
+ Link: 'Vytvořit odkaz',
+ Unlink: 'Zrušit odkaz',
+ Image: 'Obrázek',
+ Table: 'Tabulka',
+ HTML: 'HTML',
+ Paragraph: 'Odstavec',
+ Heading_1: 'Nadpis 1. úrovně',
+ Heading_2: 'Nadpis 2. úrovně',
+ Heading_3: 'Nadpis 3. úrovně',
+ Heading_4: 'Nadpis 4. úrovně',
+ Heading_5: 'Nadpis 5. úrovně',
+ Heading_6: 'Nadpis 6. úrovně',
+ Preformatted: 'Předformátovaný text',
+ Blockquote: 'Citace',
+ Table_Header: 'HlaviÄková buňka tabulky',
+ URL: 'Adresa',
+ Title: 'Text po najetí myší',
+ Alternative_Text: 'Text pro případ nezobrazení obrázku',
+ Caption: 'Titulek tabulky',
+ Summary: 'Shrnutí obsahu',
+ Number_Of_Rows: 'PoÄet řádek',
+ Number_Of_Cols: 'PoÄet sloupců',
+ Submit: 'Vytvořit',
+ Cancel: 'Zrušit',
+ Choose: 'Vybrat',
+ Preview: 'Náhled',
+ Paste_From_Word: 'Vložit z Wordu',
+ Tools: 'Nástroje',
+ Containers: 'Typy obsahu',
+ Classes: 'Třídy',
+ Status: 'Stav',
+ Source_Code: 'Zdrojový kód'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/cy.js b/objectapp/static/objectapp/js/wymeditor/lang/cy.js
new file mode 100644
index 0000000..7d15b79
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/cy.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['cy'] = {
+ Strong: 'Bras',
+ Emphasis: 'Italig',
+ Superscript: 'Uwchsgript',
+ Subscript: 'Is-sgript',
+ Ordered_List: 'Rhestr mewn Trefn',
+ Unordered_List: 'Pwyntiau Bwled',
+ Indent: 'Mewnoli',
+ Outdent: 'Alloli',
+ Undo: 'Dadwneud',
+ Redo: 'Ailwneud',
+ Link: 'Cysylltu',
+ Unlink: 'Datgysylltu',
+ Image: 'Delwedd',
+ Table: 'Tabl',
+ HTML: 'HTML',
+ Paragraph: 'Paragraff',
+ Heading_1: 'Pennawd 1',
+ Heading_2: 'Pennawd 2',
+ Heading_3: 'Pennawd 3',
+ Heading_4: 'Pennawd 4',
+ Heading_5: 'Pennawd 5',
+ Heading_6: 'Pennawd 6',
+ Preformatted: 'Rhagfformat',
+ Blockquote: 'Bloc Dyfyniad',
+ Table_Header: 'Pennyn Tabl',
+ URL: 'URL',
+ Title: 'Teitl',
+ Alternative_Text: 'Testun Amgen',
+ Caption: 'Pennawd',
+ Summary: 'Crynodeb',
+ Number_Of_Rows: 'Nifer y rhesi',
+ Number_Of_Cols: 'Nifer y colofnau',
+ Submit: 'Anfon',
+ Cancel: 'Diddymu',
+ Choose: 'Dewis',
+ Preview: 'Rhagolwg',
+ Paste_From_Word: 'Gludo o Word',
+ Tools: 'Offer',
+ Containers: 'Cynhwysyddion',
+ Classes: 'Dosbarthiadau',
+ Status: 'Statws',
+ Source_Code: 'Cod ffynhonnell'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/de.js b/objectapp/static/objectapp/js/wymeditor/lang/de.js
new file mode 100644
index 0000000..a1e01e1
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/de.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['de'] = {
+ Strong: 'Fett',
+ Emphasis: 'Kursiv',
+ Superscript: 'Text hochstellen',
+ Subscript: 'Text tiefstellen',
+ Ordered_List: 'Geordnete Liste einfügen',
+ Unordered_List: 'Ungeordnete Liste einfügen',
+ Indent: 'Einzug erhöhen',
+ Outdent: 'Einzug vermindern',
+ Undo: 'Befehle rückgängig machen',
+ Redo: 'Befehle wiederherstellen',
+ Link: 'Hyperlink einfügen',
+ Unlink: 'Hyperlink entfernen',
+ Image: 'Bild einfügen',
+ Table: 'Tabelle einfügen',
+ HTML: 'HTML anzeigen/verstecken',
+ Paragraph: 'Absatz',
+ Heading_1: 'Ãœberschrift 1',
+ Heading_2: 'Ãœberschrift 2',
+ Heading_3: 'Ãœberschrift 3',
+ Heading_4: 'Ãœberschrift 4',
+ Heading_5: 'Ãœberschrift 5',
+ Heading_6: 'Ãœberschrift 6',
+ Preformatted: 'Vorformatiert',
+ Blockquote: 'Zitat',
+ Table_Header: 'Tabellenüberschrift',
+ URL: 'URL',
+ Title: 'Titel',
+ Alternative_Text: 'Alternativer Text',
+ Caption: 'Tabellenüberschrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Anzahl Zeilen',
+ Number_Of_Cols: 'Anzahl Spalten',
+ Submit: 'Absenden',
+ Cancel: 'Abbrechen',
+ Choose: 'Auswählen',
+ Preview: 'Vorschau',
+ Paste_From_Word: 'Aus Word einfügen',
+ Tools: 'Werkzeuge',
+ Containers: 'Inhaltstyp',
+ Classes: 'Klassen',
+ Status: 'Status',
+ Source_Code: 'Quellcode'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/en.js b/objectapp/static/objectapp/js/wymeditor/lang/en.js
new file mode 100644
index 0000000..1e351e5
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/en.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['en'] = {
+ Strong: 'Strong',
+ Emphasis: 'Emphasis',
+ Superscript: 'Superscript',
+ Subscript: 'Subscript',
+ Ordered_List: 'Ordered List',
+ Unordered_List: 'Unordered List',
+ Indent: 'Indent',
+ Outdent: 'Outdent',
+ Undo: 'Undo',
+ Redo: 'Redo',
+ Link: 'Link',
+ Unlink: 'Unlink',
+ Image: 'Image',
+ Table: 'Table',
+ HTML: 'HTML',
+ Paragraph: 'Paragraph',
+ Heading_1: 'Heading 1',
+ Heading_2: 'Heading 2',
+ Heading_3: 'Heading 3',
+ Heading_4: 'Heading 4',
+ Heading_5: 'Heading 5',
+ Heading_6: 'Heading 6',
+ Preformatted: 'Preformatted',
+ Blockquote: 'Blockquote',
+ Table_Header: 'Table Header',
+ URL: 'URL',
+ Title: 'Title',
+ Alternative_Text: 'Alternative text',
+ Caption: 'Caption',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Number of rows',
+ Number_Of_Cols: 'Number of cols',
+ Submit: 'Submit',
+ Cancel: 'Cancel',
+ Choose: 'Choose',
+ Preview: 'Preview',
+ Paste_From_Word: 'Paste from Word',
+ Tools: 'Tools',
+ Containers: 'Containers',
+ Classes: 'Classes',
+ Status: 'Status',
+ Source_Code: 'Source code'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/es.js b/objectapp/static/objectapp/js/wymeditor/lang/es.js
new file mode 100644
index 0000000..cdb03c1
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/es.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['es'] = {
+ Strong: 'Resaltar',
+ Emphasis: 'Enfatizar',
+ Superscript: 'Superindice',
+ Subscript: 'Subindice',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista sin ordenar',
+ Indent: 'Indentado',
+ Outdent: 'Sin indentar',
+ Undo: 'Deshacer',
+ Redo: 'Rehacer',
+ Link: 'Enlazar',
+ Unlink: 'Eliminar enlace',
+ Image: 'Imagen',
+ Table: 'Tabla',
+ HTML: 'HTML',
+ Paragraph: 'Párrafo',
+ Heading_1: 'Cabecera 1',
+ Heading_2: 'Cabecera 2',
+ Heading_3: 'Cabecera 3',
+ Heading_4: 'Cabecera 4',
+ Heading_5: 'Cabecera 5',
+ Heading_6: 'Cabecera 6',
+ Preformatted: 'Preformateado',
+ Blockquote: 'Cita',
+ Table_Header: 'Cabecera de la tabla',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Leyenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de filas',
+ Number_Of_Cols: 'Número de columnas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Seleccionar',
+ Preview: 'Vista previa',
+ Paste_From_Word: 'Pegar desde Word',
+ Tools: 'Herramientas',
+ Containers: 'Contenedores',
+ Classes: 'Clases',
+ Status: 'Estado',
+ Source_Code: 'Código fuente'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/fa.js b/objectapp/static/objectapp/js/wymeditor/lang/fa.js
new file mode 100644
index 0000000..9d70fcb
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/fa.js
@@ -0,0 +1,46 @@
+//Translation To Persian: Ghassem Tofighi (http://ght.ir)
+WYMeditor.STRINGS['fa'] = {
+ Strong: 'پررنگ',//Strong
+ Emphasis: 'ایتالیک',//Emphasis
+ Superscript: 'بالانويس‌ ',//Superscript
+ Subscript: 'زيرنويس‌',//Subscript
+ Ordered_List: 'لیست مرتب',//Ordered List
+ Unordered_List: 'لیست نامرتب',//Unordered List
+ Indent: 'اÙزودن دندانه',//Indent
+ Outdent: 'کاهش دندانه',//Outdent
+ Undo: 'واگردانی',//Undo
+ Redo: 'تکرار',//Redo
+ Link: 'ساختن پیوند',//Link
+ Unlink: 'برداشتن پیوند',//Unlink
+ Image: 'تصویر',//Image
+ Table: 'جدول',//Table
+ HTML: 'HTML',//HTML
+ Paragraph: 'پاراگراÙ',//Paragraph
+ Heading_1: 'سرتیتر ۱',//Heading 1
+ Heading_2: 'سرتیتر ۲',//Heading 2
+ Heading_3: 'سرتیتر ۳',//Heading 3
+ Heading_4: 'سرتیتر ۴',//Heading 4
+ Heading_5: 'سرتیتر ۵',//Heading 5
+ Heading_6: 'سرتیتر ۶',//Heading 6
+ Preformatted: 'قالب آماده',//Preformatted
+ Blockquote: 'نقل قول',//Blockquote
+ Table_Header: 'سرجدول',//Table Header
+ URL: 'آدرس اینترنتی',//URL
+ Title: 'عنوان',//Title
+ Alternative_Text: 'متن جایگزین',//Alternative text
+ Caption: 'عنوان',//Caption
+ Summary: 'Summary',
+ Number_Of_Rows: 'تعداد سطرها',//Number of rows
+ Number_Of_Cols: 'تعداد ستون‌ها',//Number of cols
+ Submit: 'Ùرستادن',//Submit
+ Cancel: 'لغو',//Cancel
+ Choose: 'انتخاب',//Choose
+ Preview: 'پیش‌نمایش',//Preview
+ Paste_From_Word: 'انتقال از ورد',//Paste from Word
+ Tools: 'ابزار',//Tools
+ Containers: '‌قالب‌ها',//Containers
+ Classes: 'کلاس‌ها',//Classes
+ Status: 'وضعیت',//Status
+ Source_Code: 'کد مبدأ'//Source code
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/fi.js b/objectapp/static/objectapp/js/wymeditor/lang/fi.js
new file mode 100644
index 0000000..fe1eab4
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/fi.js
@@ -0,0 +1,44 @@
+WYMeditor.STRINGS['fi'] = {
+ Strong: 'Lihavoitu',
+ Emphasis: 'Korostus',
+ Superscript: 'Yläindeksi',
+ Subscript: 'Alaindeksi',
+ Ordered_List: 'Numeroitu lista',
+ Unordered_List: 'Luettelomerkit',
+ Indent: 'Suurenna sisennystä',
+ Outdent: 'Pienennä sisennystä',
+ Undo: 'Kumoa',
+ Redo: 'Toista',
+ Link: 'Linkitä',
+ Unlink: 'Poista linkitys',
+ Image: 'Kuva',
+ Table: 'Taulukko',
+ HTML: 'HTML',
+ Paragraph: 'Kappale',
+ Heading_1: 'Otsikko 1',
+ Heading_2: 'Otsikko 2',
+ Heading_3: 'Otsikko 3',
+ Heading_4: 'Otsikko 4',
+ Heading_5: 'Otsikko 5',
+ Heading_6: 'Otsikko 6',
+ Preformatted: 'Esimuotoilu',
+ Blockquote: 'Sitaatti',
+ Table_Header: 'Taulukon otsikko',
+ URL: 'URL',
+ Title: 'Otsikko',
+ Alternative_Text: 'Vaihtoehtoinen teksti',
+ Caption: 'Kuvateksti',
+ Summary: 'Yhteenveto',
+ Number_Of_Rows: 'Rivien määrä',
+ Number_Of_Cols: 'Palstojen määrä',
+ Submit: 'Lähetä',
+ Cancel: 'Peruuta',
+ Choose: 'Valitse',
+ Preview: 'Esikatsele',
+ Paste_From_Word: 'Tuo Wordista',
+ Tools: 'Työkalut',
+ Containers: 'Muotoilut',
+ Classes: 'Luokat',
+ Status: 'Tila',
+ Source_Code: 'Lähdekoodi'
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/fr.js b/objectapp/static/objectapp/js/wymeditor/lang/fr.js
new file mode 100644
index 0000000..9b6deb9
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/fr.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['fr'] = {
+ Strong: 'Mise en évidence',
+ Emphasis: 'Emphase',
+ Superscript: 'Exposant',
+ Subscript: 'Indice',
+ Ordered_List: 'Liste Ordonnée',
+ Unordered_List: 'Liste Non-Ordonnée',
+ Indent: 'Imbriqué',
+ Outdent: 'Non-imbriqué',
+ Undo: 'Annuler',
+ Redo: 'Rétablir',
+ Link: 'Lien',
+ Unlink: 'Supprimer le Lien',
+ Image: 'Image',
+ Table: 'Tableau',
+ HTML: 'HTML',
+ Paragraph: 'Paragraphe',
+ Heading_1: 'Titre 1',
+ Heading_2: 'Titre 2',
+ Heading_3: 'Titre 3',
+ Heading_4: 'Titre 4',
+ Heading_5: 'Titre 5',
+ Heading_6: 'Titre 6',
+ Preformatted: 'Pré-formatté',
+ Blockquote: 'Citation',
+ Table_Header: 'Cellule de titre',
+ URL: 'URL',
+ Title: 'Titre',
+ Alternative_Text: 'Texte alternatif',
+ Caption: 'Légende',
+ Summary: 'Résumé',
+ Number_Of_Rows: 'Nombre de lignes',
+ Number_Of_Cols: 'Nombre de colonnes',
+ Submit: 'Envoyer',
+ Cancel: 'Annuler',
+ Choose: 'Choisir',
+ Preview: 'Prévisualisation',
+ Paste_From_Word: 'Copier depuis Word',
+ Tools: 'Outils',
+ Containers: 'Type de texte',
+ Classes: 'Type de contenu',
+ Status: 'Infos',
+ Source_Code: 'Code source'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/gl.js b/objectapp/static/objectapp/js/wymeditor/lang/gl.js
new file mode 100644
index 0000000..d4786b8
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/gl.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['gl'] = {
+ Strong: 'Moita énfase',
+ Emphasis: 'Énfase',
+ Superscript: 'Superíndice',
+ Subscript: 'Subíndice',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista sen ordenar',
+ Indent: 'Aniñar',
+ Outdent: 'Desaniñar',
+ Undo: 'Desfacer',
+ Redo: 'Refacer',
+ Link: 'Ligazón',
+ Unlink: 'Desligar',
+ Image: 'Imaxe',
+ Table: 'Táboa',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Preformatado',
+ Blockquote: 'Cita en parágrafo',
+ Table_Header: 'Cabeceira da táboa',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Título',
+ Summary: 'Resumo',
+ Number_Of_Rows: 'Número de filas',
+ Number_Of_Cols: 'Número de columnas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Escoller',
+ Preview: 'Previsualizar',
+ Paste_From_Word: 'Colar dende Word',
+ Tools: 'Ferramentas',
+ Containers: 'Contenedores',
+ Classes: 'Clases',
+ Status: 'Estado',
+ Source_Code: 'Código fonte'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/he.js b/objectapp/static/objectapp/js/wymeditor/lang/he.js
new file mode 100644
index 0000000..97c9675
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/he.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['he'] = {
+ Strong: 'חזק',
+ Emphasis: 'מובלט',
+ Superscript: 'כתב עילי',
+ Subscript: 'כתב תחתי',
+ Ordered_List: 'רשימה ממוספרת',
+ Unordered_List: 'רשימה ×œ× ×ž×ž×•×¡×¤×¨×ª',
+ Indent: 'הזחה פנימה',
+ Outdent: 'הזחה החוצה',
+ Undo: 'בטל פעולה',
+ Redo: 'בצע מחדש פעולה',
+ Link: 'קישור',
+ Unlink: 'בטל קישור',
+ Image: 'תמונה',
+ Table: 'טבלה',
+ HTML: 'קוד HTML',
+ Paragraph: 'פסקה',
+ Heading_1: 'כותרת 1 ; תג &lt;h1&gt;',
+ Heading_2: 'כותרת 2 ; תג &lt;h2&gt;',
+ Heading_3: 'כותרת 3 ; תג &lt;h3&gt;',
+ Heading_4: 'כותרת 4 ; תג &lt;h4&gt;',
+ Heading_5: 'כותרת 5 ; תג &lt;h5&gt;',
+ Heading_6: 'כותרת 6 ; תג &lt;h6&gt;',
+ Preformatted: 'משמר רווחי×',
+ Blockquote: 'ציטוט',
+ Table_Header: 'כותרת טבלה',
+ URL: 'קישור (URL)',
+ Title: 'כותרת',
+ Alternative_Text: 'טקסט חלופי',
+ Caption: 'כותרת',
+ Summary: 'סיכו×',
+ Number_Of_Rows: 'מספר שורות',
+ Number_Of_Cols: 'מספר טורי×',
+ Submit: 'שלח',
+ Cancel: 'בטל',
+ Choose: 'בחר',
+ Preview: 'תצוגה מקדימה',
+ Paste_From_Word: 'העתק מ-Word',
+ Tools: 'כלי×',
+ Containers: 'מיכלי×',
+ Classes: 'מחלקות',
+ Status: 'מצב',
+ Source_Code: 'קוד מקור'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/hr.js b/objectapp/static/objectapp/js/wymeditor/lang/hr.js
new file mode 100644
index 0000000..193e31a
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/hr.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['hr'] = {
+ Strong: 'Podebljano',
+ Emphasis: 'Naglašeno',
+ Superscript: 'Iznad',
+ Subscript: 'Ispod',
+ Ordered_List: 'Pobrojana lista',
+ Unordered_List: 'Nepobrojana lista',
+ Indent: 'Uvuci',
+ Outdent: 'Izvuci',
+ Undo: 'Poništi promjenu',
+ Redo: 'Ponovno promjeni',
+ Link: 'Hiperveza',
+ Unlink: 'Ukloni hipervezu',
+ Image: 'Slika',
+ Table: 'Tablica',
+ HTML: 'HTML',
+ Paragraph: 'Paragraf',
+ Heading_1: 'Naslov 1',
+ Heading_2: 'Naslov 2',
+ Heading_3: 'Naslov 3',
+ Heading_4: 'Naslov 4',
+ Heading_5: 'Naslov 5',
+ Heading_6: 'Naslov 6',
+ Preformatted: 'Unaprijed formatirano',
+ Blockquote: 'Citat',
+ Table_Header: 'Zaglavlje tablice',
+ URL: 'URL',
+ Title: 'Naslov',
+ Alternative_Text: 'Alternativni tekst',
+ Caption: 'Zaglavlje',
+ Summary: 'Sažetak',
+ Number_Of_Rows: 'Broj redova',
+ Number_Of_Cols: 'Broj kolona',
+ Submit: 'Snimi',
+ Cancel: 'Odustani',
+ Choose: 'Izaberi',
+ Preview: 'Pregled',
+ Paste_From_Word: 'Zalijepi iz Word-a',
+ Tools: 'Alati',
+ Containers: 'Kontejneri',
+ Classes: 'Klase',
+ Status: 'Status',
+ Source_Code: 'Izvorni kod'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/hu.js b/objectapp/static/objectapp/js/wymeditor/lang/hu.js
new file mode 100644
index 0000000..a8cdbc6
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/hu.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['hu'] = {
+ Strong: 'Félkövér',
+ Emphasis: 'Kiemelt',
+ Superscript: 'Felső index',
+ Subscript: 'Alsó index',
+ Ordered_List: 'Rendezett lista',
+ Unordered_List: 'Rendezetlen lista',
+ Indent: 'Bekezdés',
+ Outdent: 'Bekezdés törlése',
+ Undo: 'Visszavon',
+ Redo: 'Visszaállít',
+ Link: 'Link',
+ Unlink: 'Link törlése',
+ Image: 'Kép',
+ Table: 'Tábla',
+ HTML: 'HTML',
+ Paragraph: 'Bekezdés',
+ Heading_1: 'Címsor 1',
+ Heading_2: 'Címsor 2',
+ Heading_3: 'Címsor 3',
+ Heading_4: 'Címsor 4',
+ Heading_5: 'Címsor 5',
+ Heading_6: 'Címsor 6',
+ Preformatted: 'Előformázott',
+ Blockquote: 'Idézet',
+ Table_Header: 'Tábla Fejléc',
+ URL: 'Webcím',
+ Title: 'Megnevezés',
+ Alternative_Text: 'Alternatív szöveg',
+ Caption: 'Fejléc',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Sorok száma',
+ Number_Of_Cols: 'Oszlopok száma',
+ Submit: 'Elküld',
+ Cancel: 'Mégsem',
+ Choose: 'Választ',
+ Preview: 'Előnézet',
+ Paste_From_Word: 'Másolás Word-ból',
+ Tools: 'Eszközök',
+ Containers: 'Tartalmak',
+ Classes: 'Osztályok',
+ Status: 'Ãllapot',
+ Source_Code: 'Forráskód'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/it.js b/objectapp/static/objectapp/js/wymeditor/lang/it.js
new file mode 100644
index 0000000..ca632a9
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/it.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['it'] = {
+ Strong: 'Grassetto',
+ Emphasis: 'Corsetto',
+ Superscript: 'Apice',
+ Subscript: 'Pedice',
+ Ordered_List: 'Lista Ordinata',
+ Unordered_List: 'Lista Puntata',
+ Indent: 'Indenta',
+ Outdent: 'Caccia',
+ Undo: 'Indietro',
+ Redo: 'Avanti',
+ Link: 'Inserisci Link',
+ Unlink: 'Togli Link',
+ Image: 'Inserisci Immagine',
+ Table: 'Inserisci Tabella',
+ HTML: 'HTML',
+ Paragraph: 'Paragrafo',
+ Heading_1: 'Heading 1',
+ Heading_2: 'Heading 2',
+ Heading_3: 'Heading 3',
+ Heading_4: 'Heading 4',
+ Heading_5: 'Heading 5',
+ Heading_6: 'Heading 6',
+ Preformatted: 'Preformattato',
+ Blockquote: 'Blockquote',
+ Table_Header: 'Header Tabella',
+ URL: 'Indirizzo',
+ Title: 'Titolo',
+ Alternative_Text: 'Testo Alternativo',
+ Caption: 'Caption',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Numero di Righe',
+ Number_Of_Cols: 'Numero di Colonne',
+ Submit: 'Invia',
+ Cancel: 'Cancella',
+ Choose: 'Scegli',
+ Preview: 'Anteprima',
+ Paste_From_Word: 'Incolla',
+ Tools: 'Tools',
+ Containers: 'Contenitori',
+ Classes: 'Classi',
+ Status: 'Stato',
+ Source_Code: 'Codice Sorgente'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/nb.js b/objectapp/static/objectapp/js/wymeditor/lang/nb.js
new file mode 100644
index 0000000..7573b78
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/nb.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nb'] = {
+ Strong: 'Fet',
+ Emphasis: 'Uthevet',
+ Superscript: 'Opphøyet',
+ Subscript: 'Nedsenket',
+ Ordered_List: 'Nummerert liste',
+ Unordered_List: 'Punktliste',
+ Indent: 'Rykk inn',
+ Outdent: 'Rykk ut',
+ Undo: 'Angre',
+ Redo: 'Gjenta',
+ Link: 'Lenke',
+ Unlink: 'Ta bort lenken',
+ Image: 'Bilde',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Avsnitt',
+ Heading_1: 'Overskrift 1',
+ Heading_2: 'Overskrift 2',
+ Heading_3: 'Overskrift 3',
+ Heading_4: 'Overskrift 4',
+ Heading_5: 'Overskrift 5',
+ Heading_6: 'Overskrift 6',
+ Preformatted: 'Preformatert',
+ Blockquote: 'Sitat',
+ Table_Header: 'Tabelloverskrift',
+ URL: 'URL',
+ Title: 'Tittel',
+ Alternative_Text: 'Alternativ tekst',
+ Caption: 'Overskrift',
+ Summary: 'Sammendrag',
+ Number_Of_Rows: 'Antall rader',
+ Number_Of_Cols: 'Antall kolonner',
+ Submit: 'Ok',
+ Cancel: 'Avbryt',
+ Choose: 'Velg',
+ Preview: 'Forhåndsvis',
+ Paste_From_Word: 'Lim inn fra Word',
+ Tools: 'Verktøy',
+ Containers: 'Formatering',
+ Classes: 'Klasser',
+ Status: 'Status',
+ Source_Code: 'Kildekode'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/nl.js b/objectapp/static/objectapp/js/wymeditor/lang/nl.js
new file mode 100644
index 0000000..cdfa21c
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/nl.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nl'] = {
+ Strong: 'Sterk benadrukken',
+ Emphasis: 'Benadrukken',
+ Superscript: 'Bovenschrift',
+ Subscript: 'Onderschrift',
+ Ordered_List: 'Geordende lijst',
+ Unordered_List: 'Ongeordende lijst',
+ Indent: 'Inspringen',
+ Outdent: 'Terugspringen',
+ Undo: 'Ongedaan maken',
+ Redo: 'Opnieuw uitvoeren',
+ Link: 'Linken',
+ Unlink: 'Ontlinken',
+ Image: 'Afbeelding',
+ Table: 'Tabel',
+ HTML: 'HTML',
+ Paragraph: 'Paragraaf',
+ Heading_1: 'Kop 1',
+ Heading_2: 'Kop 2',
+ Heading_3: 'Kop 3',
+ Heading_4: 'Kop 4',
+ Heading_5: 'Kop 5',
+ Heading_6: 'Kop 6',
+ Preformatted: 'Voorgeformatteerd',
+ Blockquote: 'Citaat',
+ Table_Header: 'Tabel-kop',
+ URL: 'URL',
+ Title: 'Titel',
+ Alternative_Text: 'Alternatieve tekst',
+ Caption: 'Bijschrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Aantal rijen',
+ Number_Of_Cols: 'Aantal kolommen',
+ Submit: 'Versturen',
+ Cancel: 'Annuleren',
+ Choose: 'Kiezen',
+ Preview: 'Voorbeeld bekijken',
+ Paste_From_Word: 'Plakken uit Word',
+ Tools: 'Hulpmiddelen',
+ Containers: 'Teksttypes',
+ Classes: 'Klassen',
+ Status: 'Status',
+ Source_Code: 'Broncode'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/nn.js b/objectapp/static/objectapp/js/wymeditor/lang/nn.js
new file mode 100644
index 0000000..51cec2b
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/nn.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nn'] = {
+ Strong: 'Feit',
+ Emphasis: 'Utheva',
+ Superscript: 'Opphøgd',
+ Subscript: 'Nedsenka',
+ Ordered_List: 'Nummerert liste',
+ Unordered_List: 'Punktliste',
+ Indent: 'Rykk inn',
+ Outdent: 'Rykk ut',
+ Undo: 'Angre',
+ Redo: 'Gjentaka',
+ Link: 'Lenkje',
+ Unlink: 'Ta bort lenkja',
+ Image: 'Bilete',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Avsnitt',
+ Heading_1: 'Overskrift 1',
+ Heading_2: 'Overskrift 2',
+ Heading_3: 'Overskrift 3',
+ Heading_4: 'Overskrift 4',
+ Heading_5: 'Overskrift 5',
+ Heading_6: 'Overskrift 6',
+ Preformatted: 'Preformatert',
+ Blockquote: 'Sitat',
+ Table_Header: 'Tabelloverskrift',
+ URL: 'URL',
+ Title: 'Tittel',
+ Alternative_Text: 'Alternativ tekst',
+ Caption: 'Overskrift',
+ Summary: 'Samandrag',
+ Number_Of_Rows: 'Tal på rader',
+ Number_Of_Cols: 'Tal på kolonnar',
+ Submit: 'Ok',
+ Cancel: 'Avbryt',
+ Choose: 'Vel',
+ Preview: 'Førehandsvis',
+ Paste_From_Word: 'Lim inn frå Word',
+ Tools: 'Verkty',
+ Containers: 'Formatering',
+ Classes: 'Klassar',
+ Status: 'Status',
+ Source_Code: 'Kjeldekode'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/pl.js b/objectapp/static/objectapp/js/wymeditor/lang/pl.js
new file mode 100644
index 0000000..d6c0471
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/pl.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pl'] = {
+ Strong: 'Nacisk',
+ Emphasis: 'Emfaza',
+ Superscript: 'Indeks górny',
+ Subscript: 'Indeks dolny',
+ Ordered_List: 'Lista numerowana',
+ Unordered_List: 'Lista wypunktowana',
+ Indent: 'Zwiększ wcięcie',
+ Outdent: 'Zmniejsz wcięcie',
+ Undo: 'Cofnij',
+ Redo: 'Ponów',
+ Link: 'Wstaw link',
+ Unlink: 'Usuń link',
+ Image: 'Obraz',
+ Table: 'Tabela',
+ HTML: 'Źródło HTML',
+ Paragraph: 'Akapit',
+ Heading_1: 'Nagłówek 1',
+ Heading_2: 'Nagłówek 2',
+ Heading_3: 'Nagłówek 3',
+ Heading_4: 'Nagłówek 4',
+ Heading_5: 'Nagłówek 5',
+ Heading_6: 'Nagłówek 6',
+ Preformatted: 'Preformatowany',
+ Blockquote: 'Cytat blokowy',
+ Table_Header: 'Nagłówek tabeli',
+ URL: 'URL',
+ Title: 'Tytuł',
+ Alternative_Text: 'Tekst alternatywny',
+ Caption: 'Tytuł tabeli',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Liczba wierszy',
+ Number_Of_Cols: 'Liczba kolumn',
+ Submit: 'Wyślij',
+ Cancel: 'Anuluj',
+ Choose: 'Wybierz',
+ Preview: 'PodglÄ…d',
+ Paste_From_Word: 'Wklej z Worda',
+ Tools: 'Narzędzia',
+ Containers: 'Format',
+ Classes: 'Styl',
+ Status: 'Status',
+ Source_Code: 'Kod źródłowy'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/pt-br.js b/objectapp/static/objectapp/js/wymeditor/lang/pt-br.js
new file mode 100644
index 0000000..2ec18fe
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/pt-br.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pt-br'] = {
+ Strong: 'Resaltar',
+ Emphasis: 'Enfatizar',
+ Superscript: 'Sobre escrito',
+ Subscript: 'Sub escrito ',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista desordenada',
+ Indent: 'Indentado',
+ Outdent: 'Desidentar',
+ Undo: 'Desfazer',
+ Redo: 'Refazer',
+ Link: 'Link',
+ Unlink: 'Remover Link',
+ Image: 'Imagem',
+ Table: 'Tabela',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Preformatado',
+ Blockquote: 'Citação',
+ Table_Header: 'Título de tabela',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Legenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de linhas',
+ Number_Of_Cols: 'Número de colunas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Selecionar',
+ Preview: 'Previsualizar',
+ Paste_From_Word: 'Copiar do Word',
+ Tools: 'Ferramentas',
+ Containers: 'Conteneiners',
+ Classes: 'Classes',
+ Status: 'Estado',
+ Source_Code: 'Código fonte'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/pt.js b/objectapp/static/objectapp/js/wymeditor/lang/pt.js
new file mode 100644
index 0000000..a3d1a17
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/pt.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pt'] = {
+ Strong: 'Negrito',
+ Emphasis: 'Itálico',
+ Superscript: 'Sobrescrito',
+ Subscript: 'Subsescrito',
+ Ordered_List: 'Lista Numerada',
+ Unordered_List: 'Lista Marcada',
+ Indent: 'Aumentar Indentaçã',
+ Outdent: 'Diminuir Indentaçã',
+ Undo: 'Desfazer',
+ Redo: 'Restaurar',
+ Link: 'Link',
+ Unlink: 'Tirar link',
+ Image: 'Imagem',
+ Table: 'Tabela',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Pré-formatado',
+ Blockquote: 'Citação',
+ Table_Header: 'Cabeçalho Tabela',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto Alterativo',
+ Caption: 'Título Tabela',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de Linhas',
+ Number_Of_Cols: 'Número de Colunas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Escolha',
+ Preview: 'Prever',
+ Paste_From_Word: 'Colar do Word',
+ Tools: 'Ferramentas',
+ Containers: 'Containers',
+ Classes: 'Classes',
+ Status: 'Status',
+ Source_Code: 'Código Fonte'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/ru.js b/objectapp/static/objectapp/js/wymeditor/lang/ru.js
new file mode 100644
index 0000000..7895f8d
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/ru.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['ru'] = {
+ Strong: 'Жирный',
+ Emphasis: 'Ðаклонный',
+ Superscript: 'ÐадÑтрочный',
+ Subscript: 'ПодÑтрочный',
+ Ordered_List: 'Ðумерованый ÑпиÑок',
+ Unordered_List: 'Ðенумерованый ÑпиÑок',
+ Indent: 'Увеличить отÑтуп',
+ Outdent: 'Уменьшить отÑтуп',
+ Undo: 'Отменить',
+ Redo: 'Повторить',
+ Link: 'СÑылка',
+ Unlink: 'Удалить ÑÑылку',
+ Image: 'Изображение',
+ Table: 'Таблица',
+ HTML: 'Править HTML',
+ Paragraph: 'Параграф',
+ Heading_1: 'Заголовок 1',
+ Heading_2: 'Заголовок 2',
+ Heading_3: 'Заголовок 3',
+ Heading_4: 'Заголовок 4',
+ Heading_5: 'Заголовок 5',
+ Heading_6: 'Заголовок 6',
+ Preformatted: 'Preformatted',
+ Blockquote: 'Цитата',
+ Table_Header: 'Заголовок таблицы',
+ URL: 'URL',
+ Title: 'Заголовок',
+ Alternative_Text: 'Ðльтернативный текÑÑ‚',
+ Caption: 'ÐадпиÑÑŒ',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Кол-во Ñтрок',
+ Number_Of_Cols: 'Кол-во Ñтолбцов',
+ Submit: 'Отправить',
+ Cancel: 'Отмена',
+ Choose: 'Выбор',
+ Preview: 'ПроÑмотр',
+ Paste_From_Word: 'Ð’Ñтавить из Word',
+ Tools: 'ИнÑтрументы',
+ Containers: 'Контейнеры',
+ Classes: 'КлаÑÑÑ‹',
+ Status: 'СтатуÑ',
+ Source_Code: 'ИÑходный код'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/sv.js b/objectapp/static/objectapp/js/wymeditor/lang/sv.js
new file mode 100644
index 0000000..bc5485c
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/sv.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['sv'] = {
+ Strong: 'Viktigt',
+ Emphasis: 'Betoning',
+ Superscript: 'Upphöjt',
+ Subscript: 'Nedsänkt',
+ Ordered_List: 'Nummerlista',
+ Unordered_List: 'Punktlista',
+ Indent: 'Indrag',
+ Outdent: 'Utdrag',
+ Undo: 'Ã…ngra',
+ Redo: 'Gör om',
+ Link: 'Länk',
+ Unlink: 'Ta bort länk',
+ Image: 'Bild',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Paragraf',
+ Heading_1: 'Rubrik 1',
+ Heading_2: 'Rubrik 2',
+ Heading_3: 'Rubrik 3',
+ Heading_4: 'Rubrik 4',
+ Heading_5: 'Rubrik 5',
+ Heading_6: 'Rubrik 6',
+ Preformatted: 'Förformaterad',
+ Blockquote: 'Blockcitat',
+ Table_Header: 'Tabellrubrik',
+ URL: 'URL',
+ Title: 'Titel',
+ Alternative_Text: 'Alternativ text',
+ Caption: 'Överskrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Antal rader',
+ Number_Of_Cols: 'Antal kolumner',
+ Submit: 'Skicka',
+ Cancel: 'Avbryt',
+ Choose: 'Välj',
+ Preview: 'Förhandsgranska',
+ Paste_From_Word: 'Klistra in från Word',
+ Tools: 'Verktyg',
+ Containers: 'Formatering',
+ Classes: 'Klasser',
+ Status: 'Status',
+ Source_Code: 'Källkod'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/tr.js b/objectapp/static/objectapp/js/wymeditor/lang/tr.js
new file mode 100644
index 0000000..d26f0ff
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/tr.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['tr'] = {
+ Strong: 'Kalın',
+ Emphasis: 'Vurgu',
+ Superscript: 'Superscript',
+ Subscript: 'Subscript',
+ Ordered_List: 'Sıralı List',
+ Unordered_List: 'Sırasız List',
+ Indent: 'İçerlek',
+ Outdent: 'Çıkıntılı',
+ Undo: 'Geri Al',
+ Redo: 'Yinele',
+ Link: 'Bağlantı',
+ Unlink: 'Bağlantıyı Kaldır',
+ Image: 'Ä°maj',
+ Table: 'Tablo',
+ HTML: 'HTML',
+ Paragraph: 'ParaÄŸraf',
+ Heading_1: 'Başlık 1',
+ Heading_2: 'Başlık 2',
+ Heading_3: 'Başlık 3',
+ Heading_4: 'Başlık 4',
+ Heading_5: 'Başlık 5',
+ Heading_6: 'Başlık 6',
+ Preformatted: 'Önceden Formatlı',
+ Blockquote: 'Alıntı',
+ Table_Header: 'Tablo Başlığı',
+ URL: 'URL',
+ Title: 'Başlık',
+ Alternative_Text: 'Alternatif Metin',
+ Caption: 'Etiket',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Satır sayısı',
+ Number_Of_Cols: 'Sütun sayısı',
+ Submit: 'Gönder',
+ Cancel: 'Ä°ptal',
+ Choose: 'Seç',
+ Preview: 'Önizleme',
+ Paste_From_Word: 'Wordden yapıştır',
+ Tools: 'Araçlar',
+ Containers: 'Kapsayıcılar',
+ Classes: 'Sınıflar',
+ Status: 'Durum',
+ Source_Code: 'Kaynak Kodu'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/lang/zh_cn.js b/objectapp/static/objectapp/js/wymeditor/lang/zh_cn.js
new file mode 100644
index 0000000..72f5aaf
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/lang/zh_cn.js
@@ -0,0 +1,47 @@
+WYMeditor.STRINGS['zh_cn'] = {
+ Strong: '加粗',
+ Emphasis: '斜体',
+ Superscript: '上标',
+ Subscript: '下标',
+ Ordered_List: '有åºåˆ—表',
+ Unordered_List: 'æ— åºåˆ—表',
+ Indent: '增加缩进',
+ Outdent: 'å‡å°‘缩进',
+ Undo: '撤消',
+ Redo: 'é‡åš',
+ Link: '链接',
+ Unlink: 'å–消链接',
+ Image: '图片',
+ Table: '表格',
+ HTML: 'HTMLæºä»£ç ',
+ Paragraph: '段è½',
+ Heading_1: '标题 1',
+ Heading_2: '标题 2',
+ Heading_3: '标题 3',
+ Heading_4: '标题 4',
+ Heading_5: '标题 5',
+ Heading_6: '标题 6',
+ Preformatted: '原始文本',
+ Blockquote: '引语',
+ Table_Header: '表头',
+ URL: '地å€',
+ Title: 'æ示文字',
+ Alternative_Text: '失效文字',
+ Caption: '标题',
+ Summary: 'Summary',
+ Number_Of_Rows: '行数',
+ Number_Of_Cols: '列数',
+ Submit: 'æ交',
+ Cancel: '放弃',
+ Choose: '选择',
+ Preview: '预览',
+ Paste_From_Word: '从Word粘贴纯文本',
+ Tools: '工具',
+ Containers: '容器',
+ Classes: '预定义样å¼',
+ Status: '状æ€',
+ Source_Code: 'æºä»£ç ',
+ Attachment: '附件',
+ NewParagraph: '新段è½'
+};
+
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js b/objectapp/static/objectapp/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js
new file mode 100644
index 0000000..e553c3e
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js
@@ -0,0 +1,52 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.embed.js
+ * Experimental embed plugin
+ *
+ * File Authors:
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+/*
+ * ISSUES:
+ * - The closing object tag seems to be stripped out...
+ */
+(function() {
+ if (WYMeditor && WYMeditor.XhtmlValidator['_tags']['param']['attributes']) {
+
+ WYMeditor.XhtmlValidator['_tags']["embed"] = {
+ "attributes":[
+ "allowscriptaccess",
+ "allowfullscreen",
+ "height",
+ "src",
+ "type",
+ "width"
+ ]
+ };
+
+ WYMeditor.XhtmlValidator['_tags']['param']['attributes'] = {
+ '0':'name',
+ '1':'type',
+ 'valuetype':/^(data|ref|object)$/,
+ '2':'valuetype',
+ '3':'value'
+ };
+
+ var XhtmlSaxListener = WYMeditor.XhtmlSaxListener;
+ WYMeditor.XhtmlSaxListener = function () {
+ var listener = XhtmlSaxListener.call(this);
+ listener.block_tags.push('embed');
+ return listener;
+ };
+ WYMeditor.XhtmlSaxListener.prototype = XhtmlSaxListener.prototype;
+ }
+})();
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif b/objectapp/static/objectapp/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif
new file mode 100644
index 0000000..d2a8b0a
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js b/objectapp/static/objectapp/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js
new file mode 100644
index 0000000..275c816
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js
@@ -0,0 +1,127 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.fullscreen.js
+ * Fullscreen plugin for WYMeditor
+ *
+ * File Authors:
+ * Luis Santos (luis.santos a-t openquest dotpt)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ * Gerd Riesselmann (gerd a-t gyro-php dot org) : Fixed issue with new skin layout
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.fullscreen = function() {
+ var wym = this,
+ $box = jQuery(this._box),
+ $iframe = jQuery(this._iframe),
+ $overlay = null,
+ $window = jQuery(window),
+
+ editorMargin = 15; // Margin from window (without padding)
+
+
+ //construct the button's html
+ var html = "<li class='wym_tools_fullscreen'>"
+ + "<a name='Fullscreen' href='#'"
+ + " style='background-image:"
+ + " url(" + wym._options.basePath +"plugins/fullscreen/icon_fullscreen.gif)'>"
+ + "Fullscreen"
+ + "</a></li>";
+
+ //add the button to the tools box
+ $box.find(wym._options.toolsSelector + wym._options.toolsListSelector)
+ .append(html);
+
+ function resize () {
+ // Calculate margins
+ var uiHeight = $box.outerHeight(true)
+ - $iframe.outerHeight(true),
+ editorPadding = $box.outerWidth() - $box.width(),
+
+ // Calculate heights
+ screenHeight = $window.height(),
+ iframeHeight = (screenHeight
+ - uiHeight
+ - (editorMargin * 2)) + 'px',
+
+ // Calculate witdths
+ screenWidth = $window.width(),
+ boxWidth = (screenWidth
+ - editorPadding
+ - (editorMargin * 2)) + 'px';
+
+ $box.css('width', boxWidth);
+ $iframe.css('height', iframeHeight);
+ $overlay.css({
+ 'height': screenHeight + 'px',
+ 'width': screenWidth + 'px'
+ });
+ };
+
+ //handle click event
+ $box.find('li.wym_tools_fullscreen a').click(function() {
+ if ($box.css('position') != 'fixed') {
+ // Store previous inline styles
+ $box.data('wym-inline-css', $box.attr('style'));
+ $iframe.data('wym-inline-css', $iframe.attr('style'));
+
+ // Create overlay
+ $overlay = jQuery('<div id="wym-fullscreen-overlay"></div>')
+ .appendTo('body').css({
+ 'position': 'fixed',
+ 'background-color': 'rgb(0, 0, 0)',
+ 'opacity': '0.75',
+ 'z-index': '98',
+ 'top': '0px',
+ 'left': '0px'
+ });
+
+ // Possition the editor
+ $box.css({
+ 'position': 'fixed',
+ 'z-index': '99',
+ 'top': editorMargin + 'px',
+ 'left': editorMargin + 'px'
+ });
+
+ // Bind event listeners
+ $window.bind('resize', resize);
+ $box.find('li.wym_tools_html a').bind('click', resize);
+
+ // Force resize
+ resize();
+ } else {
+ // Unbind event listeners
+ $window.unbind('resize', resize);
+ $box.find('li.wym_tools_html a').unbind('click', resize);
+
+ // Remove inline styles
+ $box.css({
+ 'position': 'static',
+ 'z-index': '',
+ 'width': '',
+ 'top': '',
+ 'left': ''
+ });
+ $iframe.css('height', '');
+
+ // Remove overlay
+ $overlay.remove();
+ $overlay = null;
+
+ // Retore previous inline styles
+ $box.attr('style', $box.data('wym-inline-css'));
+ $iframe.attr('style', $iframe.data('wym-inline-css'));
+ }
+
+ return false;
+ });
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js b/objectapp/static/objectapp/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js
new file mode 100644
index 0000000..2c71ba5
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js
@@ -0,0 +1,57 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.hovertools.js
+ * hovertools plugin for WYMeditor
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.hovertools = function() {
+
+ var wym = this;
+
+ //bind events on buttons
+ jQuery(this._box).find(this._options.toolSelector).hover(
+ function() {
+ wym.status(jQuery(this).html());
+ },
+ function() {
+ wym.status('&nbsp;');
+ }
+ );
+
+ //classes: add/remove a style attr to matching elems
+ //while mouseover/mouseout
+ jQuery(this._box).find(this._options.classSelector).hover(
+ function() {
+ var aClasses = eval(wym._options.classesItems);
+ var sName = jQuery(this).attr(WYMeditor.NAME);
+ var oClass = WYMeditor.Helper.findByName(aClasses, sName);
+
+ if(oClass){
+ jqexpr = oClass.expr;
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ if(!jQuery.browser.msie)
+ jQuery(wym._doc).find(jqexpr).css('background-color','#cfc');
+ }
+ },
+ function() {
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ if(!jQuery.browser.msie)
+ jQuery(wym._doc).find('*').removeAttr('style');
+ }
+ );
+
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js b/objectapp/static/objectapp/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js
new file mode 100644
index 0000000..1ba2d2e
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js
@@ -0,0 +1,91 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.resizable.js
+ * resize plugin for WYMeditor
+ *
+ * File Authors:
+ * Peter Eschler (peschler _at_ gmail.com)
+ * Jean-Francois Hovinne - http://www.hovinne.com/
+ *
+ * Version:
+ * 0.4
+ *
+ * Changelog:
+ *
+ * 0.4
+ * - Removed UI and UI.resizable scripts loading - see #167 (jfh).
+ *
+ * 0.3
+ * - Added 'iframeOriginalSize' and removed 'ui.instance' calls (jfh).
+ *
+ * 0.2
+ * - Added full support for all jQueryUI resizable plugin options.
+ * - Refactored and documented code.
+ * 0.1
+ * - Initial release.
+ */
+
+/**
+ * The resizable plugin makes the wymeditor box vertically resizable.
+ * It it based on the ui.resizable.js plugin of the jQuery UI library.
+ *
+ * The WYMeditor resizable plugin supports all parameters of the jQueryUI
+ * resizable plugin. The parameters are passed like this:
+ *
+ * wym.resizable({ handles: "s,e",
+ * maxHeight: 600 });
+ *
+ * DEPENDENCIES: jQuery UI, jQuery UI resizable
+ *
+ * @param options options for the plugin
+ */
+WYMeditor.editor.prototype.resizable = function(options) {
+
+ var wym = this;
+ var iframe = jQuery(wym._box).find('iframe');
+ var iframeOriginalSize = {};
+
+ // Define some default options
+ var default_options = {
+ start: function(e, ui) {
+ iframeOriginalSize = {
+ width: jQuery(iframe).width(),
+ height: jQuery(iframe).height()
+ }
+ },
+
+ // resize is called by the jQuery resizable plugin whenever the
+ // client area was resized.
+ resize: function(e, ui) {
+ var diff = ui.size.height - ui.originalSize.height;
+ jQuery(iframe).height( iframeOriginalSize.height + diff );
+
+ // If the plugin has horizontal resizing disabled we need to
+ // adjust the "width" attribute of the area css, because the
+ // resizing will set a fixed width (which breaks liquid layout
+ // of the wymeditor area).
+ if( !ui.options.handles['w'] && !ui.options.handles['e'] ) {
+ ui.size.width = "inherit";
+ }
+ },
+ handles: "s,e,se",
+ minHeight: 250,
+ maxHeight: 600
+ };
+
+ // Merge given options with default options. Given options override
+ // default ones.
+ var final_options = jQuery.extend(default_options, options);
+
+ if(jQuery.isFunction( jQuery.fn.resizable )) jQuery(wym._box).resizable(final_options);
+ else WYMeditor.console.error('Oops, jQuery UI.resizable unavailable.');
+
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/resizable/readme.txt b/objectapp/static/objectapp/js/wymeditor/plugins/resizable/readme.txt
new file mode 100644
index 0000000..2a0444e
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/resizable/readme.txt
@@ -0,0 +1,124 @@
+
+
+resizable plugin for WYMeditor
+##############################
+
+The ``resizable`` plugin for WYMeditor_ enables vertical resizing of the
+editor area. The plugin is based on the jQuery UI library.
+
+Requirements
+============
+The following packages are required for using the WYMeditor ``resizable``
+plugin:
+
+* jQuery (tested with jQuery ``jquery-1.2.4a.js`` from ``jquery.ui`` package)
+* WYMeditor SVN trunk (Revision: 482)
+* jQuery-UI (tested with ``jquery.ui-1.5b2``)
+
+It should be possible to use this plugin with ``WYMeditor-0.4`` but I have not
+tried.
+
+Download
+========
+You can download the WYMeditor ``resizable`` plugin here:
+
+* wymeditor-resizable-plugin-0.2.tgz_
+* wymeditor-resizable-plugin-0.1.tgz_
+
+See the Changelog_ for more infos about the releases.
+
+.. _wymeditor-resizable-plugin-0.2.tgz: http://pyjax.net/download/wymeditor-resizable-plugin-0.2.tgz
+.. _wymeditor-resizable-plugin-0.1.tgz: http://pyjax.net/download/wymeditor-resizable-plugin-0.1.tgz
+
+Installation
+============
+Just extract the downloaded archive into your WYMeditor's ``plugin``
+directory.
+
+Usage
+=====
+For general instructions on WYMeditor plugins please refer to the `WYMeditor
+plugin page`_.
+
+To use the ``resizable`` plugin simply include the plugin's JavaScript file in
+your code. You **do not** need to include the jQuery UI files - this is done
+automatically by the plugin (see `Internals`_)::
+
+ <script type="text/javascript"
+ src="/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js">
+ </script>
+
+Make sure to adjust the ``src`` attribute to your needs, then initialize the
+plugin in WYMeditor's ``postInit`` function::
+
+ wymeditor({postInit: function(wym) {
+ wym.hovertools(); // other plugins...
+ wym.resizable({handles: "s,e",
+ maxHeight: 600});
+ }
+ })
+
+The ``resizable`` plugin takes exactly one parameter, which is an object literal
+containing the options of the plugin. The WYMeditor ``resizable`` plugin
+supports all options of the jQuery UI ``resizable`` plugin. These are the
+default values used by the plugin::
+
+ handles: "s,e,se",
+ minHeight: 250,
+ maxHeight: 600
+
+See the `jQuery UI resizable plugin docs`_ for a list of all options.
+
+That's it! You are now able to resize the WYMeditor vertically, horizontally or
+both, depending on your options.
+
+.. _jQuery UI resizable plugin docs: http://docs.jquery.com/UI/Resizables
+
+Internals
+=========
+The plugin takes care of loading the necessary jQuery UI files (``base`` and
+``resizable``) from the same path the jQuery library was loaded. Here's how
+it's done::
+
+ // Get the jQuery path from the editor, stripping away the jQuery file.
+ // see http://www.oreilly.com/catalog/regex/chapter/ch04.html
+ // The match result array contains the path and the filename.
+ var jQueryPath = wym.computeJqueryPath().match(/^(.*)\/(.*)$/)[1];
+
+ // Make an array of the external JavaScript files required by the plugin.
+ var jQueryPlugins = [jQueryPath + '/ui.base.js',
+ jQueryPath + '/ui.resizable.js'];
+
+ // First get the jQuery UI base file
+ $.getScript(jQueryPlugins[0]);
+
+ // Get the jQuery UI resizeable plugin and then init the wymeditor resizable
+ // plugin. It is import to do the initialisation after loading the
+ // necessary jQuery UI files has finished, otherwise the "resizable" method
+ // would not be available.
+ $.getScript(jQueryPlugins[1], function() {
+ jQuery(wym._box).resizable(final_options);
+ });
+
+An alternative approach would be to use an AJAX queue when getting the script
+files to ensure that all jQuery files are loaded before the initialisation code
+of the plugin is executed. There is an `jQuery AJAX queue plugin`_ which does
+that.
+
+.. _jQuery AJAX queue plugin: http://plugins.jquery.com/project/ajaxqueue
+
+Changelog
+=========
+
+0.2
+---
+- Added full support for all jQuery UI resizable plugin options.
+- Refactored and documented code.
+- Now contains a packed version (775 bytes).
+
+0.1
+---
+- Initial release.
+
+.. _WYMeditor: http://www.wymeditor.org/
+.. _WYMeditor plugin page: http://trac.wymeditor.org/trac/wiki/0.4/Plugins
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/tidy/README b/objectapp/static/objectapp/js/wymeditor/plugins/tidy/README
new file mode 100644
index 0000000..acc7ffd
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/tidy/README
@@ -0,0 +1,19 @@
+WYMeditor : what you see is What You Mean web-based editor
+Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+Dual licensed under the MIT (MIT-license.txt)
+and GPL (GPL-license.txt) licenses.
+
+For further information visit:
+ http://www.wymeditor.org/
+
+File Name:
+ README - HTML Tidy plugin for WYMeditor
+
+File Authors:
+ Jean-François Hovinne (jf.hovinne a-t wymeditor dotorg)
+
+Credits:
+ 'HTML Tidy' by Dave Ragget - http://tidy.sourceforge.net/
+ Icon 'wand' by Mark James - http://famfamfam.com/
+
+WYMeditor documentation is available online at http://www.wymeditor.org/
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js b/objectapp/static/objectapp/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js
new file mode 100644
index 0000000..bf30c4c
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js
@@ -0,0 +1,82 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.tidy.js
+ * HTML Tidy plugin for WYMeditor
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.tidy = function(options) {
+ var tidy = new WymTidy(options, this);
+ return(tidy);
+};
+
+//WymTidy constructor
+function WymTidy(options, wym) {
+
+ options = jQuery.extend({
+
+ sUrl: wym._options.basePath + "plugins/tidy/tidy.php",
+ sButtonHtml: "<li class='wym_tools_tidy'>"
+ + "<a name='CleanUp' href='#'"
+ + " style='background-image:"
+ + " url(" + wym._options.basePath + "plugins/tidy/wand.png)'>"
+ + "Clean up HTML"
+ + "</a></li>",
+
+ sButtonSelector: "li.wym_tools_tidy a"
+
+ }, options);
+
+ this._options = options;
+ this._wym = wym;
+
+};
+
+//WymTidy initialization
+WymTidy.prototype.init = function() {
+
+ var tidy = this;
+
+ jQuery(this._wym._box).find(
+ this._wym._options.toolsSelector + this._wym._options.toolsListSelector)
+ .append(this._options.sButtonHtml);
+
+ //handle click event
+ jQuery(this._wym._box).find(this._options.sButtonSelector).click(function() {
+ tidy.cleanup();
+ return(false);
+ });
+
+};
+
+//WymTidy cleanup
+WymTidy.prototype.cleanup = function() {
+
+ var wym = this._wym;
+ var html = "<html><body>" + wym.xhtml() + "</body></html>";
+
+ jQuery.post(this._options.sUrl, { html: html}, function(data) {
+
+ if(data.length > 0 && data != '0') {
+ if(data.indexOf("<?php") == 0) {
+ wym.status("Ooops... Is PHP installed?");
+ } else {
+ wym.html(data);
+ wym.status("HTML has been cleaned up.");
+ }
+ } else {
+ wym.status("An error occurred.");
+ }
+ });
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/tidy/tidy.php b/objectapp/static/objectapp/js/wymeditor/plugins/tidy/tidy.php
new file mode 100644
index 0000000..23b9bec
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/tidy/tidy.php
@@ -0,0 +1,36 @@
+<?php
+
+if (get_magic_quotes_gpc()) $html = stripslashes($_REQUEST['html']);
+else $html = $_REQUEST['html'];
+
+if(strlen($html) > 0) {
+
+ // Specify configuration
+ $config = array(
+ 'bare' => true,
+ 'clean' => true,
+ 'doctype' => 'strict',
+ 'drop-empty-paras' => true,
+ 'drop-font-tags' => true,
+ 'drop-proprietary-attributes' => true,
+ 'enclose-block-text' => true,
+ 'indent' => false,
+ 'join-classes' => true,
+ 'join-styles' => true,
+ 'logical-emphasis' => true,
+ 'output-xhtml' => true,
+ 'show-body-only' => true,
+ 'wrap' => 0);
+
+ // Tidy
+ $tidy = new tidy;
+ $tidy->parseString($html, $config, 'utf8');
+ $tidy->cleanRepair();
+
+ // Output
+ echo $tidy;
+} else {
+
+echo ('0');
+}
+?>
diff --git a/objectapp/static/objectapp/js/wymeditor/plugins/tidy/wand.png b/objectapp/static/objectapp/js/wymeditor/plugins/tidy/wand.png
new file mode 100644
index 0000000..bb55eea
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/plugins/tidy/wand.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/compact/icons.png b/objectapp/static/objectapp/js/wymeditor/skins/compact/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/compact/icons.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/compact/skin.css b/objectapp/static/objectapp/js/wymeditor/skins/compact/skin.css
new file mode 100644
index 0000000..4a6a0c6
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/compact/skin.css
@@ -0,0 +1,134 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_compact p, .wym_skin_compact h2, .wym_skin_compact h3,
+ .wym_skin_compact ul, .wym_skin_compact li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_compact .wym_area_left { display: none; }
+ .wym_skin_compact .wym_area_right { display: none; }
+
+
+/*TYPO*/
+ .wym_skin_compact { font-size: 10px; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_compact h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_compact h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_compact li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_compact { border: 1px solid gray; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_compact:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_compact { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_compact .wym_html { width: 98%;}
+ .wym_skin_compact .wym_html textarea { font-size: 120%; width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_compact .wym_iframe { width: 98%;}
+ .wym_skin_compact .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_compact .wym_area_left { width: 100px; float: left;}
+ .wym_skin_compact .wym_area_right { width: 150px; float: right;}
+ .wym_skin_compact .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_compact .wym_area_main { height: 1%;}
+ * html .wym_skin_compact .wym_area_top { height: 1%;}
+ *+html .wym_skin_compact .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_compact .wym_section { margin-bottom: 5px; }
+ .wym_skin_compact .wym_section h2,
+ .wym_skin_compact .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_compact .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_compact .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_compact .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_compact .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_compact .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_compact .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_compact .wym_panel { }
+ .wym_skin_compact .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_compact .wym_dropdown h2 { display: block; }
+ .wym_skin_compact .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_compact .wym_dropdown:hover ul,
+ .wym_skin_compact .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_compact .wym_buttons li { float:left;}
+ .wym_skin_compact .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_compact .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_compact .wym_section h2 { background: #f0f0f0; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_compact .wym_section h2 span { color: gray;}
+ .wym_skin_compact .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_compact .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_compact .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_compact .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/compact/skin.js b/objectapp/static/objectapp/js/wymeditor/skins/compact/skin.js
new file mode 100644
index 0000000..cfb7cc1
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/compact/skin.js
@@ -0,0 +1,35 @@
+WYMeditor.SKINS['compact'] = {
+
+ init: function(wym) {
+
+ //move the containers panel to the top area
+ jQuery(wym._options.containersSelector + ', '
+ + wym._options.classesSelector, wym._box)
+ .appendTo( jQuery("div.wym_area_top", wym._box) )
+ .addClass("wym_dropdown")
+ .css({"margin-right": "10px", "width": "120px", "float": "left"});
+
+ //render following sections as buttons
+ jQuery(wym._options.toolsSelector, wym._box)
+ .addClass("wym_buttons")
+ .css({"margin-right": "10px", "float": "left"});
+
+ //make hover work under IE < 7
+ jQuery(".wym_section", wym._box).hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+
+ var postInit = wym._options.postInit;
+ wym._options.postInit = function(wym) {
+
+ if(postInit) postInit.call(wym, wym);
+ var rule = {
+ name: 'body',
+ css: 'background-color: #f0f0f0;'
+ };
+ wym.addCssRule( wym._doc.styleSheets[0], rule);
+ };
+ }
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/default/icons.png b/objectapp/static/objectapp/js/wymeditor/skins/default/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/default/icons.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/default/skin.css b/objectapp/static/objectapp/js/wymeditor/skins/default/skin.css
new file mode 100644
index 0000000..eb4680f
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/default/skin.css
@@ -0,0 +1,133 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * skin.css
+ * main stylesheet for the default WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_default p, .wym_skin_default h2, .wym_skin_default h3,
+ .wym_skin_default ul, .wym_skin_default li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_default .wym_area_left { display: none; }
+ .wym_skin_default .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_default { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_default h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_default h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_default li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_default { border: 1px solid gray; background: #f2f2f2; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_default:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_default { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_default .wym_html { width: 98%;}
+ .wym_skin_default .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_default .wym_iframe { width: 98%;}
+ .wym_skin_default .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_default .wym_area_left { width: 150px; float: left;}
+ .wym_skin_default .wym_area_right { width: 150px; float: right;}
+ .wym_skin_default .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_default .wym_area_main { height: 1%;}
+ * html .wym_skin_default .wym_area_top { height: 1%;}
+ *+html .wym_skin_default .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_default .wym_section { margin-bottom: 5px; }
+ .wym_skin_default .wym_section h2,
+ .wym_skin_default .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_default .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_default .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_default .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_default .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_default .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_default .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_default .wym_panel { }
+ .wym_skin_default .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_default .wym_dropdown h2 { display: block; }
+ .wym_skin_default .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_default .wym_dropdown:hover ul,
+ .wym_skin_default .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_default .wym_buttons li { float:left;}
+ .wym_skin_default .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_default .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_default .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_default .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_default .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_default .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_default .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_default .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_default .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_default .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_default .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_default .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_default .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_default .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_default .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_default .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_default .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_default .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_default .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_default .wym_section h2 { background: #ddd; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_default .wym_section h2 span { color: gray;}
+ .wym_skin_default .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_default .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_default .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_default .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/default/skin.js b/objectapp/static/objectapp/js/wymeditor/skins/default/skin.js
new file mode 100644
index 0000000..3d204e0
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/default/skin.js
@@ -0,0 +1,40 @@
+WYMeditor.SKINS['default'] = {
+
+ init: function(wym) {
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .addClass("wym_dropdown")
+ .find(WYMeditor.H2)
+ .append("<span>&#160;&gt;</span>");
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "155px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/django/icons.png b/objectapp/static/objectapp/js/wymeditor/skins/django/icons.png
new file mode 100644
index 0000000..0e04964
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/django/icons.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/django/skin.css b/objectapp/static/objectapp/js/wymeditor/skins/django/skin.css
new file mode 100644
index 0000000..6ae3772
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/django/skin.css
@@ -0,0 +1,136 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2008 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * skin.css
+ * main stylesheet for the django WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jannis Leidel (jannis@leidel.info)
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_django p, .wym_skin_django h2, .wym_skin_django h3,
+ .wym_skin_django ul, .wym_skin_django li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_django .wym_area_left { display: none; }
+ .wym_skin_django .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_django { font-size: 62,5%; font-family: "Lucida Grande", "DejaVu Sans", "Bitstream Vera Sans", Verdana, Arial, sans-serif }
+ .wym_skin_django h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_django h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_django li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_django { border: 0px; background: none; padding: 10px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_django:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_django { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_django .wym_html { width: 98%;}
+ .wym_skin_django .wym_html textarea { width: 100%; height: 235px; border: 1px solid #ccc; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_django .wym_iframe { width: 98%;}
+ .wym_skin_django .wym_iframe iframe { width: 100%; height: 235px; border: 1px solid #ccc; background: white }
+
+
+/*AREAS*/
+ .wym_skin_django .wym_area_left { width: 150px; float: left;}
+ .wym_skin_django .wym_area_right { width: 150px; float: right;}
+ .wym_skin_django .wym_area_bottom { clear: both; display: none;}
+ * html .wym_skin_django .wym_area_main { height: 1%;}
+ * html .wym_skin_django .wym_area_top { height: 1%;}
+ *+html .wym_skin_django .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_django .wym_section { margin-bottom: 5px; }
+ .wym_skin_django .wym_section h2,
+ .wym_skin_django .wym_section h3 { padding: 1px 3px; margin: 0; color:#333; }
+ .wym_skin_django .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_django .wym_section a:hover { background-color: #eaeaea; }
+ /*hide section titles by default*/
+ .wym_skin_django .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_django .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_django .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_django .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_django .wym_panel { }
+ .wym_skin_django .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_django .wym_dropdown h2 { display: block; }
+ .wym_skin_django .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_django .wym_dropdown:hover ul,
+ .wym_skin_django .wym_dropdown.hover ul { display: block; }
+
+ .wym_skin_django .wym_tools ul { padding-left: 0px !important; margin-left: 0px; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_django .wym_buttons li { float:left;}
+ .wym_skin_django .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_django .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_django .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_django .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_django .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_django .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_django .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_django .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_django .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_django .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_django .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_django .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_django .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_django .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_django .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_django .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_django .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_django .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_django .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_django .wym_section h2 { background: #fff;}
+ .wym_skin_django .wym_section h2 span { color: gray;}
+ .wym_skin_django .wym_panel { padding: 0; background: white;}
+ .wym_skin_django .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_django .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_django .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/django/skin.js b/objectapp/static/objectapp/js/wymeditor/skins/django/skin.js
new file mode 100644
index 0000000..47597e6
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/django/skin.js
@@ -0,0 +1,42 @@
+WYMeditor.SKINS['django'] = {
+
+ init: function(wym) {
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .addClass("wym_panel")
+ .find(WYMeditor.H2);
+
+ //replace the status markup in h2
+ wym.status('&nbsp;');
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "155px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.header.gif b/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.header.gif
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.header.gif
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.selector.silver.gif b/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.selector.silver.gif
new file mode 100644
index 0000000..e65976b
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.selector.silver.gif
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.wymeditor.png b/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.wymeditor.png
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/bg.wymeditor.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/icons.silver.gif b/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/icons.silver.gif
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/minimal/images/icons.silver.gif
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/minimal/skin.css b/objectapp/static/objectapp/js/wymeditor/skins/minimal/skin.css
new file mode 100644
index 0000000..cea8d84
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/minimal/skin.css
@@ -0,0 +1,131 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * skin.css
+ * main stylesheet for the minimal WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne
+ * Scott Lewis (see Silver skin)
+*/
+
+/* Set iframe */
+.wym_skin_minimal div.wym_iframe iframe {
+ width: 90%;
+ height: 200px;
+}
+
+/* Hide h2 by default */
+.wym_skin_minimal h2 {
+ display: none;
+}
+
+/* Show specific h2 */
+.wym_skin_minimal div.wym_tools h2,
+.wym_skin_minimal div.wym_containers h2,
+.wym_skin_minimal div.wym_classes h2 {
+ display: block;
+}
+
+.wym_skin_minimal div.wym_section ul {
+ margin: 0;
+}
+
+.wym_skin_minimal div.wym_section ul li {
+ float: left;
+ list-style-type: none;
+ margin-right: 5px;
+}
+
+.wym_skin_minimal div.wym_area_top,
+.wym_skin_minimal div.wym_area_right,
+.wym_skin_minimal div.wym_containers,
+.wym_skin_minimal div.wym_classes {
+ float: left;
+}
+
+.wym_skin_minimal div.wym_area_main {
+ clear: both;
+}
+
+.wym_skin_minimal div.wym_html {
+ width: 90%;
+}
+
+.wym_skin_minimal textarea.wym_html_val {
+ width: 100%;
+ height: 100px;
+}
+
+/* DROPDOWNS (see Silver skin) */
+.wym_skin_minimal div.wym_dropdown {
+ cursor: pointer;
+ margin: 0px 4px 10px 0px;
+ padding: 0px;
+ z-index: 1001;
+ display: block;
+}
+
+.wym_skin_minimal div.wym_dropdown ul {
+ display: none;
+ width: 124px;
+ padding: 0px;
+ margin: 0px;
+ list-style-type: none;
+ list-style-image: none;
+ z-index: 1002;
+ position: absolute;
+ border-top: 1px solid #AAA;
+}
+
+.wym_skin_minimal div.wym_dropdown ul li {
+ width: 146px;
+ height: 20px;
+ padding: 0px;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #EEE;
+ list-style-image: none;
+}
+
+.wym_skin_minimal div.wym_dropdown h2 {
+ width: 138px;
+ height: 16px;
+ color: #000;
+ background-image: url(images/bg.selector.silver.gif);
+ background-position: 0px -18px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ font-weight: bold;
+ padding: 2px 0px 0px 10px;
+ margin: 0px;
+}
+
+.wym_skin_minimal div.wym_dropdown a {
+ text-decoration: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ padding: 5px 0px 0px 10px;
+ display: block;
+ width: 136px;
+ height: 15px;
+ color: #000;
+ text-align: left;
+ margin-left: 0px;
+}
+
+.wym_skin_minimal div.wym_dropdown a:hover {
+ background: #BBB;
+ border-bottom: none;
+}
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/minimal/skin.js b/objectapp/static/objectapp/js/wymeditor/skins/minimal/skin.js
new file mode 100644
index 0000000..af29ed4
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/minimal/skin.js
@@ -0,0 +1,30 @@
+jQuery.fn.selectify = function() {
+ return this.each(function() {
+ jQuery(this).hover(
+ function() {
+ jQuery("h2", this).css("background-position", "0px -18px");
+ jQuery("ul", this).fadeIn("fast");
+ },
+ function() {
+ jQuery("h2", this).css("background-position", "");
+ jQuery("ul", this).fadeOut("fast");
+ }
+ );
+ });
+};
+
+WYMeditor.SKINS['minimal'] = {
+ //placeholder for the skin JS, if needed
+
+ //init the skin
+ //wym is the WYMeditor.editor instance
+ init: function(wym) {
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.toolsSelector + ', ' + wym._options.containersSelector + ', ' + wym._options.classesSelector)
+ .addClass("wym_dropdown")
+ .selectify();
+
+
+ }
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/silver/COPYING b/objectapp/static/objectapp/js/wymeditor/skins/silver/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/silver/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/silver/README b/objectapp/static/objectapp/js/wymeditor/skins/silver/README
new file mode 100644
index 0000000..130dc46
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/silver/README
@@ -0,0 +1,27 @@
+/**
+* @version Alpha 0.1 2008-05-10 23:28:43 $
+* @package Silver skin for WYMeditor
+* @copyright Copyright (C) 2008 Scott Edwin Lewis. All rights reserved.
+* @license GNU/GPL, see COPYING
+* Silver skin for WYMeditor is free software and is licensed under the
+* GNU General Public License. See COPYING for copyright notices and details.
+*/
+
+Adds custom buttons and color palette to the WYMeditor XHTML Editor.
+
+INSTALLATION:
+
+1. Copy the entire /silver/ directory to /wymeditor/skins/
+2. Initialize the WYMeditor 'skin' option as below:
+
+<script type="text/javascript">
+jQuery(function() {
+
+ jQuery('.wymeditor').wymeditor({
+ skin: 'silver'
+ });
+
+});
+</script>
+
+That's it. You're done.
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.header.gif b/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.header.gif
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.header.gif
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.selector.silver.gif b/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.selector.silver.gif
new file mode 100644
index 0000000..e65976b
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.selector.silver.gif
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.wymeditor.png b/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.wymeditor.png
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/silver/images/bg.wymeditor.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/silver/images/icons.silver.gif b/objectapp/static/objectapp/js/wymeditor/skins/silver/images/icons.silver.gif
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/silver/images/icons.silver.gif
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/silver/skin.css b/objectapp/static/objectapp/js/wymeditor/skins/silver/skin.css
new file mode 100644
index 0000000..8284d81
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/silver/skin.css
@@ -0,0 +1,297 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the default WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Scott Edwin Lewis
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_silver p, .wym_skin_silver h2, .wym_skin_silver h3,
+ .wym_skin_silver ul, .wym_skin_silver li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_silver .wym_area_left { display: none; }
+ .wym_skin_silver .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_silver { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_silver h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_silver h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_silver li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_silver { border: 1px solid gray; background: #f2f2f2; padding: 0px; margin: 0px;}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_silver:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_silver { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_silver .wym_html { width: 98%;}
+ .wym_skin_silver .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_silver .wym_iframe { width: 98%;}
+ .wym_skin_silver .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_silver .wym_area_left { width: 150px; float: left;}
+ .wym_skin_silver .wym_area_right { width: 150px; float: right;}
+ .wym_skin_silver .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_silver .wym_area_main { height: 1%;}
+ * html .wym_skin_silver .wym_area_top { height: 1%;}
+ *+html .wym_skin_silver .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_silver .wym_section { margin-bottom: 5px; }
+ .wym_skin_silver .wym_section h2,
+ .wym_skin_silver .wym_section h3 { padding: 1px 3px; margin: 0; cursor: pointer; }
+ .wym_skin_silver .wym_section a { padding: 5px 0px 0px 10px; display: block; text-decoration: none; color: black; }
+ .wym_skin_silver .wym_section a:hover { /*background-color: #DDD;*/}
+ /*hide section titles by default*/
+ .wym_skin_silver .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_silver .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_silver .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; padding: 0px; }
+ * html .wym_skin_silver .wym_section ul { height: 1%;}
+ .wym_skin_silver .wym_section li {}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_silver .wym_panel { }
+ .wym_skin_silver .wym_panel h2 { display: block; font-size: 11px; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_silver .wym_dropdown h2 { display: block; font-size: 11px;}
+ .wym_skin_silver .wym_dropdown ul { position: absolute; background: white; padding: 0px;}
+ .wym_skin_silver .wym_dropdown:hover ul,
+ .wym_skin_silver .wym_dropdown.hover ul { cursor: pointer;}
+ .wym_skin_silver .wym_dropdown ul li a {/*border-bottom: 1px solid #AAA;*/}
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_silver .wym_buttons li { float:left;}
+ .wym_skin_silver .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px; text-decoration: none !important; border: 1px solid #666; }
+ .wym_skin_silver .wym_buttons a:hover { text-decoration: none !important; border: 1px solid #000;}
+ /*image replacements*/
+ .wym_skin_silver .wym_buttons li a { background: url(images/icons.silver.gif) no-repeat; text-indent: -9999px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_strong a { background-position: 0 -384px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_emphasis a { background-position: 0 -24px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_superscript a { background-position: 0 -432px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_subscript a { background-position: 0 -456px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_indent a { background-position: 0 -600px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_outdent a { background-position: 0 -624px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_undo a { background-position: 0 -504px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_redo a { background-position: 0 -528px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_image a { background-position: 0 -120px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_html a { background-position: 0 -192px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_gadget a { background-position: 0 -576px;}
+
+ .wym_skin_silver .wym_buttons li.wym_tools_strong a:hover { background-position: -24px -384px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_emphasis a:hover { background-position: -24px -24px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_superscript a:hover { background-position: -24px -432px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_subscript a:hover { background-position: -24px -456px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_ordered_list a:hover { background-position: -24px -48px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unordered_list a:hover{ background-position: -24px -72px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_indent a:hover { background-position: -24px -600px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_outdent a:hover { background-position: -24px -624px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_undo a:hover { background-position: -24px -504px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_redo a:hover { background-position: -24px -528px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_link a:hover { background-position: -24px -96px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unlink a:hover { background-position: -24px -168px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_image a:hover { background-position: -24px -120px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_table a:hover { background-position: -24px -144px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_paste a:hover { background-position: -24px -552px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_html a:hover { background-position: -24px -192px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_preview a:hover { background-position: -24px -408px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_gadget a:hover { background-position: -24px -576px;}
+
+/*DECORATION*/
+ .wym_skin_silver .wym_section h2 { background: #ddd; border: none;}
+ .wym_skin_silver .wym_section h2 span { color: gray;}
+ .wym_skin_silver .wym_panel { padding: 0; border: solid gray; border-width: 0px;}
+ .wym_skin_silver .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_silver .wym_dropdown { padding: 0; border: none; }
+ .wym_skin_silver .wym_dropdown ul { border: none; margin-left: -1px; padding: 0px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link
+ {
+ text-indent: -9999px;
+ float: right;
+ display: block;
+ width: 50px;
+ height: 15px;
+ background: url(../wymeditor_icon.png);
+ background-position: 1px 1px;
+ background-repeat: no-repeat;
+ overflow: hidden;
+ text-decoration: none;
+ padding: 1px !important;
+ border: 1px solid #333 !important;
+ background-color: #FFF !important;
+ }
+
+.wym_box
+{
+ padding: 0px !important;
+ margin: 0px;
+}
+.wym_inner
+{
+ border-left: 1px solid #FFF;
+ border-top: 1px solid #FFF;
+ border-right: 1px solid #FFF;
+ border-bottom: 1px solid #FFF;
+ padding: 5px;
+ background-color: #B8C1C4;
+ height: auto;
+}
+
+.clear {clear: both;}
+
+div.wym_dropdown
+{
+ cursor: pointer;
+ width: 138px !important;
+ margin: 0px 4px 10px 0px !important;
+ padding: 0px;
+ z-index: 1001;
+ display: block;
+ border: 1px solid red;
+}
+
+div.wym_dropdown ul
+{
+ display: none;
+ width: 124px;
+ padding: 0px !important;
+ margin: 0px !important;
+ list-style-type: none;
+ list-style-image: none;
+ z-index: 1002;
+ position: absolute;
+ border-top: 1px solid #AAA;
+}
+
+div.wym_dropdown ul li
+{
+ width: 146px;
+ height: 20px;
+ padding: 0px !important;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #DDD;
+ list-style-image: none;
+}
+
+div.wym_dropdown h2
+{
+ width: 138px;
+ height: 16px;
+ color: #000 !important;
+ background-image: url(images/bg.selector.silver.gif) !important;
+ background-position: 0px -18px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px !important;
+ font-weight: bold !important;
+ padding: 2px 0px 0px 10px !important;
+ margin: 0px !important;
+}
+
+.wym_skin_silver .wym_panel h2
+{
+ width: 138px;
+ height: 16px;
+ color: #000 !important;
+ background-image: url(images/bg.header.gif) !important;
+ background-position: 0px 0px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px !important;
+ font-weight: bold !important;
+ padding: 2px 0px 0px 10px !important;
+ margin: 0px !important;
+}
+
+.wym_skin_silver .wym_panel ul
+{
+ margin-top: 0px !important;
+}
+
+.wym_skin_silver .wym_panel ul li
+{
+ width: 146px;
+ height: 20px;
+ padding: 0px !important;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #DDD;
+ list-style-image: none;
+}
+
+.wym_skin_silver .wym_panel a,
+div.wym_dropdown a
+{
+ text-decoration: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ padding: 5px 0px 0px 10px !important;
+ display: block;
+ width: 136px;
+ height: 15px;
+ color: #000;
+ text-align: left !important;
+ margin-left: 0px !important;
+}
+
+div.wym_dropdown a:hover,
+.wym_skin_silver .wym_panel a:hover
+{
+ background: #BBB;
+ border-bottom: none !important;
+}
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/silver/skin.js b/objectapp/static/objectapp/js/wymeditor/skins/silver/skin.js
new file mode 100644
index 0000000..948ed91
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/silver/skin.js
@@ -0,0 +1,61 @@
+/* This file is part of the Silver skin for WYMeditor by Scott Edwin Lewis */
+
+jQuery.fn.selectify = function() {
+ return this.each(function() {
+ jQuery(this).hover(
+ function() {
+ jQuery("h2", this).css("background-position", "0px -18px");
+ jQuery("ul", this).fadeIn("fast");
+ },
+ function() {
+ jQuery("h2", this).css("background-position", "");
+ jQuery("ul", this).fadeOut("fast");
+ }
+ );
+ });
+};
+
+WYMeditor.SKINS['silver'] = {
+
+ init: function(wym) {
+
+ //add some elements to improve the rendering
+ jQuery(wym._box)
+ .append('<div class="clear"></div>')
+ .wrapInner('<div class="wym_inner"></div>');
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .addClass("wym_dropdown")
+ .selectify();
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "155px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/twopanels/icons.png b/objectapp/static/objectapp/js/wymeditor/skins/twopanels/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/twopanels/icons.png
Binary files differ
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/twopanels/skin.css b/objectapp/static/objectapp/js/wymeditor/skins/twopanels/skin.css
new file mode 100644
index 0000000..7e6b8fd
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/twopanels/skin.css
@@ -0,0 +1,134 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jean-Francois Hovinne
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_twopanels p, .wym_skin_twopanels h2, .wym_skin_twopanels h3,
+ .wym_skin_twopanels ul, .wym_skin_twopanels li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_twopanels .wym_area_left { display: block; }
+ .wym_skin_twopanels .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_twopanels { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_twopanels h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_twopanels h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_twopanels li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_twopanels { border: 1px solid gray; background: #f2f2f2; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_twopanels:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_twopanels { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_twopanels .wym_html { width: 98%;}
+ .wym_skin_twopanels .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_twopanels .wym_iframe { width: 98%;}
+ .wym_skin_twopanels .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_twopanels .wym_area_left { width: 100px; float: left;}
+ .wym_skin_twopanels .wym_area_right { width: 150px; float: right;}
+ .wym_skin_twopanels .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_twopanels .wym_area_main { height: 1%;}
+ * html .wym_skin_twopanels .wym_area_top { height: 1%;}
+ *+html .wym_skin_twopanels .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_twopanels .wym_section { margin-bottom: 5px; }
+ .wym_skin_twopanels .wym_section h2,
+ .wym_skin_twopanels .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_twopanels .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_twopanels .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_twopanels .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_twopanels .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_twopanels .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_twopanels .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_twopanels .wym_panel { }
+ .wym_skin_twopanels .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_twopanels .wym_dropdown h2 { display: block; }
+ .wym_skin_twopanels .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_twopanels .wym_dropdown:hover ul,
+ .wym_skin_twopanels .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_twopanels .wym_buttons li { float:left;}
+ .wym_skin_twopanels .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_twopanels .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_twopanels .wym_section h2 { background: #ddd; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_twopanels .wym_section h2 span { color: gray;}
+ .wym_skin_twopanels .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_twopanels .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_twopanels .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_twopanels .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/twopanels/skin.js b/objectapp/static/objectapp/js/wymeditor/skins/twopanels/skin.js
new file mode 100644
index 0000000..e82efc5
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/twopanels/skin.js
@@ -0,0 +1,39 @@
+WYMeditor.SKINS['twopanels'] = {
+
+ init: function(wym) {
+
+ //move the containers panel to the left area
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .appendTo("div.wym_area_left");
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector + ', '
+ + wym._options.containersSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "115px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/objectapp/static/objectapp/js/wymeditor/skins/wymeditor_icon.png b/objectapp/static/objectapp/js/wymeditor/skins/wymeditor_icon.png
new file mode 100644
index 0000000..d4fc155
--- /dev/null
+++ b/objectapp/static/objectapp/js/wymeditor/skins/wymeditor_icon.png
Binary files differ
diff --git a/objectapp/templates/404.html b/objectapp/templates/404.html
new file mode 100644
index 0000000..7551f02
--- /dev/null
+++ b/objectapp/templates/404.html
@@ -0,0 +1,46 @@
+{% extends "objectapp/skeleton.html" %}
+{% load i18n objectapp_tags %}
+
+{% block breadcrumbs %}
+ {% trans "Error 404" %}
+{% endblock %}
+
+{% block title %}{% trans "Page not found" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Page not found" %}</h2>
+
+<p>{% trans "Sorry, but the requested page could not be found." %}</p>
+
+<h3>{% trans "Useful links" %}</h3>
+<div class="links">
+ <ul>
+ <li>
+ <a href="{% url objectapp_gbobject_archive_index %}" title="{% trans "Blog index" %}">
+ {% trans "Blog index" %}
+ </a>
+ </li>
+ <li>
+ <a href="{% url objectapp_sitemap %}" title="{% trans "Sitemap" %}">
+ {% trans "Sitemap" %}
+ </a>
+ </li>
+ </ul>
+</div>
+
+<h3>{% trans "Recent gbobjects" %}</h3>
+<div class="recents">
+ {% get_recent_gbobjects 5 %}
+</div>
+
+<h3>{% trans "Search" %}</h3>
+<div class="search">
+ <form method="get" id="searchform" action="{% url objectapp_gbobject_search %}">
+ <p>
+ <input type="text" value="{% trans "Keywords..." %}" name="pattern" id="searchbox" onfocus="this.value=''"/>
+ <input type="submit" class="submitbutton" value="OK" />
+ </p>
+ </form>
+</div>
+
+{% endblock %}
diff --git a/objectapp/templates/500.html b/objectapp/templates/500.html
new file mode 100644
index 0000000..f481350
--- /dev/null
+++ b/objectapp/templates/500.html
@@ -0,0 +1,46 @@
+{% extends "objectapp/skeleton.html" %}
+{% load i18n objectapp_tags %}
+
+{% block breadcrumbs %}
+ {% trans "Error 500" %}
+{% endblock %}
+
+{% block title %}{% trans "Server error" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Server error" %}</h2>
+
+<p>{% trans "There's been an error. It's been reported to the site administrators via e-mail and should be fixed shortly. Thanks for your patience." %}</p>
+
+<h3>{% trans "Useful links" %}</h3>
+<div class="links">
+ <ul>
+ <li>
+ <a href="{% url objectapp_gbobject_archive_index %}" title="{% trans "Blog index" %}">
+ {% trans "Blog index" %}
+ </a>
+ </li>
+ <li>
+ <a href="{% url objectapp_sitemap %}" title="{% trans "Sitemap" %}">
+ {% trans "Sitemap" %}
+ </a>
+ </li>
+ </ul>
+</div>
+
+<h3>{% trans "Recent gbobjects" %}</h3>
+<div class="recents">
+ {% get_recent_gbobjects 5 %}
+</div>
+
+<h3>{% trans "Search" %}</h3>
+<div class="search">
+ <form method="get" id="searchform" action="{% url objectapp_gbobject_search %}">
+ <p>
+ <input type="text" value="{% trans "Keywords..." %}" name="pattern" id="searchbox" onfocus="this.value=''"/>
+ <input type="submit" class="submitbutton" value="OK" />
+ </p>
+ </form>
+</div>
+
+{% endblock %}
diff --git a/objectapp/templates/admin/objectapp/app_index.html b/objectapp/templates/admin/objectapp/app_index.html
new file mode 100644
index 0000000..769c163
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/app_index.html
@@ -0,0 +1,41 @@
+{% extends "admin/app_index.html" %}
+{% load i18n adminmedia %}
+
+{% block extrastyle %}
+{{ block.super }}
+<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}objectapp/css/jquery.autocomplete.css" />
+<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}objectapp/css/dashboard_objectapp.css" />
+{% endblock %}
+
+{% block extrahead %}
+{{ block.super }}
+<script type="text/javascript" src="{{ STATIC_URL }}objectapp/js/jquery.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL }}objectapp/js/jquery.masonry.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL }}objectapp/js/jquery.bgiframe.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL }}objectapp/js/jquery.autocomplete.js"></script>
+<script type="text/javascript" src="{% url admin:objectapp_gbobject_autocomplete_tags %}"></script>
+<script type="text/javascript">
+$(document).ready(function() {
+ $("#content-main").masonry({
+ singleMode: true, itemSelector: ".module"
+ });
+});
+</script>
+{% endblock %}
+
+{% block content %}
+<div id="content-main">
+ {% include "admin/objectapp/widgets/content_stats.html" %}
+
+ {% include "admin/objectapp/widgets/quickpost.html" %}
+
+ {% include "admin/objectapp/widgets/recent_comments.html" %}
+
+ {% include "admin/objectapp/widgets/draft_gbobjects.html" %}
+
+ {% include "admin/objectapp/widgets/recent_linkbacks.html" %}
+</div>
+{% endblock %}
+
+
+
diff --git a/objectapp/templates/admin/objectapp/gbobject/autocomplete_tags.js b/objectapp/templates/admin/objectapp/gbobject/autocomplete_tags.js
new file mode 100644
index 0000000..41cb344
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/gbobject/autocomplete_tags.js
@@ -0,0 +1,11 @@
+{% load tagging_tags %}
+
+$(document).ready(function() {
+ {% tags_for_model objectapp.Gbobject as gbobject_tags %}
+ var data = "{{ gbobject_tags|join:',' }}".split(",");
+ $("#id_tags").autocomplete(data, {
+ width: 150, max: 10,
+ multiple: true, multipleSeparator: ", ",
+ scroll: true, scrollHeight: 300,
+ matchContains: true, autoFill: true,});
+}); \ No newline at end of file
diff --git a/objectapp/templates/admin/objectapp/gbobject/markitup.js b/objectapp/templates/admin/objectapp/gbobject/markitup.js
new file mode 100644
index 0000000..543d49b
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/gbobject/markitup.js
@@ -0,0 +1,3 @@
+$(document).ready(function() {
+ $("#id_content").markItUp(mySettings);
+});
diff --git a/objectapp/templates/admin/objectapp/gbobject/tinymce_textareas.js b/objectapp/templates/admin/objectapp/gbobject/tinymce_textareas.js
new file mode 100644
index 0000000..51461c6
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/gbobject/tinymce_textareas.js
@@ -0,0 +1,23 @@
+tinyMCE.init({
+ mode: "exact",
+ elements: "id_content",
+ theme: "advanced",
+ skin : "o2k7",
+ skin_variant : "silver",
+ height: "250",
+ width: "800",
+ relative_urls: false,
+ language: "{{ language }}",
+ directionality: "{{ directionality }}",
+ spellchecker_languages : "{{ spellchecker_languages }}",
+ spellchecker_rpc_url : "{{ spellchecker_rpc_url }}",
+ theme_advanced_toolbar_location : "top",
+ theme_advanced_toolbar_align : "left",
+ theme_advanced_statusbar_location : "bottom",
+ theme_advanced_resizing : true,
+ plugins: "contextmenu,directionality,fullscreen,paste,preview,searchreplace,spellchecker,visualchars,wordcount",
+ paste_auto_cleanup_on_paste : true,
+ theme_advanced_buttons1 : "formatselect,fontsizeselect,|,undo,redo,|,cut,copy,paste,pastetext,pasteword,|,search,replace,|,visualchars,visualaid,cleanup,code,preview,fullscreen",
+ theme_advanced_buttons2 : "bold,italic,underline,strikethrough,|,forecolor,backcolor,removeformat,|,justifyleft,justifycenter,justifyright,justifyfull,|,sub,sup,|,bullist,numlist,|,outdent,indent,|,link,unlink,anchor,image,blockquote,hr,charmap,",
+ theme_advanced_buttons3 : "",
+ });
diff --git a/objectapp/templates/admin/objectapp/gbobject/wymeditor.js b/objectapp/templates/admin/objectapp/gbobject/wymeditor.js
new file mode 100644
index 0000000..f67218c
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/gbobject/wymeditor.js
@@ -0,0 +1,10 @@
+$(document).ready(function() {
+ $("#id_content").wymeditor({
+ skin: "django", lang: "{{ LANGUAGE_CODE }}",
+ stylesheet: "{{ STATIC_URL }}objectapp/css/wymeditor_styles.css",
+ updateSelector: "input:submit", updateEvent: "click",
+ postInit: function(wym) {
+ wym.hovertools();
+ }
+ });
+ });
diff --git a/objectapp/templates/admin/objectapp/widgets/_content_stats.html b/objectapp/templates/admin/objectapp/widgets/_content_stats.html
new file mode 100644
index 0000000..ab1b484
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/_content_stats.html
@@ -0,0 +1,38 @@
+{% load i18n %}
+
+<tr>
+ <th>{% trans "Contents" %}</th>
+ <th>{% trans "Discussions" %}</th>
+</tr>
+<tr>
+ <td>
+ <a href="{% url admin:objectapp_gbobject_changelist %}">
+ {% blocktrans %}{{ gbobjects }} gbobjects{% endblocktrans %}
+ </a>
+ </td>
+ <td>
+ <a href="{% url admin:comments_comment_changelist %}?is_public__exact=1">
+ {% blocktrans %}{{ comments }} comments{% endblocktrans %}
+ </a>
+ </td>
+</tr>
+<tr>
+ <td>
+ <a href="{% url admin:tagging_tag_changelist %}">
+ {% blocktrans %}{{ tags }} tags{% endblocktrans %}
+ </a>
+ </td>
+ <td>{% blocktrans %}{{ trackbacks }} trackbacks{% endblocktrans %}</td>
+</tr>
+<tr>
+ <td>
+ <a href="{% url admin:auth_user_changelist %}">
+ {% blocktrans %}{{ authors }} authors{% endblocktrans %}
+ </a>
+ </td>
+ <td>
+ <a href="{% url admin:comments_comment_changelist %}?is_public__exact=0">
+ {% blocktrans %}{{ rejects }} rejected{% endblocktrans %}
+ </a>
+ </td>
+</tr>
diff --git a/objectapp/templates/admin/objectapp/widgets/_draft_gbobjects.html b/objectapp/templates/admin/objectapp/widgets/_draft_gbobjects.html
new file mode 100644
index 0000000..ea7fca9
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/_draft_gbobjects.html
@@ -0,0 +1,37 @@
+{% load i18n objectapp_tags %}
+
+{% for gbobject in gbobjects %}
+<tr>
+ <th>
+ <a href="{% url admin:objectapp_gbobject_change gbobject.pk %}"
+ title="{% trans "Edit the gbobject" %}">
+ {{ gbobject.title }}
+ </a>
+ {% trans "on" %}
+ <abbr>{{ gbobject.creation_date|date:"DATE_FORMAT" }}</abbr>
+ {{ gbobject.html_content|truncatewords_html:20|safe }}
+ </th>
+ <td>
+ <a href="{{ gbobject.get_absolute_url }}" class="previewlink">
+ {% trans "Preview" %}
+ </a>
+ </td>
+</tr>
+{% empty %}
+<tr>
+ <th colspan="2">
+ {% trans "No draft gbobjects." %}
+ </th>
+</tr>
+{% endfor %}
+
+{% if gbobjects %}
+<tr>
+ <th colspan="2">
+ <a href="{% url admin:objectapp_gbobject_changelist %}?status__exact=0"
+ title="{% trans "View all draft gbobjects" %}" class="managelink">
+ {% trans "View all draft gbobjects" %}
+ </a>
+ </th>
+</tr>
+{% endif %}
diff --git a/objectapp/templates/admin/objectapp/widgets/_recent_comments.html b/objectapp/templates/admin/objectapp/widgets/_recent_comments.html
new file mode 100644
index 0000000..afa65e3
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/_recent_comments.html
@@ -0,0 +1,45 @@
+{% load i18n objectapp_tags %}
+
+{% for comment in comments %}
+<tr>
+ <th>
+ <img src="{% get_gravatar comment.email 40 "G" %}"
+ class="gravatar photo" alt="{{ comment.user_name }}" />
+ </th>
+ <td>
+ {% with comment.content_object as gbobject %}
+ <strong>{{ comment.user_name }}</strong> {% trans "in" %}
+ <strong>
+ <a href="{{ gbobject.get_absolute_url }}#comment_{{ comment.pk }}"
+ title="{% trans "Comment on" %} {{ gbobject.title }}">
+ {{ gbobject.title }}
+ </a>
+ </strong>
+ {% endwith %}
+ {{ comment.comment|truncatewords:20|linebreaks }}
+ </td>
+ <td>
+ <a href="{% url admin:comments_comment_change comment.pk %}"
+ title="{% trans "Edit the comment" %}" class="changelink">
+ {% trans "Edit" %}
+ </a>
+ </td>
+</tr>
+{% empty %}
+<tr>
+ <th colspan="3">
+ {% trans "No comments yet." %}
+ </th>
+</tr>
+{% endfor %}
+
+{% if comments %}
+<tr>
+ <th colspan="3">
+ <a href="{% url admin:comments_comment_changelist %}"
+ title="{% trans "Manage the comments" %}" class="managelink">
+ {% trans "Manage the comments" %}
+ </a>
+ </th>
+</tr>
+{% endif %}
diff --git a/objectapp/templates/admin/objectapp/widgets/_recent_linkbacks.html b/objectapp/templates/admin/objectapp/widgets/_recent_linkbacks.html
new file mode 100644
index 0000000..89618eb
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/_recent_linkbacks.html
@@ -0,0 +1,31 @@
+{% load i18n %}
+
+{% for linkback in linkbacks %}
+<tr>
+ <th>
+ {% with linkback.content_object as gbobject %}
+ <a href="{{ linkback.url }}">{{ linkback.user_name }}</a>
+ {% trans "made a linkback on" %}
+ <a href="{{ gbobject.get_absolute_url }}#{{ linkback.flags.all.0.flag }}_{{ linkback.pk }}"
+ title="{{ gbobject.title }}">
+ {{ gbobject.title }}
+ </a>
+ {% endwith %}
+ {{ linkback.comment|truncatewords:20|linebreaks }}
+ </th>
+ <td>
+ <a href="{% url admin:comments_comment_change linkback.pk %}"
+ title="{% trans "Edit the linkback" %}" class="changelink">
+ {% trans "Edit" %}
+ </a>
+ </td>
+</tr>
+{% empty %}
+<tr>
+ <th colspan="2">
+ {% trans "No linkbacks yet." %}
+ </th>
+</tr>
+{% endfor %}
+
+
diff --git a/objectapp/templates/admin/objectapp/widgets/base.html b/objectapp/templates/admin/objectapp/widgets/base.html
new file mode 100644
index 0000000..d0790cd
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/base.html
@@ -0,0 +1,6 @@
+<div id="{% block module_id %}{% endblock %}" class="module">
+ <table summary="{% block summary %}{% endblock %}">
+ <caption>{% block title %}{% endblock %}</caption>
+ {% block content %}{% endblock %}
+ </table>
+</div>
diff --git a/objectapp/templates/admin/objectapp/widgets/content_stats.html b/objectapp/templates/admin/objectapp/widgets/content_stats.html
new file mode 100644
index 0000000..ece7f58
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/content_stats.html
@@ -0,0 +1,12 @@
+{% extends "admin/objectapp/widgets/base.html" %}
+{% load i18n objectapp_admin_tags %}
+
+{% block module_id %}contents{% endblock %}
+
+{% block summary %}{% trans "Today" %}{% endblock %}
+{% block title %}{% trans "Today" %}{% endblock %}
+
+{% block content %}
+ {% get_content_stats %}
+{% endblock %}
+
diff --git a/objectapp/templates/admin/objectapp/widgets/draft_gbobjects.html b/objectapp/templates/admin/objectapp/widgets/draft_gbobjects.html
new file mode 100644
index 0000000..e59ea70
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/draft_gbobjects.html
@@ -0,0 +1,12 @@
+{% extends "admin/objectapp/widgets/base.html" %}
+{% load i18n objectapp_admin_tags %}
+
+{% block module_id %}drafts{% endblock %}
+
+{% block summary %}{% trans "Draft gbobjects" %}{% endblock %}
+{% block title %}{% trans "Draft gbobjects" %}{% endblock %}
+
+{% block content %}
+ {% get_draft_gbobjects %}
+{% endblock %}
+
diff --git a/objectapp/templates/admin/objectapp/widgets/quickpost.html b/objectapp/templates/admin/objectapp/widgets/quickpost.html
new file mode 100644
index 0000000..6bf0150
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/quickpost.html
@@ -0,0 +1,40 @@
+{% load i18n %}
+<div id="quickpost" class="module">
+ <form method="post" action="{% url objectapp_gbobject_quick_post %}">
+ {% csrf_token %}
+ <table summary="{% trans "Quick publishing" %}">
+ <caption>{% trans "Quick publishing" %}</caption>
+ <tr>
+ <th>
+ <label for="id_title" class="required">{% trans "Title" %}:</label>
+ </th>
+ <td>
+ <input name="title" class="vTextField" maxlength="255" id="id_title" type="text" />
+ </td>
+ </tr>
+ <tr>
+ <th>
+ <label for="id_content" class="required">{% trans "Content" %}:</label>
+ </th>
+ <td>
+ <textarea id="id_content" rows="7" name="content" class="vLargeTextField"></textarea>
+ </td>
+ </tr>
+ <tr>
+ <th>
+ <label for="id_tags">{% trans "Tags" %}:</label>
+ </th>
+ <td>
+ <input name="tags" class="vTextField" maxlength="255" id="id_tags" type="text" />
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <input type="submit" value="{% trans "Save as draft" %}" name="save_draft" class="button" />
+ <input type="reset" value="{% trans "Reset" %}" class="button" />
+ <input type="submit" value="{% trans "Publish" %}" class="default" name="save" />
+ </td>
+ </tr>
+ </table>
+ </form>
+</div>
diff --git a/objectapp/templates/admin/objectapp/widgets/recent_comments.html b/objectapp/templates/admin/objectapp/widgets/recent_comments.html
new file mode 100644
index 0000000..e2a2127
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/recent_comments.html
@@ -0,0 +1,12 @@
+{% extends "admin/objectapp/widgets/base.html" %}
+{% load i18n objectapp_tags %}
+
+{% block module_id %}comments{% endblock %}
+
+{% block summary %}{% trans "Recent comments" %}{% endblock %}
+{% block title %}{% trans "Recent comments" %}{% endblock %}
+
+{% block content %}
+ {% get_recent_comments 3 "admin/objectapp/widgets/_recent_comments.html" %}
+{% endblock %}
+
diff --git a/objectapp/templates/admin/objectapp/widgets/recent_linkbacks.html b/objectapp/templates/admin/objectapp/widgets/recent_linkbacks.html
new file mode 100644
index 0000000..e104aa5
--- /dev/null
+++ b/objectapp/templates/admin/objectapp/widgets/recent_linkbacks.html
@@ -0,0 +1,12 @@
+{% extends "admin/objectapp/widgets/base.html" %}
+{% load i18n objectapp_tags %}
+
+{% block module_id %}linkbacks{% endblock %}
+
+{% block summary %}{% trans "Recent linkbacks" %}{% endblock %}
+{% block title %}{% trans "Recent linkbacks" %}{% endblock %}
+
+{% block content %}
+ {% get_recent_linkbacks 5 "admin/objectapp/widgets/_recent_linkbacks.html" %}
+{% endblock %}
+
diff --git a/objectapp/templates/comments/comment_authors_email.txt b/objectapp/templates/comments/comment_authors_email.txt
new file mode 100644
index 0000000..e32c876
--- /dev/null
+++ b/objectapp/templates/comments/comment_authors_email.txt
@@ -0,0 +1,6 @@
+{% load i18n %}{% trans "Author" %}: {{ comment.userinfo.name }} {{ comment.userinfo.url }}
+
+{% trans "Comment" %}:
+{{ comment.comment }}
+
+{% trans "View this comment" %}: {{ protocol }}://{{ site }}{{ content_object.get_absolute_url }}#comment_{{ comment.pk }}
diff --git a/objectapp/templates/comments/comment_notification_email.txt b/objectapp/templates/comments/comment_notification_email.txt
new file mode 100644
index 0000000..996166b
--- /dev/null
+++ b/objectapp/templates/comments/comment_notification_email.txt
@@ -0,0 +1,14 @@
+{% load i18n %}{% trans "Author" %}: {{ comment.userinfo.name }} {{ comment.userinfo.url }}
+{% trans "Email" %}: {{ comment.userinfo.email }}
+{% trans "IP" %}: {{ comment.ip_address }}
+
+{% trans "Comment" %}:
+{{ comment.comment }}
+
+{% trans "View this comment" %}: {{ protocol }}://{{ site }}{{ content_object.get_absolute_url }}#comment_{{ comment.pk }}
+
+{% trans "Flag this comment" %}: {{ protocol }}://{{ site }}{% url comments-flag comment.pk %}
+
+{% trans "Delete this comment" %}: {{ protocol }}://{{ site }}{% url comments-delete comment.pk %}
+
+{% trans "Approve this comment" %}: {{ protocol }}://{{ site }}{% url comments-approve comment.pk %}
diff --git a/objectapp/templates/comments/comment_reply_email.txt b/objectapp/templates/comments/comment_reply_email.txt
new file mode 100644
index 0000000..e32c876
--- /dev/null
+++ b/objectapp/templates/comments/comment_reply_email.txt
@@ -0,0 +1,6 @@
+{% load i18n %}{% trans "Author" %}: {{ comment.userinfo.name }} {{ comment.userinfo.url }}
+
+{% trans "Comment" %}:
+{{ comment.comment }}
+
+{% trans "View this comment" %}: {{ protocol }}://{{ site }}{{ content_object.get_absolute_url }}#comment_{{ comment.pk }}
diff --git a/objectapp/templates/comments/objectapp/gbobject/form.html b/objectapp/templates/comments/objectapp/gbobject/form.html
new file mode 100644
index 0000000..a61758d
--- /dev/null
+++ b/objectapp/templates/comments/objectapp/gbobject/form.html
@@ -0,0 +1,22 @@
+{% load comments i18n %}
+
+<form action="{% comment_form_target %}" method="post">
+ {% csrf_token %}
+ <fieldset>
+ <input type="hidden" name="next" value="{% url objectapp_discussion_success %}"/>
+ <legend>{% trans "Post your comment" %}</legend>
+ {% for field in form %}
+ {% if field.is_hidden %}{{ field }}{% else %}
+ <div{% if field.errors %} class="error"{% endif %}{% ifequal field.name "honeypot" %} style="display:none;"{% endifequal %}>
+ {{ field.label_tag }}
+ {% if field.errors %}{{ field.errors }}{% else %}<br />{% endif %}
+ {{ field }}
+ </div>
+ {% endif %}
+ {% endfor %}
+ <p class="submit">
+ <input type="submit" name="submit" class="submit-post" value="{% trans "Post" %}" />
+ <input type="submit" name="preview" class="submit-preview" value="{% trans "Preview" %}" />
+ </p>
+ </fieldset>
+</form>
diff --git a/objectapp/templates/comments/objectapp/gbobject/posted.html b/objectapp/templates/comments/objectapp/gbobject/posted.html
new file mode 100644
index 0000000..8db7d06
--- /dev/null
+++ b/objectapp/templates/comments/objectapp/gbobject/posted.html
@@ -0,0 +1,13 @@
+{% extends "objectapp/base.html" %}
+{% load i18n %}
+
+{% block title %}{% trans "Thanks for your comment" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Thanks for your comment" %}</h2>
+<p>
+ <a href="{% url objectapp_gbobject_archive_index %}" title="{% trans "Return to gbobject list" %}">
+ {% trans "Return to gbobject list" %}
+ </a>
+</p>
+{% endblock %}
diff --git a/objectapp/templates/comments/objectapp_gbobject_preview.html b/objectapp/templates/comments/objectapp_gbobject_preview.html
new file mode 100644
index 0000000..47eaeed
--- /dev/null
+++ b/objectapp/templates/comments/objectapp_gbobject_preview.html
@@ -0,0 +1,37 @@
+{% extends "objectapp/base.html" %}
+{% load i18n objectapp_tags comments %}
+
+{% block title %}{% trans "Comment preview" %}{% endblock %}
+
+{% block content %}
+ {% if form.errors %}
+ <h2>
+ {% blocktrans count form.errors|length as errors %}Please correct following error.{% plural %}Please correct following errors.{% endblocktrans %}
+ </h2>
+ {% else %}
+ <h2>{% trans "Preview of the comment" %}</h2>
+ <ol id="comment-list" class="span-15 last">
+ <li id="comment_preview" class="comment vcard {% cycle box1,box2 %} span-15 last">
+ <div class="span-2">
+ <img src="{% get_gravatar form.cleaned_data.email 60 "G" %}" class="gravatar photo" alt="form.cleaned_data.name" />
+ </div>
+ <div class="comment-body span-13 last">
+ <div class="comment-author span-13 last">
+ {% if form.cleaned_data.url %}
+ <a href="{{ form.cleaned_data.url }}" class="fn url">{{ form.cleaned_data.name }}</a>
+ {% else %}
+ {{ form.cleaned_data.name }}
+ {% endif %}
+ {% trans "on" %} {% now "m/d/Y P" %}
+ </div>
+ <div class="span-13 last">
+ {{ comment|linebreaks }}
+ </div>
+ </div>
+ </li>
+ </ol>
+ {% endif %}
+ <div class="commentForm span-16 last">
+ {% include "comments/objectapp/gbobject/form.html" %}
+ </div>
+{% endblock %}
diff --git a/objectapp/templates/feeds/comment_description.html b/objectapp/templates/feeds/comment_description.html
new file mode 100644
index 0000000..8141388
--- /dev/null
+++ b/objectapp/templates/feeds/comment_description.html
@@ -0,0 +1 @@
+{{ obj.comment }}
diff --git a/objectapp/templates/feeds/comment_title.html b/objectapp/templates/feeds/comment_title.html
new file mode 100644
index 0000000..f9ee8ab
--- /dev/null
+++ b/objectapp/templates/feeds/comment_title.html
@@ -0,0 +1,2 @@
+{% load i18n %}
+{{ obj.user_name }} {% trans "on" %} {{ obj.submit_date|date:"DATETIME_FORMAT" }}
diff --git a/objectapp/templates/feeds/discussion_description.html b/objectapp/templates/feeds/discussion_description.html
new file mode 100644
index 0000000..8141388
--- /dev/null
+++ b/objectapp/templates/feeds/discussion_description.html
@@ -0,0 +1 @@
+{{ obj.comment }}
diff --git a/objectapp/templates/feeds/discussion_title.html b/objectapp/templates/feeds/discussion_title.html
new file mode 100644
index 0000000..f9ee8ab
--- /dev/null
+++ b/objectapp/templates/feeds/discussion_title.html
@@ -0,0 +1,2 @@
+{% load i18n %}
+{{ obj.user_name }} {% trans "on" %} {{ obj.submit_date|date:"DATETIME_FORMAT" }}
diff --git a/objectapp/templates/feeds/gbobject_description.html b/objectapp/templates/feeds/gbobject_description.html
new file mode 100644
index 0000000..7afb28a
--- /dev/null
+++ b/objectapp/templates/feeds/gbobject_description.html
@@ -0,0 +1 @@
+{{ obj.html_content|safe }}
diff --git a/objectapp/templates/feeds/gbobject_title.html b/objectapp/templates/feeds/gbobject_title.html
new file mode 100644
index 0000000..91b32ca
--- /dev/null
+++ b/objectapp/templates/feeds/gbobject_title.html
@@ -0,0 +1 @@
+{{ obj.title|safe }}
diff --git a/objectapp/templates/feeds/pingback_description.html b/objectapp/templates/feeds/pingback_description.html
new file mode 100644
index 0000000..8141388
--- /dev/null
+++ b/objectapp/templates/feeds/pingback_description.html
@@ -0,0 +1 @@
+{{ obj.comment }}
diff --git a/objectapp/templates/feeds/pingback_title.html b/objectapp/templates/feeds/pingback_title.html
new file mode 100644
index 0000000..f9ee8ab
--- /dev/null
+++ b/objectapp/templates/feeds/pingback_title.html
@@ -0,0 +1,2 @@
+{% load i18n %}
+{{ obj.user_name }} {% trans "on" %} {{ obj.submit_date|date:"DATETIME_FORMAT" }}
diff --git a/objectapp/templates/feeds/trackback_description.html b/objectapp/templates/feeds/trackback_description.html
new file mode 100644
index 0000000..8141388
--- /dev/null
+++ b/objectapp/templates/feeds/trackback_description.html
@@ -0,0 +1 @@
+{{ obj.comment }}
diff --git a/objectapp/templates/feeds/trackback_title.html b/objectapp/templates/feeds/trackback_title.html
new file mode 100644
index 0000000..f9ee8ab
--- /dev/null
+++ b/objectapp/templates/feeds/trackback_title.html
@@ -0,0 +1,2 @@
+{% load i18n %}
+{{ obj.user_name }} {% trans "on" %} {{ obj.submit_date|date:"DATETIME_FORMAT" }}
diff --git a/objectapp/templates/objectapp/_gbobject_detail.html b/objectapp/templates/objectapp/_gbobject_detail.html
new file mode 100644
index 0000000..b9d1b91
--- /dev/null
+++ b/objectapp/templates/objectapp/_gbobject_detail.html
@@ -0,0 +1,148 @@
+{% load tagging_tags comments i18n %}
+<div id="gbobject-{{ object.pk }}" class="hgbobject{% if object.featured %} featured{% endif %} span-16 last">
+ <div class="gbobject-header span-16 last">
+ <h2 class="gbobject-title">
+ <a href="{{ object.get_absolute_url }}" title="{{ object.title }}" rel="bookmark">
+ {{ object.title }}
+ </a>
+ </h2>
+ <p class="gbobject-info">
+ {% if object.authors.count %}
+ {% trans "Written by" %}
+ {% for author in object.authors.all %}
+ <span class="vcard author">
+ <a href="{% url objectapp_author_detail author %}" class="fn nickname url" rel="author"
+ title="{% blocktrans with author as author %}Show {{ author }} gbobjects{% endblocktrans %}">{{ author }}</a>
+ </span>{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+ {% trans "on" %}
+ {% else %}
+ {% trans "Written on" %}
+ {% endif %}
+ <abbr class="published" title="{{ object.creation_date|date:"c" }}Z">{{ object.creation_date|date:"DATE_FORMAT" }}</abbr>
+ {% if object.objecttypes.count %}
+ {% trans "in" %}
+ {% for Objecttype in object.objecttypes.all %}
+ <a href="{{ Objecttype.get_absolute_url }}" title="{{ Objecttype }}" rel="tag Objecttype">{{ Objecttype }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}{% endif %}.
+ </p>
+
+</div>
+
+</div>
+<p>
+ <b>Name: </b> {{ object.title }} <br/>
+
+ {% if object.get_rendered_nbh.plural %}
+ <b> Plural Name :<nbsp></b>
+ {{object.get_rendered_nbh.plural}} <br/>
+ {% endif %}
+ {% if object.get_rendered_nbh.altnames %}
+ <b> Alternate names :<nbsp></b>
+ {{object.get_rendered_nbh.altnames}} <br/>
+{% endif %}
+
+ {% if object.get_rendered_nbh.member_of %}
+
+
+ <b> Member of Metatypes :<nbsp></b>
+
+ {% for mkey,mvalue in object.get_rendered_nbh.member_of.items %}
+ <a href="{{mvalue}}">{{mkey}}</a>;
+ {% endfor %}<br/>
+ {% endif %}
+
+ {% if object.get_rendered_nbh.relations %}
+
+
+ {% for relkey,relvalue in object.get_rendered_nbh.relations.items %}
+ <b>{{relkey}}:<nbsp></b>
+{% for relk, relv in relvalue.items %}
+<a href="{{relv}}">{{relk}}</a>;
+ {% endfor %}<br/>
+{% endfor %}
+{% endif %}
+
+ {% if object.get_rendered_nbh.attributes %}
+
+
+
+<b>Attributes: <nbsp> </b>
+
+{% for items in object.get_rendered_nbh.attributes %}
+ {{items}};
+{% endfor %} <br/>
+{% endif %}
+
+</p>
+
+
+
+
+ <div class="gbobject-body span-16 last">
+ {% if object.image %}
+ <div class="gbobject-image">
+ <p>
+ <a href="{{ object.get_absolute_url }}" rel="bookmark">
+<img src="{{ object.image.url }}" alt="{{ object.title }}" class="left" />
+ </a>
+ </p>
+ </div>
+ {% endif %}
+ <div class="gbobject-content">
+ {{ object_content }}
+ </div>
+ </div>
+
+ <div class="gbobject-footer">
+ <p class="gbobject-tags span-16 last">
+ <strong>{% trans "Tags" %}</strong> :
+ {% tags_for_object object as tag_list %}
+ {% for tag in tag_list %}
+ <a href="{% url objectapp_tag_detail tag %}"
+ title="Tag {{ tag }}" rel="tag">{{ tag }}</a>
+ {% empty %}
+ <span>{% trans "No tags" %}</span>
+ {% endfor %}
+ </p>
+
+ <p class="gbobject-shorturl span-16 last">
+ <strong>{% trans "Short url" %}</strong> :
+ <a href="{{ object.short_url }}" title="{{ object.title }}" rel="shortlink">
+ {{ object.short_url }}
+ </a>
+ </p>
+
+ <p class="gbobject-comments span-16 last">
+ <strong>{% trans "Discussions" %}</strong> :
+ {% with object.comments.count as comment_count %}
+ {% if comment_count %}
+ <a href="{{ object.get_absolute_url }}#comments" title="{{ object.title }}">
+ {% blocktrans count comment_count as comment_count %}{{ comment_count }} comment{% plural %}{{ comment_count }} comments{% endblocktrans %}
+ </a>
+ {% else %}
+ {% if object.comments_are_open %}
+ {% trans "No comments yet." %}
+ <a href="{{ object.get_absolute_url }}#comments" title="{{ object.title }}">
+ {% trans "Be first to comment!" %}
+ </a>
+ {% else %}
+ {% trans "Comments are closed." %}
+ {% endif %}
+ {% endif %}
+ {% endwith %}
+ {% with object.pingbacks.count as pingback_count %}
+ {% if pingback_count %}
+ <a href="{{ object.get_absolute_url }}#pingbacks" title="{{ object.title }}">
+ {% blocktrans count pingback_count as pingback_count %}{{ pingback_count }} pingback{% plural %}{{ pingback_count }} pingbacks{% endblocktrans %}
+ </a>
+ {% endif %}
+ {% endwith %}
+ {% with object.trackbacks.count as trackback_count %}
+ {% if trackback_count %}
+ <a href="{{ object.get_absolute_url }}#trackbacks" title="{{ object.title }}">
+ {% blocktrans count trackback_count as trackback_count %}{{ trackback_count }} trackback{% plural %}{{ trackback_count }} trackbacks{% endblocktrans %}
+ </a>
+ {% endif %}
+ {% endwith %}
+ </p>
+ </div>
diff --git a/objectapp/templates/objectapp/_header.html b/objectapp/templates/objectapp/_header.html
new file mode 100644
index 0000000..fa79f9d
--- /dev/null
+++ b/objectapp/templates/objectapp/_header.html
@@ -0,0 +1,56 @@
+{% load i18n grp_tags %}
+
+<div id="header">
+ <div class="branding">&nbsp;</div>
+ <!-- Title -->
+ <div class="admin-title">{% if grappelli_admin_title %}{{ grappelli_admin_title }}{% else %}{% get_admin_title %}{% endif %}</div>
+ {% if user.is_authenticated and user.is_staff %}
+ <ul id="user-tools">
+ <!-- Username -->
+ <li class="user-options-container collapse closed">
+ <a href="javascript://" class="user-options-handler collapse-handler">{% firstof user.first_name user.username %}</a>
+ <ul class="user-options">
+ <!-- Change Password -->
+ {% url admin:password_change as password_change_url %}
+ {% if password_change_url %}
+ <li><a href="{{ password_change_url }}">
+ {% else %}
+ <li><a href="{{ root_path }}password_change/">
+ {% endif %}
+ {% trans 'Change password' %}</a></li>
+ <!-- Logout -->
+ {% url admin:logout as logout_url %}
+ {% if logout_url %}
+ <li><a href="{{ logout_url }}">
+ {% else %}
+ <li><a href="{{ root_path }}logout/">
+ {% endif %}
+ {% trans 'Log out' %}</a></li>
+ </ul>
+ </li>
+ <!-- Userlinks -->
+ {% block userlinks %}
+ <!-- JS tests -->
+ {% url test-index as testindexurl %}
+ {% if testindexurl %}
+ <li><a href="{{ testindexurl }}">{% trans 'Tests' %}</a></li>
+ {% endif %}
+ <!-- Documentation -->
+ {% url django-admindocs-docroot as docsroot %}
+ {% if docsroot %}
+ <li><a href="{{ docsroot }}">{% trans 'Documentation' %}</a></li>
+ {% endif %}
+ {% endblock %}
+ </ul>
+ {% endif %}
+ {% block nav-global %}
+ {% endblock %}
+</div>
+
+<script type="text/javascript" charset="utf-8">
+ (function($) {
+ $(document).ready(function() {
+ $("div#header .collapse").grp_collapsible();
+ });
+ })(django.jQuery);
+</script>
diff --git a/objectapp/templates/objectapp/_narrative_detail.html b/objectapp/templates/objectapp/_narrative_detail.html
new file mode 100644
index 0000000..fc695d7
--- /dev/null
+++ b/objectapp/templates/objectapp/_narrative_detail.html
@@ -0,0 +1,117 @@
+<p>
+ <b>Name: </b> {{ object.title }} <br/>
+
+ {% if object.get_rendered_nbh.plural %}
+ <b> Plural Name :<nbsp></b>
+ {{object.get_rendered_nbh.plural}} <br/>
+ {% endif %}
+ {% if object.get_rendered_nbh.altnames %}
+ <b> Alternate names :<nbsp></b>
+ {{object.get_rendered_nbh.altnames}} <br/>
+ {% endif %}
+
+ {% if object.get_rendered_nbh.member_of %}
+
+
+ <b> Member of Metatypes :<nbsp></b>
+
+ {% for mkey,mvalue in object.get_rendered_nbh.member_of.items %}
+ <a href="{{mvalue}}">{{mkey}}</a>;
+ {% endfor %}<br/>
+ {% endif %}
+
+ {% if object.get_rendered_nbh.relations %}
+
+
+ {% for relkey,relvalue in object.get_rendered_nbh.relations.items %}
+ <b>{{relkey}}:<nbsp></b>
+ {% for relk, relv in relvalue.items %}
+ <a href="{{relv}}">{{relk}}</a>;
+ {% endfor %}<br/>
+ {% endfor %}
+ {% endif %}
+
+ {% if object.get_rendered_nbh.attributes %}
+
+
+
+ <b>Attributes: <nbsp> </b>
+
+ {% for items in object.get_rendered_nbh.attributes %}
+ {{items}};
+ {% endfor %} <br/>
+ {% endif %}
+
+</p>
+
+
+
+
+ <div class="gbobject-body span-16 last">
+ {% if object.image %}
+ <div class="gbobject-image">
+ <p>
+ <a href="{{ object.get_absolute_url }}" rel="bookmark">
+ <img src="{{ object.image.url }}" alt="{{ object.title }}" class="left" />
+ </a>
+ </p>
+ </div>
+ {% endif %}
+ <div class="gbobject-content">
+ {{ object_content }}
+ </div>
+ </div>
+
+ <div class="gbobject-footer">
+ <p class="gbobject-tags span-16 last">
+ <strong>{% trans "Tags" %}</strong> :
+ {% tags_for_object object as tag_list %}
+ {% for tag in tag_list %}
+ <a href="{% url objectapp_tag_detail tag %}"
+ title="Tag {{ tag }}" rel="tag">{{ tag }}</a>
+ {% empty %}
+ <span>{% trans "No tags" %}</span>
+ {% endfor %}
+ </p>
+
+ <p class="gbobject-shorturl span-16 last">
+ <strong>{% trans "Short url" %}</strong> :
+ <a href="{{ object.short_url }}" title="{{ object.title }}" rel="shortlink">
+ {{ object.short_url }}
+ </a>
+ </p>
+
+ <p class="gbobject-comments span-16 last">
+ <strong>{% trans "Discussions" %}</strong> :
+ {% with object.comments.count as comment_count %}
+ {% if comment_count %}
+ <a href="{{ object.get_absolute_url }}#comments" title="{{ object.title }}">
+ {% blocktrans count comment_count as comment_count %}{{ comment_count }} comment{% plural %}{{ comment_count }} comments{% endblocktrans %}
+ </a>
+ {% else %}
+ {% if object.comments_are_open %}
+ {% trans "No comments yet." %}
+ <a href="{{ object.get_absolute_url }}#comments" title="{{ object.title }}">
+ {% trans "Be first to comment!" %}
+ </a>
+ {% else %}
+ {% trans "Comments are closed." %}
+ {% endif %}
+ {% endif %}
+ {% endwith %}
+ {% with object.pingbacks.count as pingback_count %}
+ {% if pingback_count %}
+ <a href="{{ object.get_absolute_url }}#pingbacks" title="{{ object.title }}">
+ {% blocktrans count pingback_count as pingback_count %}{{ pingback_count }} pingback{% plural %}{{ pingback_count }} pingbacks{% endblocktrans %}
+ </a>
+ {% endif %}
+ {% endwith %}
+ {% with object.trackbacks.count as trackback_count %}
+ {% if trackback_count %}
+ <a href="{{ object.get_absolute_url }}#trackbacks" title="{{ object.title }}">
+ {% blocktrans count trackback_count as trackback_count %}{{ trackback_count }} trackback{% plural %}{{ trackback_count }} trackbacks{% endblocktrans %}
+ </a>
+ {% endif %}
+ {% endwith %}
+ </p>
+ </div>
diff --git a/objectapp/templates/objectapp/_subtype_detail.html b/objectapp/templates/objectapp/_subtype_detail.html
new file mode 100644
index 0000000..e77ced8
--- /dev/null
+++ b/objectapp/templates/objectapp/_subtype_detail.html
@@ -0,0 +1 @@
+nothing as yet
diff --git a/objectapp/templates/objectapp/author/gbobject_list.html b/objectapp/templates/objectapp/author/gbobject_list.html
new file mode 100644
index 0000000..d85e241
--- /dev/null
+++ b/objectapp/templates/objectapp/author/gbobject_list.html
@@ -0,0 +1 @@
+{% extends "objectapp/gbobject_list.html" %}
diff --git a/objectapp/templates/objectapp/author_list.html b/objectapp/templates/objectapp/author_list.html
new file mode 100644
index 0000000..fbb5dcb
--- /dev/null
+++ b/objectapp/templates/objectapp/author_list.html
@@ -0,0 +1,27 @@
+{% extends "objectapp/base.html" %}
+{% load i18n %}
+
+{% block meta-description %}{% trans "Author list" %}{% endblock %}
+
+{% block title %}{% trans "Authors" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Author list" %}</h2>
+<div>
+ <ul>
+ {% for object in object_list %}
+ <li class="vcard">
+ <a href="{% url objectapp_author_detail object.username %}"
+ title="{{ object.username }}" class="fn nickname url">
+ {{ object.username }}
+ </a>
+ {% blocktrans count object.gbobjects_published.count as gbobject_count %}{{ gbobject_count }} gbobject{% plural %}{{ gbobject_count }} gbobjects{% endblocktrans %}
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No authors yet." %}
+ </li>
+ {% endfor %}
+ </ul>
+</div>
+{% endblock %}
diff --git a/objectapp/templates/objectapp/base.html b/objectapp/templates/objectapp/base.html
new file mode 100644
index 0000000..40e6324
--- /dev/null
+++ b/objectapp/templates/objectapp/base.html
@@ -0,0 +1,107 @@
+{% extends "objectapp/skeleton.html" %}
+{% load objectapp_tags i18n %}
+
+ {% block meta %}
+ <meta name="generator" content="Objectapp {{ OBJECTAPP_VERSION }}" />
+{% endblock %}
+
+{% block link %}
+ <link rel="index" href="{% url objectapp_gbobject_archive_index %}" />
+ <link rel="EditURI" type="application/rsd+xml" href="{% url objectapp_rsd %}" />
+ <link rel="wlwmanifest" type="application/wlwmanifest+xml" href="{% url objectapp_wlwmanifest %}" />
+ <link rel="search" type="application/opensearchdescription+xml" title="Objectapp's Blog" href="{% url objectapp_opensearch %}" />
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed of latest gbobjects" %}" href="{% url objectapp_gbobject_latest_feed %}" />
+ {% get_archives_gbobjects "objectapp/tags/archives_gbobjects_link.html" %}
+{% endblock %}
+
+{% block breadcrumbs %}
+ {% objectapp_breadcrumbs %}
+{% endblock %}
+
+{% block sidebar %}
+ <img src="{{ STATIC_URL }}objectapp/img/rss.png" alt="?" width="20" height="" />
+ <a href="{% url objectapp_gbobject_latest_feed %}" class="feeds"><h4>Rssfeed</h4></a>
+
+<div class="search">
+ <h3>{% trans "Search" %}</h3>
+ <form method="get" id="searchform" action="{% url objectapp_gbobject_search %}">
+ <p>
+ <input type="text" value="{% trans "Keywords..." %}" name="pattern" id="searchbox" onfocus="this.value=''" />
+ <input type="submit" class="submitbutton" value="OK" />
+ <a title="{% trans 'You can use - to exclude words or phrases, &quot;double quotes&quot; for exact phrases and the AND/OR boolean operators combined with parenthesis for complex searchs.' %}">
+ <img src="{{ STATIC_URL }}objectapp/img/help.png" alt="?" width="14" height="14" />
+ </a>
+ </p>
+ </form>
+</div>
+<div class="objecttypes">
+ <h3>{% trans "Objecttypes" %}</h3>
+ {% get_objecttypes %}
+</div>
+<div class="authors">
+ <h3>{% trans "Authors" %}</h3>
+ {% get_authors %}
+</div>
+<div class="calendar">
+ <h3>{% trans "Calendar" %}</h3>
+ {% get_calendar_gbobjects %}
+</div>
+<div class="tags">
+ <h3>{% trans "Tags" %}</h3>
+ {% get_tag_cloud %}
+</div>
+<div class="recents">
+ <h3>{% trans "Recent gbobjects" %}</h3>
+ {% get_recent_gbobjects 5 %}
+</div>
+<div class="comments">
+ <h3>{% trans "Recent comments" %}</h3>
+ {% get_recent_comments 5 %}
+</div>
+<div class="linkbacks">
+ <h3>{% trans "Recent linkbacks" %}</h3>
+ {% get_recent_linkbacks 5 %}
+</div>
+<div class="randoms">
+ <h3>{% trans "Random gbobjects" %}</h3>
+ {% get_random_gbobjects 5 %}
+</div>
+<div class="populars">
+ <h3>{% trans "Popular gbobjects" %}</h3>
+ {% get_popular_gbobjects 5 %}
+</div>
+<div class="archives">
+ <h3>{% trans "Archives" %}</h3>
+ {% get_archives_gbobjects_tree %}
+</div>
+{% if user.is_authenticated %}
+<div class="tools">
+ <h3>{% trans "Tools" %}</h3>
+ <ul>
+ {% if perms.objectapp %}
+ <li>
+ <a href="{% url admin:app_list "objectapp" %}" title="{% trans "Dashboard" %}">
+ {% trans "Dashboard" %}
+ </a>
+ </li>
+ {% endif %}
+ {% if perms.objectapp.add_gbobject %}
+ <li>
+ <a href="{% url admin:objectapp_gbobject_add %}" title="{% trans "Post an gbobject" %}">
+ {% trans "Post an gbobject" %}
+ </a>
+ </li>
+ {% endif %}
+ {% block admin_tools %}
+ {% endblock %}
+ <li>
+ <a href="{% url admin:logout %}" title="{% trans "Log out" %}">
+ {% trans "Log out" %}
+ </a>
+ </li>
+ </ul>
+</div>
+{% endif %}
+{% endblock %}
+
+
diff --git a/objectapp/templates/objectapp/cms/gbobject_detail.html b/objectapp/templates/objectapp/cms/gbobject_detail.html
new file mode 100644
index 0000000..a93d0f0
--- /dev/null
+++ b/objectapp/templates/objectapp/cms/gbobject_detail.html
@@ -0,0 +1,8 @@
+{% load i18n %}
+{% for gbobject in gbobjects %}
+ {% with object=gbobject object_content=gbobject.html_content|truncatewords_html:100|safe %}
+ {% include "objectapp/_gbobject_detail.html" %}
+ {% endwith %}
+{% empty %}
+ <p class="notice">{% trans "No gbobjects yet." %}</p>
+{% endfor %}
diff --git a/objectapp/templates/objectapp/cms/gbobject_list.html b/objectapp/templates/objectapp/cms/gbobject_list.html
new file mode 100644
index 0000000..9e9c924
--- /dev/null
+++ b/objectapp/templates/objectapp/cms/gbobject_list.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+<ul>
+ {% for gbobject in gbobjects %}
+ <li>
+ <a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No gbobjects yet." %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/objectapp/templates/objectapp/cms/random_gbobjects.html b/objectapp/templates/objectapp/cms/random_gbobjects.html
new file mode 100644
index 0000000..e215e06
--- /dev/null
+++ b/objectapp/templates/objectapp/cms/random_gbobjects.html
@@ -0,0 +1,4 @@
+{% load objectapp_tags %}
+
+{% get_random_gbobjects number_of_gbobjects template_to_render %}
+
diff --git a/objectapp/templates/objectapp/gbobject_archive.html b/objectapp/templates/objectapp/gbobject_archive.html
new file mode 100644
index 0000000..26f06a9
--- /dev/null
+++ b/objectapp/templates/objectapp/gbobject_archive.html
@@ -0,0 +1,30 @@
+{% extends "objectapp/gbobject_list.html" %}
+{% load i18n objectapp_tags %}
+
+{% block meta-description %}{% trans "Latest gbobjects" %}{% endblock %}
+
+{% block link %}
+ {{ block.super }}
+ <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}objectapp/css/slider.css" />
+{% endblock %}
+
+{% block script %}
+ {{ block.super }}
+ <script type="text/javascript" src="{{ STATIC_URL }}objectapp/js/jquery.js"></script>
+ <script type="text/javascript" src="{{ STATIC_URL }}objectapp/js/jquery.ui.js"></script>
+ <script type="text/javascript">
+ $(document).ready(function(){
+ $("#slider").tabs({ fx:{ opacity: "toggle"}}).tabs("rotate", 5000, true);
+ $("#slider").hover(
+ function() { $("#slider").tabs("rotate", 0, true); },
+ function() { $("#slider").tabs("rotate", 5000, true); });
+ });
+ </script>
+{% endblock %}
+
+{% block title %}{% trans "Latest gbobjects" %}{% endblock %}
+
+{% block content %}
+{% get_featured_gbobjects 5 "objectapp/tags/slider_gbobjects.html" %}
+{{ block.super }}
+{% endblock %}
diff --git a/objectapp/templates/objectapp/gbobject_archive_day.html b/objectapp/templates/objectapp/gbobject_archive_day.html
new file mode 100644
index 0000000..99e140c
--- /dev/null
+++ b/objectapp/templates/objectapp/gbobject_archive_day.html
@@ -0,0 +1,23 @@
+{% extends "objectapp/gbobject_list.html" %}
+{% load i18n %}
+
+{% block meta-description %}{% trans "Archives" %} {{ day|date:"DATE_FORMAT" }}{% endblock %}
+
+{% block title %}{% trans "Archives" %} {{ day|date:"DATE_FORMAT" }}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Archives" %} {{ day|date:"DATE_FORMAT" }}</h1>
+
+{{ block.super }}
+
+<!--<ul>
+ <li>
+ <a href="{% url objectapp_gbobject_archive_day previous_day|date:"Y" previous_day|date:"m" previous_day|date:"d" %}" rel="archives">{{ previous_day|date:"DATE_FORMAT" }}</a>
+ </li>
+ {% if next_day %}
+ <li>
+ <a href="{% url objectapp_gbobject_archive_day next_day|date:"Y" next_day|date:"m" next_day|date:"d" %}" rel="archives">{{ next_day|date:"DATE_FORMAT" }}</a>
+ </li>
+ {% endif %}
+</ul>-->
+{% endblock %}
diff --git a/objectapp/templates/objectapp/gbobject_archive_month.html b/objectapp/templates/objectapp/gbobject_archive_month.html
new file mode 100644
index 0000000..2caa7c7
--- /dev/null
+++ b/objectapp/templates/objectapp/gbobject_archive_month.html
@@ -0,0 +1,31 @@
+{% extends "objectapp/gbobject_list.html" %}
+{% load i18n %}
+
+{% block meta-description %}{% trans "Archives" %} {{ month|date:"YEAR_MONTH_FORMAT" }}{% endblock %}
+
+{% block title %}{% trans "Archives" %} {{ month|date:"YEAR_MONTH_FORMAT" }}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Archives" %} {{ month|date:"YEAR_MONTH_FORMAT" }}</h1>
+
+{{ block.super }}
+
+<h2>{% trans "Daily archives" %}</h2>
+<ul>
+ {% for date in date_list %}
+ <li>
+ <a href="{% url objectapp_gbobject_archive_day date|date:"Y" date|date:"m" date|date:"d" %}" rel="archives">{{ date|date:"DATE_FORMAT" }}</a>
+ </li>
+ {% endfor %}
+</ul>
+<!--<ul>
+ <li>
+ <a href="{% url objectapp_gbobject_archive_month previous_month|date:"Y" previous_month|date:"m" %}" rel="archives">{{ previous_month|date:"YEAR_MONTH_FORMAT" }}</a>
+ </li>
+ {% if next_month %}
+ <li>
+ <a href="{% url objectapp_gbobject_archive_month next_month|date:"Y" next_month|date:"m" %}" rel="archives">{{ next_month|date:"YEAR_MONTH_FORMAT" }}</a>
+ </li>
+ {% endif %}
+</ul>-->
+{% endblock %}
diff --git a/objectapp/templates/objectapp/gbobject_archive_year.html b/objectapp/templates/objectapp/gbobject_archive_year.html
new file mode 100644
index 0000000..eeca042
--- /dev/null
+++ b/objectapp/templates/objectapp/gbobject_archive_year.html
@@ -0,0 +1,21 @@
+{% extends "objectapp/gbobject_list.html" %}
+{% load i18n %}
+
+{% block meta-description %}{% trans "Archives" %} {{ year }}{% endblock %}
+
+{% block title %}{% trans "Archives" %} {{ year }}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Archives" %} {{ year }}</h1>
+
+{{ block.super }}
+
+<h2>{% trans "Monthly archives" %}</h2>
+<ul>
+ {% for date in date_list %}
+ <li>
+ <a href="{% url objectapp_gbobject_archive_month year date|date:"m" %}" rel="archives">{{ date|date:"YEAR_MONTH_FORMAT" }}</a>
+ </li>
+ {% endfor %}
+</ul>
+{% endblock %}
diff --git a/objectapp/templates/objectapp/gbobject_detail.html b/objectapp/templates/objectapp/gbobject_detail.html
new file mode 100644
index 0000000..120fbc9
--- /dev/null
+++ b/objectapp/templates/objectapp/gbobject_detail.html
@@ -0,0 +1,242 @@
+{% extends "objectapp/base.html" %}
+{% load i18n comments objectapp_tags %}
+
+{% block title %}{{ object.title }}{% endblock %}
+
+{% block meta-description %}{% if object.excerpt %}{{ object.excerpt|striptags }}{% else %}{{ object.content|striptags|truncatewords:100 }}{% endif %}{% endblock %}
+
+{% block meta-keywords %}{% if object.tags %}{{ object.tags }}{% else %}{{ block.super }}{% endif %}{% endblock %}
+
+{% block link %}
+ {{ block.super }}
+ {% with object.previous_gbobject as previous_gbobject %}{% if previous_gbobject %}
+ <link rel="prev" title="{{ previous_gbobject.title }}" href="{{ previous_gbobject.get_absolute_url }}" />
+ {% endif %}{% endwith %}
+ {% with object.next_gbobject as next_gbobject %}{% if next_gbobject %}
+ <link rel="next" title="{{ next_gbobject.title }}" href="{{ next_gbobject.get_absolute_url }}" />
+ {% endif %}{% endwith %}
+ <link rel="shortlink" href="{{ object.short_url }}" />
+ <link rel="canonical" href="{{ object.get_absolute_url }}" />
+ {% with year=object.creation_date|date:"Y" month=object.creation_date|date:"m" day=object.creation_date|date:"d" %}
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed of discussions on" %} '{{ object.title }}'"
+ href="{% url objectapp_gbobject_discussion_feed year month day object.slug %}" />
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed of comments on" %} '{{ object.title }}'"
+ href="{% url objectapp_gbobject_comment_feed year month day object.slug %}" />
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed of pingbacks on" %} '{{ object.title }}'"
+ href="{% url objectapp_gbobject_pingback_feed year month day object.slug %}" />
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed of trackbacks on" %} '{{ object.title }}'"
+ href="{% url objectapp_gbobject_trackback_feed year month day object.slug %}" />
+ {% endwith %}
+{% endblock %}
+
+
+
+
+{% block content %}
+{% block gbobject-content %}
+
+ {% with object.html_content|safe as object_content %}
+ {% include "objectapp/_gbobject_detail.html" %}
+ {% endwith %}
+{% endblock %}
+
+
+
+{% block gbobject-widgets %}
+<div class="gbobject-widgets span-16 last">
+ {% with object.next_gbobject as next_gbobject %}
+ {% if next_gbobject %}
+
+ <div class="gbobject-next">
+ <h3>{% trans "Next object" %}</h3>
+ <ul>
+ <li>
+<a href="{{ next_gbobject.get_absolute_url }}" title="{{ next_gbobject.title }}" rel="next">
+{{ next_gbobject.title }}
+
+</a>
+ </li>
+ </ul>
+ </div>
+ {% endif %}
+ {% endwith %}
+
+
+
+ {% with object.previous_gbobject as previous_gbobject %}
+ {% if previous_gbobject %}
+ <div class="gbobject-previous">
+ <h3>{% trans "Previous object" %}</h3>
+ <ul>
+ <li>
+<a href="{{ previous_gbobject.get_absolute_url }}" title="{{ previous_gbobject.title }}" rel="prev">
+{{ previous_gbobject.title }}
+</a>
+ </li>
+ </ul>
+ </div>
+ {% endif %}
+ {% endwith %}
+ {% if object.related_published %}
+ <div class="gbobject-related">
+ <h3>{% trans "Related objects" %}</h3>
+ <ul>
+ {% for gbobject in object.related_published %}
+ <li>
+<a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ </div>
+ {% endif %}
+ <div class="gbobject-similar">
+ <h3>{% trans "Similar objects" %}</h3>
+ {% get_similar_gbobjects 5 %}
+ </div>
+</div>
+{% endblock %}
+
+
+
+
+
+
+{% block gbobject-comments %}
+<div id="comments" class="span-16 last">
+ <h3>{% trans "Comments" %}</h3>
+ {% with object.comments as comment_list %}
+ {% if comment_list.count %}
+ <ol id="comment-list" class="span-15 last">
+ {% for comment in comment_list %}
+ <li id="comment_{{ comment.pk }}" class="comment vcard {% cycle box1,box2 %}{% if comment.user in object.authors.all %} post-author{% endif %} span-15 last">
+ <div class="span-2">
+<img src="{% get_gravatar comment.email 60 "G" %}" class="gravatar photo" alt="{{ comment.user_name }}"/>
+ </div>
+ <div class="comment-body span-13 last">
+<div class="comment-author span-13 last">
+{% if comment.url %}
+<a href="{{ comment.url }}" class="fn url">{{ comment.user_name }}</a>
+{% else %}
+{{ comment.user_name }}
+{% endif %}
+{% trans "on" %} {{ comment.submit_date|date:"SHORT_DATETIME_FORMAT" }}
+</div>
+<div class="span-13 last">
+{{ comment.comment|linebreaks }}
+</div>
+ </div>
+ </li>
+ {% endfor %}
+ </ol>
+ {% if not object.comments_are_open %}
+ <p>{% trans "Comments are closed." %}</p>
+ {% endif %}
+ {% else %}
+ {% if object.comments_are_open %}
+ <p>{% trans "No comments yet." %}</p>
+ {% else %}
+ <p>{% trans "Comments are closed." %}</p>
+ {% endif %}
+ {% endif %}
+ {% endwith %}
+</div>
+{% endblock %}
+
+
+
+
+{% block gbobject-pingbacks %}
+<div id="pingbacks" class="span-16 last">
+ <h3>{% trans "Pingbacks" %}</h3>
+ {% with object.pingbacks as pingback_list %}
+ {% if pingback_list.count %}
+ <ol id="pingback-list" class="span-15 last">
+ {% for pingback in pingback_list %}
+ <li id="pingback_{{ pingback.pk }}" class="pingback {% cycle box1,box2 %} span-15 last">
+ <div class="pingback-body span-15 last">
+<div class="pingback-author span-15 last">
+<a href="{{ pingback.url }}">{{ pingback.user_name }}</a>
+{% trans "on" %} {{ pingback.submit_date|date:"SHORT_DATETIME_FORMAT" }}
+</div>
+<div class="span-15 last">
+<cite>
+{{ pingback.comment }}
+</cite>
+</div>
+ </div>
+ </li>
+ {% endfor %}
+ </ol>
+ {% endif %}
+ {% if object.pingback_enabled %}
+ <p>{% trans "Pingbacks are open." %}</p>
+ {% else %}
+ <p>{% trans "Pingbacks are closed." %}</p>
+ {% endif %}
+ {% endwith %}
+</div>
+{% endblock %}
+
+
+{% block gbobject-trackbacks %}
+{% with object.trackbacks as trackback_list %}
+{% if trackback_list.count or object.pingback_enabled %}
+<div id="trackbacks" class="span-16 last">
+ <h3>{% trans "Trackbacks" %}</h3>
+ {% if trackback_list.count %}
+ <ol id="trackback-list" class="span-15 last">
+ {% for trackback in trackback_list %}
+ <li id="trackback_{{ trackback.pk }}" class="trackback {% cycle box1,box2 %} span-15 last">
+ <div class="trackback-body span-15 last">
+<div class="trackback-author span-15 last">
+<a href="{{ trackback.url }}">{{ trackback.user_name }}</a>
+{% trans "on" %} {{ trackback.submit_date|date:"SHORT_DATETIME_FORMAT" }}
+</div>
+<div class="span-15 last">
+<cite>
+{{ trackback.comment }}
+</cite>
+</div>
+ </div>
+ </li>
+ {% endfor %}
+ </ol>
+ {% endif %}
+ {% if object.pingback_enabled %}
+ <p>
+ <a href="{% url objectapp_gbobject_trackback object.pk %}" rel="trackback">
+ {% trans "Trackback URL" %}</a>
+ </p>
+ {% endif %}
+</div>
+{% endif %}
+{% endwith %}
+{% endblock %}
+
+
+
+{% block gbobject-comments-form %}
+{% if object.comments_are_open %}
+<div class="commentForm span-16 last">
+ {% render_comment_form for object %}
+</div>
+{% endif %}
+{% endblock %}
+
+{% endblock %}
+
+
+{% block admin_tools %}
+{% if perms.objectapp.change_gbobject %}
+<li>
+ <a href="{% url admin:objectapp_gbobject_change object.pk %}" title="{% trans "Edit the object" %}">
+ {% trans "Edit the object" %}
+ </a>
+</li>
+{% endif %}
+{% endblock %}
+
+
+
+
+
diff --git a/objectapp/templates/objectapp/gbobject_list.html b/objectapp/templates/objectapp/gbobject_list.html
new file mode 100644
index 0000000..73a3adf
--- /dev/null
+++ b/objectapp/templates/objectapp/gbobject_list.html
@@ -0,0 +1,73 @@
+{% extends "objectapp/base.html" %}
+{% load i18n objectapp_tags %}
+
+{% block meta-description %}{% trans "Latest gbobjects for" %} {% if Objecttype %}{% trans "the Objecttype" %} {{ Objecttype }}{% if Objecttype.description %}: {{ Objecttype.description|striptags|safe }}{% endif %}{% endif %}{% if tag %}{% trans "the tag" %} {{ tag }}{% endif %}{% if author %}{% trans "the author" %} {{ author }}{% endif %}{% if page_obj %}{% ifnotequal page_obj.number 1 %} {% trans "page" %} {{ page_obj.number }}{% endifnotequal %}{% endif %}{% endblock %}
+
+{% block link %}
+ {{ block.super }}
+ {% if Objecttype %}
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed" %} {{ Objecttype.title }}" href="{% url objectapp_Objecttype_feed Objecttype.tree_path %}" />
+ {% endif %}
+ {% if tag %}
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed" %} {{ tag.name }}" href="{% url objectapp_tag_feed tag.name %}" />
+ {% endif %}
+ {% if author %}
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed" %} {{ author.username }}" href="{% url objectapp_author_feed author.username %}" />
+ {% endif %}
+{% endblock %}
+
+{% block title %}{% trans "Latest gbobjects" %} {% if Objecttype %}| {% trans "Objecttype" %} {{ Objecttype }}{% endif %}{% if tag %}| {% trans "Tag" %} {{ tag }}{% endif %}{% if author %}| {% trans "Author" %} {{ author }}{% endif %}{% if page_obj %}{% ifnotequal page_obj.number 1 %} | {% trans "Page" %} {{ page_obj.number }}{% endifnotequal %}{% endif %}{% endblock %}
+
+{% block content %}
+{% if Objecttype %}
+<h1>{{ Objecttype.title }}</h1>
+{% if Objecttype.description %}
+<p>{{ Objecttype.description|striptags|safe }}</p>
+{% endif %}
+{% endif %}
+
+{% if tag %}
+<h1>{% trans "Tag" %} : {{ tag }}</h1>
+{% endif %}
+
+{% if author %}
+<h1>{% blocktrans with author.username as author %}Gbobjects by {{ author }}{% endblocktrans %}</h1>
+{% endif %}
+
+{% for object in object_list %}
+ {% with object.html_content|truncatewords_html:100|safe as object_content %}
+ {% include "objectapp/_gbobject_detail.html" %}
+ {% endwith %}
+{% empty %}
+ <p class="notice">{% trans "No gbobjects yet." %}</p>
+{% endfor %}
+
+{% if is_paginated %}
+ {% objectapp_pagination page_obj %}
+{% endif %}
+{% endblock %}
+
+{% block admin_tools %}
+ {% if Objecttype and perms.objectapp.change_Objecttype %}
+ <li>
+ <a href="{% url admin:objectapp_Objecttype_change Objecttype.pk %}" title="{% trans "Edit the Objecttype" %}">
+ {% trans "Edit the Objecttype" %}
+ </a>
+ </li>
+ {% endif %}
+ {% if tag and perms.tagging.change_tag %}
+ <li>
+ <a href="{% url admin:tagging_tag_change tag.pk %}" title="{% trans "Edit the tag" %}">
+ {% trans "Edit the tag" %}
+ </a>
+ </li>
+ {% endif %}
+ {% if author and perms.auth.change_user %}
+ <li>
+ <a href="{% url admin:auth_user_change author.pk %}" title="{% trans "Edit the author" %}">
+ {% trans "Edit the author" %}
+ </a>
+ </li>
+ {% endif %}
+{% endblock %}
+
diff --git a/objectapp/templates/objectapp/gbobject_search.html b/objectapp/templates/objectapp/gbobject_search.html
new file mode 100644
index 0000000..c65e174
--- /dev/null
+++ b/objectapp/templates/objectapp/gbobject_search.html
@@ -0,0 +1,49 @@
+{% extends "objectapp/gbobject_list.html" %}
+{% load i18n objectapp_tags %}
+
+{% block title %}{% trans "Search results for" %} {% if pattern %}'{{ pattern }}'{% endif %}{% if page_obj %}{% ifnotequal page_obj.number 1 %} | {% trans "Page" %} {{ page_obj.number }}{% endifnotequal %}{% endif %}{% endblock %}
+
+{% block meta-description %}{% trans "Search results for" %} {% if pattern %}'{{ pattern }}'{% endif %}{% if page_obj %}{% ifnotequal page_obj.number 1 %} {% trans "page" %} {{ page_obj.number }}{% endifnotequal %}{% endif %}{% endblock %}
+
+{% block link %}
+ {{ block.super }}
+ <link rel="alternate" type="application/rss+xml" title="{% trans "RSS Feed of search result of" %} '{{ pattern }}'" href="{% url objectapp_gbobject_search_feed %}?pattern={{ pattern }}" />
+{% endblock %}
+
+{% block content %}
+<h1>{% trans "Search results for" %} {% if pattern %}'{{ pattern }}'{% endif %}</h1>
+
+{% if error %}
+<p class="error">{{ error }}</p>
+{% endif %}
+
+{% if object_list %}
+<p class="success">
+ {% blocktrans count paginator.count as gbobject_count %}{{ gbobject_count }} gbobject found{% plural %}{{ gbobject_count }} gbobjects found{% endblocktrans %}
+</p>
+{% endif %}
+
+{% for object in object_list %}
+ {% with object.html_content|truncatewords_html:100|safe as object_content %}
+ {% include "objectapp/_gbobject_detail.html" %}
+ {% endwith %}
+{% empty %}
+<p class="notice">{% trans "Nothing found." %}</p>
+{% endfor %}
+
+<div class="search">
+ <form method="get" id="main_searchform" action="{% url objectapp_gbobject_search %}">
+ <p>
+ <input type="text" value="{{ pattern }}" name="pattern" id="main_searchbox" />
+ <input type="submit" id="button" value="OK" />
+ <a title="{% trans 'You can use - to exclude words or phrases, &quot;double quotes&quot; for exact phrases and the AND/OR boolean operators combined with parenthesis for complex searchs.' %}">
+ <img src="{{ STATIC_URL }}objectapp/img/help.png" alt="?" width="14" height="14" />
+ </a>
+ </p>
+ </form>
+</div>
+
+{% if is_paginated %}
+ {% objectapp_pagination page_obj %}
+{% endif %}
+{% endblock %}
diff --git a/objectapp/templates/objectapp/gbobject_trackback.xml b/objectapp/templates/objectapp/gbobject_trackback.xml
new file mode 100644
index 0000000..66dbbb7
--- /dev/null
+++ b/objectapp/templates/objectapp/gbobject_trackback.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<response>
+ {% if not error %}
+ <error>0</error>
+ {% else %}
+ <error>1</error>
+ <message>{{ error }}</message>
+ {% endif %}
+</response>
diff --git a/objectapp/templates/objectapp/login.html b/objectapp/templates/objectapp/login.html
new file mode 100644
index 0000000..496e0ca
--- /dev/null
+++ b/objectapp/templates/objectapp/login.html
@@ -0,0 +1,40 @@
+{% extends "objectapp/base.html" %}
+{% load i18n %}
+
+{% block title %}{% trans "Login required" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Login required" %}</h2>
+
+<div class="gbobject_login">
+ {% if form.errors %}
+ <p class="error">
+ {% trans "Your username and password didn't match. Please try again." %}
+ </p>
+ {% endif %}
+ <p>
+ {% trans "You need to be connected to view this gbobject." %}
+ </p>
+
+ <form method="post" action=".">
+ {% csrf_token %}
+ <table>
+ <tr>
+ <td>{{ form.username.label_tag }}</td>
+ <td>{{ form.username }}</td>
+ </tr>
+ <tr>
+ <td>{{ form.password.label_tag }}</td>
+ <td>{{ form.password }}</td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <input type="hidden" name="next" value="." />
+ <input type="submit" value="{% trans "Login" %}" />
+ </td>
+ </tr>
+ </table>
+ </form>
+</div>
+{% endblock %}
+
diff --git a/objectapp/templates/objectapp/objecttype/gbobject_list.html b/objectapp/templates/objectapp/objecttype/gbobject_list.html
new file mode 100644
index 0000000..d85e241
--- /dev/null
+++ b/objectapp/templates/objectapp/objecttype/gbobject_list.html
@@ -0,0 +1 @@
+{% extends "objectapp/gbobject_list.html" %}
diff --git a/objectapp/templates/objectapp/objecttype_list.html b/objectapp/templates/objectapp/objecttype_list.html
new file mode 100644
index 0000000..c530641
--- /dev/null
+++ b/objectapp/templates/objectapp/objecttype_list.html
@@ -0,0 +1,25 @@
+{% extends "objectapp/base.html" %}
+{% load i18n mptt_tags %}
+
+{% block meta-description %}{% trans "Objecttype list" %}{% endblock %}
+
+{% block title %}{% trans "Objecttypes" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Objecttype list" %}</h2>
+<div>
+ {% for object, structure in object_list|tree_info %}
+ {% if structure.new_level %}<ul><li>{% else %}</li><li>{% endif %}
+ <a href="{{ object.get_absolute_url }}" title="{{ object }}">{{ object }}</a>
+ {% blocktrans count object.gbobjects_published.count as gbobject_count %}{{ gbobject_count }} gbobject{% plural %}{{ gbobject_count }} gbobjects{% endblocktrans %}
+ {% if object.description %}<br />{{ object.description|striptags|safe }}{% endif %}
+ {% for level in structure.closed_levels %}</li></ul>{% endfor %}
+ {% empty %}
+ <ul>
+ <li>
+ {% trans "No objecttypes yet." %}
+ </li>
+ </ul>
+ {% endfor %}
+</div>
+{% endblock %}
diff --git a/objectapp/templates/objectapp/opensearch.xml b/objectapp/templates/objectapp/opensearch.xml
new file mode 100644
index 0000000..c951ffa
--- /dev/null
+++ b/objectapp/templates/objectapp/opensearch.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+{% load objectapp_tags %}
+{% get_tags as gbobject_tags %}
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
+ xmlns:moz="http://www.mozilla.org/2006/browser/search/">
+ <ShortName>{{ site.name }}</ShortName>
+ <LongName>{{ site.name }} - web search</LongName>
+ <Description>Make a search on {{ site.name }}.</Description>
+ <Attribution>{{ copyright }}</Attribution>
+ <Tags>{{ gbobject_tags|slice:":15"|join:" " }}</Tags>
+ <Url type="text/html" template="{{ protocol }}://{{ site.domain }}{% url objectapp_gbobject_search %}?pattern={searchTerms}" />
+ <Url type="application/{{ feeds_format }}+xml" template="{{ protocol }}://{{ site.domain }}{% url objectapp_gbobject_search_feed %}?pattern={searchTerms}" />
+ <Url type="application/opensearchdescription+xml" rel="self" template="{{ protocol }}://{{ site.domain }}{% url objectapp_opensearch %}"/>
+ <Image height="16" width="16" type="image/vnd.microsoft.icon">{{ STATIC_URL }}objectapp/img/favicon.ico</Image>
+ <Query role="example" searchTerms="{{ gbobject_tags.0 }}" />
+ <Developer>Fantomas42</Developer>
+ <Contact>fantomas42@gmail.com</Contact>
+ <SyndicationRight>open</SyndicationRight>
+ <AdultContent>false</AdultContent>
+ <Language>{{ LANGUAGE_CODE }}</Language>
+ <OutputEncoding>UTF-8</OutputEncoding>
+ <InputEncoding>UTF-8</InputEncoding>
+ <moz:SearchForm>{{ protocol }}://{{ site.domain }}/</moz:SearchForm>
+</OpenSearchDescription>
diff --git a/objectapp/templates/objectapp/password.html b/objectapp/templates/objectapp/password.html
new file mode 100644
index 0000000..6eefc5c
--- /dev/null
+++ b/objectapp/templates/objectapp/password.html
@@ -0,0 +1,35 @@
+{% extends "objectapp/base.html" %}
+{% load i18n %}
+
+{% block title %}{% trans "Password required" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Password required" %}</h2>
+
+<div class="gbobject_password">
+ {% if error %}
+ <p class="error">
+ {% trans "The password provided is not valid. Please try again." %}
+ </p>
+ {% endif %}
+ <p>
+ {% trans "You need to provide a password to view this gbobject." %}
+ </p>
+
+ <form method="post" action=".">
+ {% csrf_token %}
+ <table>
+ <tr>
+ <td><label for="id_password">{% trans "Password" %}</label></td>
+ <td><input name="password" id="id_password" type="password" /></td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <input type="submit" value="{% trans "Valid" %}" />
+ </td>
+ </tr>
+ </table>
+ </form>
+</div>
+{% endblock %}
+
diff --git a/objectapp/templates/objectapp/rsd.xml b/objectapp/templates/objectapp/rsd.xml
new file mode 100644
index 0000000..1277462
--- /dev/null
+++ b/objectapp/templates/objectapp/rsd.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
+ <service>
+ <engineName>Django Blog Objectapp {{ OBJECTAPP_VERSION }}</engineName>
+ <engineLink>http://github.com/Fantomas42/django-blog-objectapp</engineLink>
+ <homePageLink>{{ protocol }}://{{ site.domain }}/</homePageLink>
+ <apis>
+ <api name="MetaWeblog" preferred="true" apiLink="{{ protocol }}://{{ site.domain }}/xmlrpc/" blogID="{{ site.id }}">
+ <settings>
+ <docs>http://www.xmlrpc.com/metaWeblogApi</docs>
+ <notes>MetaWeblog API support + Blogger API partial support</notes>
+ </settings>
+ </api>
+ </apis>
+ </service>
+</rsd>
diff --git a/objectapp/templates/objectapp/sitemap.html b/objectapp/templates/objectapp/sitemap.html
new file mode 100644
index 0000000..32c9e03
--- /dev/null
+++ b/objectapp/templates/objectapp/sitemap.html
@@ -0,0 +1,70 @@
+{% extends "objectapp/base.html" %}
+{% load i18n objectapp_tags mptt_tags %}
+
+{% block title %}{% trans "Sitemap" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Sitemap" %}</h2>
+
+<div class="gbobjectsbyobjecttypes">
+ <h3>{% trans "Gbobjects per objecttypes" %}</h3>
+ {% for Objecttype in objecttypes %}
+ <h4>{{ Objecttype }}</h4>
+ <ul>
+ {% for gbobject in Objecttype.gbobjects_published.all %}
+ <li>
+ <a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ {% with gbobject.comments.count as comment_count %}
+ - {{ comment_count }} {% trans "comment" %}{{ comment_count|pluralize }}
+ {% endwith %}
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No gbobjects yet." %}
+ </li>
+ {% endfor %}
+ </ul>
+ {% endfor %}
+</div>
+
+<div class="allgbobjects">
+ <h3>{% trans "All the gbobjects" %}</h3>
+ <ul>
+ {% for gbobject in gbobjects %}
+ <li>
+ <a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ - {{ gbobject.creation_date|date:"SHORT_DATE_FORMAT" }}
+ {% with gbobject.comments.count as comment_count %}
+ - {{ comment_count }} {% trans "comment" %}{{ comment_count|pluralize }}
+ {% endwith %}
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No gbobjects yet." %}
+ </li>
+ {% endfor %}
+ </ul>
+</div>
+
+<div class="objecttypes">
+ <h3>{% trans "Objecttypes" %}</h3>
+ {% for Objecttype, structure in objecttypes|tree_info %}
+ {% if structure.new_level %}<ul><li>{% else %}</li><li>{% endif %}
+ <a href="{{ Objecttype.get_absolute_url }}" title="{{ Objecttype.title }}">{{ Objecttype.title }}</a>
+ -
+ {% blocktrans count Objecttype.gbobjects_published.count as gbobject_count %}{{ gbobject_count }} gbobject{% plural %}{{ gbobject_count }} gbobjects{% endblocktrans %}
+ {% for level in structure.closed_levels %}</li></ul>{% endfor %}
+ {% empty %}
+ <ul>
+ <li>
+ {% trans "No objecttypes yet." %}
+ </li>
+ </ul>
+ {% endfor %}
+</div>
+
+<div class="montharchives">
+ <h3>{% trans "Monthly archives" %}</h3>
+ {% get_archives_gbobjects %}
+</div>
+{% endblock %}
diff --git a/objectapp/templates/objectapp/skeleton.html b/objectapp/templates/objectapp/skeleton.html
new file mode 100644
index 0000000..5f89159
--- /dev/null
+++ b/objectapp/templates/objectapp/skeleton.html
@@ -0,0 +1,488 @@
+{% load gstudio_tags i18n %}
+{% load adminmedia grp_tags %}
+{% get_tags as objecttype_tags %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xml:lang="{{ LANGUAGE_CODE }}" lang="{{ LANGUAGE_CODE }}" version="-//W3C//DTD XHTML 1.1//EN" xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+
+<style type="text/css">
+ .link {
+ stroke : #ccc;
+ }
+
+ .nodetext {
+ pointer-events : none;
+ font : 10px sans-serif;
+ }
+
+ .node {
+ border-width : 1px;
+ border-color : gray;
+ }
+ body {
+ background-color:white;
+ }
+
+ #chart {
+ height: 90%;
+ margin: 0 auto;
+ width: 90%;
+ }
+
+ svg {
+ margin-left: 10%;
+ margin-right: 10%;
+ display: block;
+ position:absolute;
+ }
+
+ </style>
+ <!-- the header lines from grappelli -->
+ {% block blockbots %}
+ <meta name="robots" content="NONE,NOARCHIVE" />
+ {% endblock %}
+ <!-- STYLESHEETS -->
+ {% block stylesheets %}
+ <link href="{% admin_media_prefix %}css/base.css" rel="stylesheet" type="text/css" />
+ {% if LANGUAGE_BIDI %}<link href="{% block stylesheet_rtl %}{% admin_media_prefix %}css/rtl.css{% endblock %}" rel="stylesheet" type="text/css" />{% endif %}
+ <link href="{% admin_media_prefix %}jquery/ui/css/custom-theme/jquery-ui-1.8.custom.css" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8" />
+ <link href="{% admin_media_prefix %}css/jquery-ui-grappelli-extensions.css" rel="stylesheet" type="text/css" />
+ {% endblock %}
+ <!-- EXTRASTYLES -->
+ {% block extrastyle %}{% endblock %}
+ <!-- JAVASCRIPTS -->
+ {% block javascripts %}
+ <script type="text/javascript">
+ // GLOBALS
+ var grappelli = {},
+ // TODO: klemens: drop ADMIN_URL
+ ADMIN_URL = "{% url admin:index %}",
+ MODEL_URL_ARRAY = {% get_content_types %},
+ DATE_FORMAT = "{% get_date_format %}",
+ TIME_FORMAT = "{% get_time_format %}",
+ DATETIME_FORMAT = "{% get_datetime_format %}";
+ </script>
+ <!-- jQuery, jQuery-UI -->
+ <script src="{% admin_media_prefix %}jquery/jquery-1.6.2.min.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}jquery/ui/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
+ <!-- Grappelli Main JS -->
+ <script src="{% admin_media_prefix %}js/grappelli/grappelli.js" type="text/javascript"></script>
+ <!-- Grappelli jQuery Plugins, Widgets -->
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_collapsible.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_collapsible_group.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_timepicker.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_related_fk.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_related_m2m.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_related_generic.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_autocomplete_fk.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_autocomplete_m2m.js" type="text/javascript"></script>
+ <script src="{% admin_media_prefix %}js/grappelli/jquery.grp_autocomplete_generic.js" type="text/javascript"></script>
+
+ <!-- EXTRAHEAD -->
+ {% block extrahead %}{% endblock %}
+
+ {% endblock %}
+
+
+ <!-- the header lines from gstudio. after checking the css classes
+ called in the tempaltes, we need to prune the following -->
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta http-equiv="cache-control" content="public" />
+ <meta name="robots" content="follow, all" />
+ <meta name="language" content="{{ LANGUAGE_CODE }}" />
+ <meta name="description" content="{% block meta-description %}Demonstration of the Gstudio Blog application.{% endblock %}" />
+ <meta name="keywords" content="{% block meta-keywords %}django, blog, gstudio, {{ objecttype_tags|join:", "}}{% endblock %}" />
+ <meta name="author" content="gnowgi" />
+ {% block meta %}{% endblock %}
+ <link rel="pingback" href="/xmlrpc/" />
+ <link rel="shortcut icon" href="{{ STATIC_URL }}gstudio/img/favicon.ico" />
+ <link rel="home" href="{% url gstudio_nodetype_archive_index %}" />
+ <link rel="stylesheet" type="text/css" media="screen, projection" href="{{ STATIC_URL }}objectapp/css/screen.css" />
+ <link rel="stylesheet" type="text/css" media="print" href="{{ STATIC_URL }}gstudio/css/print.css" />
+ <!--[if lt IE 8]>
+ <link rel="stylesheet" type="text/css" media="screen, projection" href="{{ STATIC_URL }}gstudio/css/ie.css" />
+ <![endif]-->
+ {% block link %}{% endblock %}
+ {% block script %}{% endblock %}
+ <title>Gnowledge Studio - {% block title %}{% endblock %}</title>
+ </head>
+ <body id="gstudio">
+<div class="body" >
+ <div class="content">
+
+ <!--
+ <div id="relation_type" class="ui-buttonset">
+ <span>Relation type:</span>
+ <input checked="checked" value="follows" type="radio" id="follows" name="radio" class="ui-helper-hidden-accessible"><label for="follows" class="ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left" aria-pressed="false" role="button" aria-disabled="false"><span class="ui-button-text">Followers</span></label>
+ <input value="mentions" type="radio" id="mentions" name="radio" class="ui-helper-hidden-accessible"><label for="mentions" aria-pressed="true" class="ui-button ui-widget ui-state-default ui-button-text-only ui-corner-right ui-state-active" role="button" aria-disabled="false"><span class="ui-button-text">Mentions</span></label>
+ <input value="hashtags" type="radio" id="hashtags" name="radio"/><label for="hashtags">Shared interests</label>
+ </div>
+ -->
+ <div id="chart"><svg width="960" height="700" style="opacity: 1; ">
+ </div>
+
+
+ <script type="text/javascript" src="/static/gstudio/js/jquery.min.js" ></script>
+ <script type="text/javascript" src="/static/gstudio/js/jquery-ui.js" ></script>
+
+ <script type="text/javascript" src="/static/gstudio/js/underscore.js" ></script>
+
+ <script type="text/javascript" src="/static/gstudio/js/d3.js"></script>
+ <script type="text/javascript" src="/static/gstudio/js/d3.layout.js"></script>
+ <script type="text/javascript" src="/static/gstudio/js/d3.geom.js"></script>
+ <!-- <script type="text/javascript" src="/static/gstudio/js/force.js"></script>
+
+Javascript for Object type force graph
+-->
+ <script type="text/javascript" >
+
+ var w = 700,
+ h = 300,
+ fill = d3.scale.category20();
+
+ var vis = d3.select("#chart")
+ .append("svg:svg")
+ .attr("width", w)
+ .attr("height", h);
+
+ vis.append("svg:g").attr("class", "edges");
+ vis.append("svg:g").attr("class", "nodes");
+
+$(function() {
+ $.ajax({
+ url: '/gstudio/graphs/graph_json/{{ object.id }}',
+ //crossDomain: true,
+ dataType: 'json',
+ success : function(json) {
+
+
+ var force;
+
+
+
+ var nodes_by_id = _.reduce(json.node_metadata, function(acc, n) {
+ acc[n._id] = n;
+ return acc;
+ }, {});
+
+ var member_of = _(json.member_of).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'member_of';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ var contains_subtypes = _(json.contains_subtypes).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'contains_subtypes';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+ var contains_members = _(json.contains_members).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'contains_members';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ var left_subjecttype_of = _(json.left_subjecttype_of).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'left_subjecttype_of';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ var subjecttype_of = _(json.subjecttype_of).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'subjecttype_of';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+ var plural = _(json.plural).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'plural';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ var altnames = _(json.altnames).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'altnames';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+ var content = _(json.content).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'content';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+ var contains_members = _(json.contains_members).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'contains_members';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+ var authors = _(json.authors).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'authors';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ var type_of = _(json.type_of).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'type_of';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ var right_subjecttype_of = _(json.right_subjecttype_of).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'right_subjecttype_of';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ var follows_edges = _(json.is_followed_by).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'follows_edges';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ var mentions_edges = _(json.is_mentioned_by).chain().map(function(e) {
+ e.source = nodes_by_id[e.from];
+ e.target = nodes_by_id[e.to];
+ e.type = 'mentions_edges';
+ return e;
+ }).filter(function(e){
+ return nodes_by_id[e.from] && nodes_by_id[e.to]
+ }).value();
+
+
+ nodes_by_id[{{ object.id }}].x = w/2.0;
+ nodes_by_id[{{ object.id }}].y = h/2.0;
+
+ all_edges = new Array();
+ all_edges = all_edges.concat(follows_edges, member_of, contains_members, contains_subtypes, mentions_edges, right_subjecttype_of, left_subjecttype_of, subjecttype_of, type_of,plural,altnames,contains_members,authors,content);
+ /* all_edges.concat(follows_edges);
+ all_edges.concat(member_of_metatype);
+ all_edges.concat(contains_members);
+ all_edges.concat(contains_subtypes);
+ all_edges.concat(mentions_edges);
+
+
+ document.getElementById('debugbox1').value= follows_edges; //member_of_metatype
+ document.getElementById('debugbox2').value= mentions_edges;
+ //document.getElementById('debugbox2').value= member_of_metatype;
+ document.getElementById('debugbox3').value= all_edges;
+ */
+ var force = d3.layout.force()
+ .linkStrength(0.5)
+ .charge(-2000)
+ .friction(0.7)
+ .gravity(0.7)
+ .linkDistance(50)
+ .nodes([])
+ .links([])
+ .size([w, h])
+ .start();
+
+ function update(edges){
+ // for each func
+ _.each(nodes_by_id, function(n){n.added = false});
+ // reduce the nodes list to have only those nodes for a given rel.
+ nodes = _.reduce(edges, function(acc, e) {
+ if(nodes_by_id[e.from] && !nodes_by_id[e.from].added){
+ nodes_by_id[e.from].added = true;
+ acc.push(nodes_by_id[e.from]);
+ }
+ if(nodes_by_id[e.to] && !nodes_by_id[e.to].added){
+ nodes_by_id[e.to].added = true;
+ acc.push(nodes_by_id[e.to]);
+ }
+ return acc;
+ }, []);
+
+ force.nodes(nodes);
+ force.links(edges);
+ force.start();
+
+ link = d3.select("#chart g.edges").selectAll("line.link")
+ .data(edges, function(e){return e.from + "-" + e.to + "-" + e.type});
+
+ link.enter().append("svg:line")
+ .attr("class", "link")
+ .style("stroke-width", 2 /* function(d) {
+ return Math.sqrt(d.value);
+ } */ )
+ .attr("x1", function(d) {
+ return d.source.x;
+ })
+
+ .attr("y1", function(d) {
+ return d.source.y;
+ })
+ .attr("x2", function(d) {
+ return d.target.x;
+ })
+ .attr("y2", function(d) {
+ return d.target.y;
+ })
+ .append("a")
+ .text(function(d) { return d.type; });
+
+
+ var node = d3.select("#chart g.nodes").selectAll("g.node").data(nodes);
+ //.filter(function(d) { return !(d._id=="a4" && d._id=="a5")});
+
+ var new_g = node.enter().append("svg:g")
+ .attr("class", "node")
+ .call(force.drag);
+
+
+
+
+
+
+
+
+ new_g.append("svg:circle")
+ .attr("cx", function(d) { return d.x - w/2.0 + 15; })
+ .attr("cy", function(d) { return d.y - h/2.0 + 15; })
+ .attr("r", 7)
+ .style("fill", function color(d) { if (d._id=={{ object.id }}) return "red"; else if (d._id== "a1" || d._id== "a2" || d._id== "a3" ) return "steelblue" ; else return "yellow"});
+
+
+
+ new_g.append("svg:text")
+ .attr("dy", 20)
+ .attr("dx", 25)
+
+ /* .attr("text-anchor", "middle") */
+ .text(function(d) {
+ return d.screen_name;
+ });
+
+
+ node.exit().remove();
+
+
+
+ force.on("tick", function() {
+
+ var x_center = $("#chart").width() / 2;
+ var y_center = $("#chart").height() / 2;
+
+ link.attr("x1", function(d) { return d.source.x; })
+ .attr("y1", function(d) { return d.source.y; })
+ .attr("x2", function(d) { return d.target.x; })
+ .attr("y2", function(d) { return d.target.y; });
+
+ node.attr("transform", function(d) { return "translate(" + (d.x-16) + "," + (d.y-16) + ")"; });
+
+ });
+ }
+
+ update(all_edges);
+ vis.style("opacity", 1e-6)
+ .transition()
+ .duration(1000)
+ .style("opacity", 1);
+
+ /* $('input#follows').change(function(){
+ update(all_edges);
+ });
+ */
+
+ /* $('input#mentions').change(function(){
+ update(all_edges);
+ }); */
+ }
+ });
+
+// $("#relation_type").buttonset();
+
+// $('input#mentions').change(function(){console.log(this)});
+// $('input#follows').change(function(){console.log(this)});
+});
+
+
+</script>
+
+ </div>
+
+</div>
+
+ <div>
+
+ {% include "gstudio/_header.html" %}
+
+
+ <div id="breadcrumbs" class="span-24 last">
+ / {% block breadcrumbs %}{% endblock %}
+ </div>
+ </div>
+ <div id="body" class="span-24 last">
+ <div id="content" class="hfeed span-16 border">
+ {% block content %}
+ <h3>The content block need to be overrided!</h3>
+ {% endblock %}
+ </div>
+ <div id="graph" class="span-16 last">
+ {% block graph %} {% endblock %}
+ </div>
+ <div id="sidebar" class="span-8 last">
+ {% block sidebar %}
+ {% endblock %}
+ </div>
+ </div>
+ <div id="footer" class="span-24 last">
+ <p>Powered by <a href="http://www.djangoproject.com">Django</a> and <a href="http://github.com/gnowgi/django-gstudio">Gstudio {{ GSTUDIO_VERSION }}</a>.</p>
+ </div>
+ </div>
+ </body>
+</html>
+
+
diff --git a/objectapp/templates/objectapp/tag/gbobject_list.html b/objectapp/templates/objectapp/tag/gbobject_list.html
new file mode 100644
index 0000000..d85e241
--- /dev/null
+++ b/objectapp/templates/objectapp/tag/gbobject_list.html
@@ -0,0 +1 @@
+{% extends "objectapp/gbobject_list.html" %}
diff --git a/objectapp/templates/objectapp/tag_list.html b/objectapp/templates/objectapp/tag_list.html
new file mode 100644
index 0000000..21905a4
--- /dev/null
+++ b/objectapp/templates/objectapp/tag_list.html
@@ -0,0 +1,24 @@
+{% extends "objectapp/base.html" %}
+{% load i18n %}
+
+{% block meta-description %}{% trans "Tag list" %}{% endblock %}
+
+{% block title %}{% trans "Tags" %}{% endblock %}
+
+{% block content %}
+<h2>{% trans "Tag list" %}</h2>
+<div>
+ <ul>
+ {% for object in object_list %}
+ <li>
+ <a href="{% url objectapp_tag_detail object %}" title="{{ object }}">{{ object }}</a>
+ {% blocktrans count object.count as gbobject_count %}{{ gbobject_count }} gbobject{% plural %}{{ gbobject_count }} gbobjects{% endblocktrans %}
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No tags yet." %}
+ </li>
+ {% endfor %}
+ </ul>
+</div>
+{% endblock %}
diff --git a/objectapp/templates/objectapp/tags/archives_gbobjects.html b/objectapp/templates/objectapp/tags/archives_gbobjects.html
new file mode 100644
index 0000000..f799579
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/archives_gbobjects.html
@@ -0,0 +1,15 @@
+{% load i18n %}
+<ul>
+ {% for date in archives %}
+ <li>
+ <a title="{% trans "Archives" %} {{ date|date:"YEAR_MONTH_FORMAT" }}" rel="archives"
+ href="{% url objectapp_gbobject_archive_month date|date:"Y" date|date:"m" %}">
+ {{ date|date:"YEAR_MONTH_FORMAT" }}
+ </a>
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No archives yet." %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/objectapp/templates/objectapp/tags/archives_gbobjects_link.html b/objectapp/templates/objectapp/tags/archives_gbobjects_link.html
new file mode 100644
index 0000000..66a186c
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/archives_gbobjects_link.html
@@ -0,0 +1,3 @@
+{% load i18n %}
+{% for date in archives %}<link rel="archives" title="{% trans "Archives" %} {{ date|date:"YEAR_MONTH_FORMAT" }}" href="{% url objectapp_gbobject_archive_month date|date:"Y" date|date:"m" %}" />
+{% endfor %}
diff --git a/objectapp/templates/objectapp/tags/archives_gbobjects_tree.html b/objectapp/templates/objectapp/tags/archives_gbobjects_tree.html
new file mode 100644
index 0000000..c3405fb
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/archives_gbobjects_tree.html
@@ -0,0 +1,43 @@
+{% load i18n %}
+
+{% regroup archives by year as year_list %}
+<ul>
+ {% for year in year_list %}
+ <li>
+ <a title="{% trans "Archives" %} {{ year.grouper }}" rel="archives"
+ href="{% url objectapp_gbobject_archive_year year.grouper %}">{{ year.grouper }}</a>
+ {% regroup year.list by month as month_list %}
+ <ul>
+ {% for month in month_list %}
+ <li>
+ {% with month.list.0 as month_date %}
+ <a title="{% trans "Archives" %} {{ month_date|date:"YEAR_MONTH_FORMAT" }}" rel="archives"
+ href="{% url objectapp_gbobject_archive_month month_date|date:"Y" month_date|date:"m" %}">
+ {{ month_date|date:"F" }}
+ </a>
+ {% endwith %}
+ {% regroup month.list by day as day_list %}
+ <ul>
+ {% for day in day_list %}
+ <li>
+ {% with day.list.0 as day_date %}
+ <a title="{% trans "Archives" %} {{ day_date|date:"DATE_FORMAT" }}" rel="archives"
+ href="{% url objectapp_gbobject_archive_day day_date|date:"Y" day_date|date:"m" day_date|date:"d" %}">
+ {{ day_date|date:"MONTH_DAY_FORMAT" }}
+ </a>
+ {% endwith %}
+ </li>
+ {% endfor %}
+ </ul>
+ </li>
+ {% endfor %}
+ </ul>
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No archives yet." %}
+ </li>
+ {% endfor %}
+</ul>
+
+
diff --git a/objectapp/templates/objectapp/tags/authors.html b/objectapp/templates/objectapp/tags/authors.html
new file mode 100644
index 0000000..c518ece
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/authors.html
@@ -0,0 +1,15 @@
+{% load i18n %}
+
+<ul>
+ {% for author in authors %}
+ <li class="vcard">
+ <a href="{% url objectapp_author_detail author.username %}"
+ title="{{ author.username }}" class="fn nickname url">{{ author.username }}</a>
+ {% blocktrans count author.gbobjects_published.count as gbobject_count %}{{ gbobject_count }} gbobject{% plural %}{{ gbobject_count }} gbobjects{% endblocktrans %}
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No authors yet." %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/objectapp/templates/objectapp/tags/breadcrumbs.html b/objectapp/templates/objectapp/tags/breadcrumbs.html
new file mode 100644
index 0000000..c7a5361
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/breadcrumbs.html
@@ -0,0 +1,7 @@
+{% for crumb in breadcrumbs %}
+{% if not forloop.last %}
+<a href="{{ crumb.url }}" title="{{ crumb.name }}">{{ crumb.name }}</a> {{ separator }}
+{% else %}
+ {{ crumb.name }}
+{% endif %}
+{% endfor %}
diff --git a/objectapp/templates/objectapp/tags/calendar.html b/objectapp/templates/objectapp/tags/calendar.html
new file mode 100644
index 0000000..17e3218
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/calendar.html
@@ -0,0 +1,17 @@
+{{ calendar|safe }}
+<div class="month-navigation">
+ <div class="month-previous">
+ {% if previous_month %}
+ <a href="{% url objectapp_gbobject_archive_month previous_month|date:"Y" previous_month|date:"m" %}">
+ &lt;&lt; {{ previous_month|date:"YEAR_MONTH_FORMAT" }}
+ </a>
+ {% endif %}
+ </div>
+ <div class="month-next">
+ {% if next_month %}
+ <a href="{% url objectapp_gbobject_archive_month next_month|date:"Y" next_month|date:"m" %}">
+ {{ next_month|date:"YEAR_MONTH_FORMAT" }} &gt;&gt;
+ </a>
+ {% endif %}
+ </div>
+</div>
diff --git a/objectapp/templates/objectapp/tags/dummy.html b/objectapp/templates/objectapp/tags/dummy.html
new file mode 100644
index 0000000..f04fcf5
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/dummy.html
@@ -0,0 +1 @@
+{% extends template %}
diff --git a/objectapp/templates/objectapp/tags/featured_gbobjects.html b/objectapp/templates/objectapp/tags/featured_gbobjects.html
new file mode 100644
index 0000000..9e9c924
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/featured_gbobjects.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+<ul>
+ {% for gbobject in gbobjects %}
+ <li>
+ <a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No gbobjects yet." %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/objectapp/templates/objectapp/tags/gbobjects.html b/objectapp/templates/objectapp/tags/gbobjects.html
new file mode 100644
index 0000000..16ba809
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/gbobjects.html
@@ -0,0 +1,16 @@
+{% load i18n mptt_tags %}
+
+{% for objecttype, structure in objecttypes|tree_info %}
+ {% if structure.new_level %}<ul><li>{% else %}</li><li>{% endif %}
+ <a href="{{ objecttype.get_absolute_url }}" title="{{ objecttype.title }}">{{ objecttype.title }}</a>
+ {% blocktrans count objecttypes.gbobjects_published.count as gbobjects_count %}{{ gbobjects_count }} gbobject{% plural %}{{ gbobjects_count }} gbobjects{% endblocktrans %}
+
+ {% empty %}
+<ul>
+ <li>
+ {% trans "No subtypes yet." %}
+ </li>
+</ul>
+{% endfor %}
+
+
diff --git a/objectapp/templates/objectapp/tags/objecttypes.html b/objectapp/templates/objectapp/tags/objecttypes.html
new file mode 100644
index 0000000..9c54641
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/objecttypes.html
@@ -0,0 +1,16 @@
+{% load i18n mptt_tags %}
+
+{% for Objecttype, structure in objecttypes|tree_info %}
+ {% if structure.new_level %}<ul><li>{% else %}</li><li>{% endif %}
+ <a href="{{ Objecttype.get_absolute_url }}" title="{{ Objecttype.title }}">{{ Objecttype.title }}</a>
+ {% blocktrans count Objecttype.gbobjects_published.count as gbobject_count %}{{ gbobject_count }} gbobject{% plural %}{{ gbobject_count }} gbobjects{% endblocktrans %}
+ {% for level in structure.closed_levels %}</li></ul>{% endfor %}
+ {% empty %}
+<ul>
+ <li>
+ {% trans "No objecttypes yet." %}
+ </li>
+</ul>
+{% endfor %}
+
+
diff --git a/objectapp/templates/objectapp/tags/pagination.html b/objectapp/templates/objectapp/tags/pagination.html
new file mode 100644
index 0000000..273811f
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/pagination.html
@@ -0,0 +1,59 @@
+{% load i18n %}
+<div class="paginator span-16 last">
+ <span class="index">
+ {% blocktrans with page.number as current_page and page.paginator.num_pages as total_page %}Page {{ current_page }} of {{ total_page }}{% endblocktrans %}
+ </span>
+
+ {% if page.has_previous %}
+ <span class="previous">
+ <a href="?page={{ page.previous_page_number }}{{ GET_string }}"
+ title="{% trans "More recent gbobjects" %}">&laquo;</a>
+ </span>
+ {% endif %}
+
+ {% for page_number in begin %}
+ <span class="page {% ifequal page.number page_number %}current{% endifequal %}">
+ {% ifequal page.number page_number %}
+ <strong>{{ page_number }}</strong>
+ {% else %}
+ <a href="?page={{ page_number }}{{ GET_string }}"
+ title="{% trans "Gbobjects page" %} {{ page_number }}">{{ page_number }}</a>
+ {% endifequal%}
+ </span>
+ {% endfor %}
+
+ {% if middle %}
+ <span class="ellipsis">&hellip;</span>
+ {% for page_number in middle %}
+ <span class="page {% ifequal page.number page_number %}current{% endifequal %}">
+ {% ifequal page.number page_number %}
+ <strong>{{ page_number }}</strong>
+ {% else %}
+ <a href="?page={{ page_number }}{{ GET_string }}"
+ title="{% trans "Gbobjects page" %} {{ page_number }}">{{ page_number }}</a>
+ {% endifequal%}
+ </span>
+ {% endfor %}
+ {% endif %}
+
+ {% if end %}
+ <span class="ellipsis">&hellip;</span>
+ {% for page_number in end %}
+ <span class="page {% ifequal page.number page_number %}current{% endifequal %}">
+ {% ifequal page.number page_number %}
+ <strong>{{ page_number }}</strong>
+ {% else %}
+ <a href="?page={{ page_number }}{{ GET_string }}"
+ title="{% trans "Gbobjects page" %} {{ page_number }}">{{ page_number }}</a>
+ {% endifequal%}
+ </span>
+ {% endfor %}
+ {% endif %}
+
+ {% if page.has_next %}
+ <span class="next">
+ <a href="?page={{ page.next_page_number }}{{ GET_string }}"
+ title="{% trans "More old gbobjects" %}">&raquo;</a>
+ </span>
+ {% endif %}
+</div>
diff --git a/objectapp/templates/objectapp/tags/popular_gbobjects.html b/objectapp/templates/objectapp/tags/popular_gbobjects.html
new file mode 100644
index 0000000..f3339af
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/popular_gbobjects.html
@@ -0,0 +1,16 @@
+{% load i18n %}
+<ul>
+ {% for gbobject in gbobjects %}
+ <li>
+ <a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ <br />
+ {% with gbobject.comments.count as comment_count %}
+ <a href="{{ gbobject.get_absolute_url }}#comments" title="{{ gbobject.title }}">{{ comment_count }} {% trans "comment" %}{{ comment_count|pluralize }}</a>
+ {% endwith %}
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No gbobjects yet." %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/objectapp/templates/objectapp/tags/random_gbobjects.html b/objectapp/templates/objectapp/tags/random_gbobjects.html
new file mode 100644
index 0000000..9e9c924
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/random_gbobjects.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+<ul>
+ {% for gbobject in gbobjects %}
+ <li>
+ <a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No gbobjects yet." %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/objectapp/templates/objectapp/tags/recent_comments.html b/objectapp/templates/objectapp/tags/recent_comments.html
new file mode 100644
index 0000000..58907d8
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/recent_comments.html
@@ -0,0 +1,20 @@
+{% load i18n %}
+
+<ul>
+ {% for comment in comments %}
+ <li>
+ {% with comment.content_object as gbobject %}
+ {{ comment.user_name }} {% trans "in" %}
+ <a href="{{ gbobject.get_absolute_url }}#comment_{{ comment.pk }}"
+ title="{% trans "Comment on" %} {{ gbobject.title }}">
+ {{ gbobject.title }}
+ </a>
+ {% endwith %}
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No comments yet." %}
+ </li>
+ {% endfor %}
+</ul>
+
diff --git a/objectapp/templates/objectapp/tags/recent_gbobjects.html b/objectapp/templates/objectapp/tags/recent_gbobjects.html
new file mode 100644
index 0000000..9e9c924
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/recent_gbobjects.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+<ul>
+ {% for gbobject in gbobjects %}
+ <li>
+ <a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No gbobjects yet." %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/objectapp/templates/objectapp/tags/recent_linkbacks.html b/objectapp/templates/objectapp/tags/recent_linkbacks.html
new file mode 100644
index 0000000..f731556
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/recent_linkbacks.html
@@ -0,0 +1,21 @@
+{% load i18n %}
+
+<ul>
+ {% for linkback in linkbacks %}
+ <li>
+ {% with linkback.content_object as gbobject %}
+ <a href="{{ linkback.url }}">{{ linkback.user_name }}</a>
+ {% trans "in" %}
+ <a href="{{ gbobject.get_absolute_url }}#{{ linkback.flags.all.0.flag }}_{{ linkback.pk }}"
+ title="{% trans "Linkback on" %} {{ gbobject.title }}">
+ {{ gbobject.title }}
+ </a>
+ {% endwith %}
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No linkbacks yet." %}
+ </li>
+ {% endfor %}
+</ul>
+
diff --git a/objectapp/templates/objectapp/tags/similar_gbobjects.html b/objectapp/templates/objectapp/tags/similar_gbobjects.html
new file mode 100644
index 0000000..fcd8650
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/similar_gbobjects.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+<ul>
+ {% for gbobject in gbobjects %}
+ <li>
+ <a href="{{ gbobject.get_absolute_url }}" title="{{ gbobject.title }}" rel="bookmark">{{ gbobject.title }}</a>
+ </li>
+ {% empty %}
+ <li>
+ {% trans "No similar objects." %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/objectapp/templates/objectapp/tags/slider_gbobjects.html b/objectapp/templates/objectapp/tags/slider_gbobjects.html
new file mode 100644
index 0000000..205a720
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/slider_gbobjects.html
@@ -0,0 +1,27 @@
+{% if gbobjects %}
+<div id="slider">
+ <ul class="ui-tabs-nav">
+ {% for gbobject in gbobjects %}
+ <li class="ui-tabs-nav-item{% if forloop.first %} ui-tabs-selected{% endif %}"
+ id="nav-slider-{{ forloop.counter }}">
+ <a href="#gbobject-slider-{{ forloop.counter }}">
+ <img src="{% if gbobject.image %}{{ gbobject.image.url }}{% endif %}" alt="{{ gbobject.title }}" />
+ <span>{{ gbobject.title }}</span>
+ </a>
+ </li>
+ {% endfor %}
+ </ul>
+
+ {% for gbobject in gbobjects %}
+ <div id="gbobject-slider-{{ forloop.counter }}" class="ui-tabs-panel">
+ <img src="{% if gbobject.image %}{{ gbobject.image.url }}{% endif %}" alt="{{ gbobject.title }}" />
+ <div class="panel_info">
+ <h2>
+ <a href="{{ gbobject.get_absolute_url }}" rel="bookmark">{{ gbobject.title }}</a>
+ </h2>
+ {{ gbobject.excerpt|linebreaks|truncatewords_html:20 }}
+ </div>
+ </div>
+ {% endfor %}
+</div>
+{% endif %}
diff --git a/objectapp/templates/objectapp/tags/tag_cloud.html b/objectapp/templates/objectapp/tags/tag_cloud.html
new file mode 100644
index 0000000..fac0061
--- /dev/null
+++ b/objectapp/templates/objectapp/tags/tag_cloud.html
@@ -0,0 +1,13 @@
+{% load i18n %}
+
+<div>
+ <ul>
+ {% for tag in tags %}
+ <li>
+ <a href="{% url objectapp_tag_detail tag.name %}"
+ title="{% blocktrans count tag.count as gbobject_count %}{{ gbobject_count }} gbobject{% plural %}{{ gbobject_count }} gbobjects{% endblocktrans %}"
+ class="tag_{{ tag.font_size }}">{{ tag }}</a>
+ </li>
+ {% endfor %}
+ </ul>
+</div>
diff --git a/objectapp/templates/objectapp/wlwmanifest.xml b/objectapp/templates/objectapp/wlwmanifest.xml
new file mode 100644
index 0000000..a1a2312
--- /dev/null
+++ b/objectapp/templates/objectapp/wlwmanifest.xml
@@ -0,0 +1,63 @@
+{% load i18n %}<?xml version="1.0" encoding="utf-8" ?>
+<manifest xmlns="http://schemas.microsoft.com/wlw/manifest/weblog">
+ <options>
+ <!-- http://msdn.microsoft.com/en-us/library/bb463260.aspx -->
+ <clientType>Metaweblog</clientType>
+ <supportsPostAsDraft>Yes</supportsPostAsDraft>
+ <supportsFileUpload>Yes</supportsFileUpload>
+ <supportsExtendedGbobjects>No</supportsExtendedGbobjects>
+ <supportsCustomDate>Yes</supportsCustomDate>
+ <supportsObjecttypes>Yes</supportsObjecttypes>
+ <supportsObjecttypesInline>Yes</supportsObjecttypesInline>
+ <supportsMultipleObjecttypes>Yes</supportsMultipleObjecttypes>
+ <supportsHierarchicalObjecttypes>Yes</supportsHierarchicalObjecttypes>
+ <supportsNewObjecttypes>Yes</supportsNewObjecttypes>
+ <supportsNewObjecttypesInline>Yes</supportsNewObjecttypesInline>
+ <supportsKeywords>Yes</supportsKeywords>
+ <supportsCommentPolicy>Yes</supportsCommentPolicy>
+ <supportsPingPolicy>Yes</supportsPingPolicy>
+ <supportsAuthor>Yes</supportsAuthor>
+ <supportsSlug>Yes</supportsSlug>
+ <supportsPassword>Yes</supportsPassword>
+ <supportsExcerpt>Yes</supportsExcerpt>
+ <supportsTrackbacks>No</supportsTrackbacks>
+ <supportsPages>No</supportsPages>
+ <supportsPageParent>No</supportsPageParent>
+ <supportsPageOrder>No</supportsPageOrder>
+ <supportsEmptyTitles>No</supportsEmptyTitles>
+ <requiresHtmlTitles>No</requiresHtmlTitles>
+ <requiresXHTML>No</requiresXHTML>
+ <supportsScripts>Yes</supportsScripts>
+ <supportsEmbeds>Yes</supportsEmbeds>
+ <characterSet>UTF-8</characterSet>
+ <maxObjecttypeNameLength>50</maxObjecttypeNameLength>
+ </options>
+
+ <weblog>
+ <serviceName>{{ site.name }} Blog</serviceName>
+ <imageUrl>{{ STATIC_URL }}objectapp/img/wlw/objectapp.png</imageUrl>
+ <watermarkImageUrl>{{ STATIC_URL }}objectapp/img/wlw/watermark.png</watermarkImageUrl>
+ <homepageLinkText>{% trans "View site" %}</homepageLinkText>
+ <adminLinkText>{% trans "Admin. site" %}</adminLinkText>
+ <adminUrl><![CDATA[
+ {blog-homepage-url}{% url admin:index %}
+ ]]></adminUrl>
+ <postEditingUrl><![CDATA[
+ {blog-homepage-url}{% url admin:objectapp_gbobject_changelist %}{post-id}/
+ ]]></postEditingUrl>
+ </weblog>
+
+ <buttons>
+ <button>
+ <id>0</id>
+ <text>{% trans "Manage comments" %}</text>
+ <imageUrl>{{ STATIC_URL }}objectapp/img/wlw/comments.png</imageUrl>
+ <clickUrl><![CDATA[
+ {blog-homepage-url}{% url admin:comments_comment_changelist %}
+ ]]></clickUrl>
+ </button>
+ </buttons>-->
+
+ <!-- Add views section -->
+</manifest>
+
diff --git a/objectapp/templates/objectapp/wxr.xml b/objectapp/templates/objectapp/wxr.xml
new file mode 100644
index 0000000..09da154
--- /dev/null
+++ b/objectapp/templates/objectapp/wxr.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+{% load tagging_tags %}
+<!-- This is a WordPress eXtended RSS file generated by Objectapp {{ version }} as an export of your blog. -->
+<!-- It contains information about your blog's posts, comments, and objecttypes. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- generator="Objectapp/{{ version }}" created="{% now "Y-m-d H:i" %}"-->
+<rss version="2.0"
+ xmlns:excerpt="http://wordpress.org/export/1.0/excerpt/"
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
+ xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:wp="http://wordpress.org/export/1.0/">
+ <channel>
+ <title>{{ site.name }}</title>
+ <link>{{ site_url }}</link>
+ <description>{{ description }}</description>
+ <pubDate>{% now "r" %}</pubDate>
+ <generator>Objectapp {{ version }}</generator>
+ <language>{{ language }}</language>
+ <wp:wxr_version>1.0</wp:wxr_version>
+ <wp:base_site_url>{{ site_url }}</wp:base_site_url>
+ <wp:base_blog_url>{{ site_url }}{% url objectapp_gbobject_archive_index %}</wp:base_blog_url>
+ {% for Objecttype in objecttypes %}
+ <wp:Objecttype>
+ <wp:Objecttype_nicename>{{ Objecttype.slug }}</wp:Objecttype_nicename>
+ <wp:Objecttype_parent>{{ Objecttype.parent.title }}</wp:Objecttype_parent>
+ <wp:cat_name><![CDATA[{{ Objecttype.title }}]]></wp:cat_name>
+ <wp:Objecttype_description><![CDATA[{{ Objecttype.description }}]]></wp:Objecttype_description>
+ </wp:Objecttype>
+ {% endfor %}{% for tag in tags %}
+ <wp:tag>
+ <wp:tag_slug>{{ tag.name }}</wp:tag_slug>
+ <wp:tag_name><![CDATA[{{ tag.name }}]]></wp:tag_name>
+ </wp:tag>
+ {% endfor %}{% for gbobject in gbobjects %}
+ <item>
+ <title>{{ gbobject.title }}</title>
+ <link>{{ site_url }}{{ gbobject.get_absolute_url }}</link>
+ <pubDate>{{ gbobject.publication_date|date:"r" }}</pubDate>
+ <dc:creator><![CDATA[{{ gbobject.authors.all.0 }}]]></dc:creator>
+ {% for Objecttype in gbobject.objecttypes.all %}
+ <Objecttype domain="Objecttype" nicename="{{ Objecttype.slug }}"><![CDATA[{{ Objecttype.title }}]]></Objecttype>
+ {% endfor %}{% tags_for_object gbobject as tag_list %}{% for tag in tag_list %}
+ <Objecttype domain="tag" nicename="{{ tag }}"><![CDATA[{{ tag }}]]></Objecttype>
+ {% endfor %}
+ <guid isPermaLink="true">{{ site_url }}{{ gbobject.get_absolute_url }}</guid>
+ <description></description>
+ <content:encoded><![CDATA[{{ gbobject.html_content|safe }}]]></content:encoded>
+ <excerpt:encoded><![CDATA[{{ gbobject.excerpt }}]]></excerpt:encoded>
+ <wp:post_id>{{ gbobject.pk }}</wp:post_id>
+ <wp:post_date>{{ gbobject.publication_date|date:"Y-m-d H:i:s"}}</wp:post_date>
+ <wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
+ <wp:comment_status>{% if gbobject.comment_enabled %}open{% else %}closed{% endif %}</wp:comment_status>
+ <wp:ping_status>{% if gbobject.pingback_enabled %}open{% else %}closed{% endif %}</wp:ping_status>
+ <wp:post_name>{{ gbobject.slug }}</wp:post_name>
+ <wp:status>{% if gbobject.is_visible %}{% if gbobject.login_required %}private{% else %}publish{% endif %}{% else %}draft{% endif %}</wp:status>
+ <wp:post_parent>0</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>post</wp:post_type>
+ <wp:post_password>{{ gbobject.password }}</wp:post_password>
+ <wp:is_sticky>{{ gbobject.featured|yesno:"1,0" }}</wp:is_sticky>
+ <wp:postmeta>
+ <wp:meta_key>_last_update</wp:meta_key>
+ <wp:meta_value><![CDATA[{{ gbobject.last_update|date:"c"}}]]></wp:meta_value>
+ </wp:postmeta>
+ <wp:postmeta>
+ <wp:meta_key>_start_publication</wp:meta_key>
+ <wp:meta_value><![CDATA[{{ gbobject.start_publication|date:"c"}}]]></wp:meta_value>
+ </wp:postmeta>
+ <wp:postmeta>
+ <wp:meta_key>_end_publication</wp:meta_key>
+ <wp:meta_value><![CDATA[{{ gbobject.end_publication|date:"c"}}]]></wp:meta_value>
+ </wp:postmeta>
+ {% for comment in gbobject.comments %}
+ <wp:comment>
+ <wp:comment_id>{{ comment.id }}</wp:comment_id>
+ <wp:comment_author><![CDATA[{{ comment.userinfo.name }}]]></wp:comment_author>
+ <wp:comment_author_email>{{ comment.userinfo.email }}</wp:comment_author_email>
+ <wp:comment_author_url>{{ comment.userinfo.url }}</wp:comment_author_url>
+ <wp:comment_author_IP>{{ comment.ip_address }}</wp:comment_author_IP>
+ <wp:comment_date>{{ comment.submit_date|date:"Y-m-d H:i:s" }}</wp:comment_date>
+ <wp:comment_date_gmt>{{ comment.submit_date|date:"Y-m-d H:i:s" }}</wp:comment_date_gmt>
+ <wp:comment_content><![CDATA[{{ comment.comment }}]]></wp:comment_content>
+ <wp:comment_approved>1</wp:comment_approved>
+ <wp:comment_type></wp:comment_type>
+ <wp:comment_parent>0</wp:comment_parent>
+ <wp:comment_user_id>0</wp:comment_user_id>
+ </wp:comment>
+ {% endfor %}{% for comment in gbobject.pingbacks %}
+ <wp:comment>
+ <wp:comment_id>{{ comment.id }}</wp:comment_id>
+ <wp:comment_author><![CDATA[{{ comment.userinfo.name }}]]></wp:comment_author>
+ <wp:comment_author_email>{{ comment.userinfo.email }}</wp:comment_author_email>
+ <wp:comment_author_url>{{ comment.userinfo.url }}</wp:comment_author_url>
+ <wp:comment_author_IP>{{ comment.ip_address }}</wp:comment_author_IP>
+ <wp:comment_date>{{ comment.submit_date|date:"Y-m-d H:i:s" }}</wp:comment_date>
+ <wp:comment_date_gmt>{{ comment.submit_date|date:"Y-m-d H:i:s" }}</wp:comment_date_gmt>
+ <wp:comment_content><![CDATA[{{ comment.comment }}]]></wp:comment_content>
+ <wp:comment_approved>1</wp:comment_approved>
+ <wp:comment_type>pingback</wp:comment_type>
+ <wp:comment_parent>0</wp:comment_parent>
+ <wp:comment_user_id>0</wp:comment_user_id>
+ </wp:comment>
+ {% endfor %}{% for comment in gbobject.trackbacks %}
+ <wp:comment>
+ <wp:comment_id>{{ comment.id }}</wp:comment_id>
+ <wp:comment_author><![CDATA[{{ comment.userinfo.name }}]]></wp:comment_author>
+ <wp:comment_author_email>{{ comment.userinfo.email }}</wp:comment_author_email>
+ <wp:comment_author_url>{{ comment.userinfo.url }}</wp:comment_author_url>
+ <wp:comment_author_IP>{{ comment.ip_address }}</wp:comment_author_IP>
+ <wp:comment_date>{{ comment.submit_date|date:"Y-m-d H:i:s" }}</wp:comment_date>
+ <wp:comment_date_gmt>{{ comment.submit_date|date:"Y-m-d H:i:s" }}</wp:comment_date_gmt>
+ <wp:comment_content><![CDATA[{{ comment.comment }}]]></wp:comment_content>
+ <wp:comment_approved>1</wp:comment_approved>
+ <wp:comment_type>trackback</wp:comment_type>
+ <wp:comment_parent>0</wp:comment_parent>
+ <wp:comment_user_id>0</wp:comment_user_id>
+ </wp:comment>
+ {% endfor %}
+ </item>
+ {% endfor %}
+ </channel>
+</rss>
diff --git a/objectapp/templates/objectappforms/gbobjectform.html b/objectapp/templates/objectappforms/gbobjectform.html
new file mode 100644
index 0000000..02e3bb5
--- /dev/null
+++ b/objectapp/templates/objectappforms/gbobjectform.html
@@ -0,0 +1,22 @@
+{% extends "gstudio/base.html" %}
+{% load i18n %}
+
+{% block content %}
+<form method="post" action=".">
+{% csrf_token %}
+<h2>Form to add Objects</h2>
+
+{% for field in formset %}
+
+ {{ field.label_tag }}:{{ field }}<br>
+ {{field.help_text}}<br>
+ {{ field.errors }}<br>
+
+
+{% endfor %}
+
+ <input type="Submit" value="{% trans 'ADD' %}" />
+ <input type="hidden" name="next" value="{{ next }}" />
+</form>
+
+{% endblock %}
diff --git a/objectapp/templates/objectappforms/processform.html b/objectapp/templates/objectappforms/processform.html
new file mode 100644
index 0000000..3a99bff
--- /dev/null
+++ b/objectapp/templates/objectappforms/processform.html
@@ -0,0 +1,22 @@
+{% extends "gstudio/base.html" %}
+{% load i18n %}
+
+{% block content %}
+<form method="post" action=".">
+{% csrf_token %}
+<h2>Form to add Processes</h2>
+
+{% for field in formset %}
+
+ {{ field.label_tag }}:{{ field }}<br>
+ {{field.help_text}}<br>
+ {{ field.errors }}<br>
+
+
+{% endfor %}
+
+ <input type="Submit" value="{% trans 'ADD' %}" />
+ <input type="hidden" name="next" value="{{ next }}" />
+</form>
+
+{% endblock %}
diff --git a/objectapp/templates/objectappforms/systemform.html b/objectapp/templates/objectappforms/systemform.html
new file mode 100644
index 0000000..9635940
--- /dev/null
+++ b/objectapp/templates/objectappforms/systemform.html
@@ -0,0 +1,22 @@
+{% extends "gstudio/base.html" %}
+{% load i18n %}
+
+{% block content %}
+<form method="post" action=".">
+{% csrf_token %}
+<h2>Form to add Systems</h2>
+
+{% for field in formset %}
+
+ {{ field.label_tag }}:{{ field }}<br>
+ {{field.help_text}}<br>
+ {{ field.errors }}<br>
+
+
+{% endfor %}
+
+ <input type="Submit" value="{% trans 'ADD' %}" />
+ <input type="hidden" name="next" value="{{ next }}" />
+</form>
+
+{% endblock %}
diff --git a/objectapp/templatetags/__init__.py b/objectapp/templatetags/__init__.py
new file mode 100644
index 0000000..118b76b
--- /dev/null
+++ b/objectapp/templatetags/__init__.py
@@ -0,0 +1 @@
+"""Templatetags for Objectapp"""
diff --git a/objectapp/templatetags/objectapp_admin_tags.py b/objectapp/templatetags/objectapp_admin_tags.py
new file mode 100644
index 0000000..7d5432d
--- /dev/null
+++ b/objectapp/templatetags/objectapp_admin_tags.py
@@ -0,0 +1,89 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Template tags and filters for Objectapp's admin"""
+from django.template import Library
+from django.contrib import comments
+from django.contrib.contenttypes.models import ContentType
+
+from objectapp.models import Gbobject
+from objectapp.models import Author
+from objectapp.models import Objecttype
+from objectapp.managers import DRAFT
+from objectapp.managers import tags_published
+
+register = Library()
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_draft_gbobjects(
+ number=5, template='admin/objectapp/widgets/_draft_gbobjects.html'):
+ """Return the latest draft gbobjects"""
+ return {'template': template,
+ 'gbobjects': Gbobject.objects.filter(status=DRAFT)[:number]}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_content_stats(
+ template='admin/objectapp/widgets/_content_stats.html'):
+ """Return statistics of the contents"""
+ content_type = ContentType.objects.get_for_model(Gbobject)
+
+ discussions = comments.get_model().objects.filter(
+ is_public=True, content_type=content_type)
+
+ return {'template': template,
+ 'gbobjects': Gbobject.published.count(),
+ 'objecttypes': Objecttype.objects.count(),
+ 'tags': tags_published().count(),
+ 'authors': Author.published.count(),
+ 'comments': discussions.filter(flags=None).count(),
+ 'pingbacks': discussions.filter(flags__flag='pingback').count(),
+ 'trackbacks': discussions.filter(flags__flag='trackback').count(),
+ 'rejects': comments.get_model().objects.filter(
+ is_public=False, content_type=content_type).count(),
+ }
diff --git a/objectapp/templatetags/objectapp_tags.py b/objectapp/templatetags/objectapp_tags.py
new file mode 100644
index 0000000..209223c
--- /dev/null
+++ b/objectapp/templatetags/objectapp_tags.py
@@ -0,0 +1,375 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Template tags and filters for Objectapp"""
+from hashlib import md5
+from random import sample
+from urllib import urlencode
+from datetime import datetime
+
+from django.db.models import Q
+from django.db import connection
+from django.template import Node
+from django.template import Library
+from django.template import TemplateSyntaxError
+from django.contrib.comments.models import CommentFlag
+from django.contrib.contenttypes.models import ContentType
+from django.utils.encoding import smart_unicode
+from django.contrib.comments import get_model as get_comment_model
+
+from tagging.models import Tag
+from tagging.utils import calculate_cloud
+
+from gstudio.gnowql import get_node
+
+from objectapp.models import Gbobject
+from objectapp.models import Author
+from objectapp.models import Objecttype
+from objectapp.managers import tags_published
+from objectapp.comparison import VectorBuilder
+from objectapp.comparison import pearson_score
+from objectapp.templatetags.zcalendar import ObjectappCalendar
+from objectapp.templatetags.zbreadcrumbs import retrieve_breadcrumbs
+
+register = Library()
+
+VECTORS = None
+VECTORS_FACTORY = lambda: VectorBuilder(Gbobject.published.all(),
+ ['title', 'excerpt', 'content'])
+CACHE_GBOBJECTS_RELATED = {}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_objecttypes(template='objectapp/tags/objecttypes.html'):
+ """Return the objecttypes"""
+ return {'template': template,
+ 'objecttypes': Objecttype.tree.all()}
+
+@register.simple_tag
+def get_this_nodes_uri(name):
+ obj = get_node(name)
+ return obj.get_absolute_url()
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_authors(template='objectapp/tags/authors.html'):
+ """Return the published authors"""
+ return {'template': template,
+ 'authors': Author.published.all()}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_recent_gbobjects(number=5, template='objectapp/tags/recent_gbobjects.html'):
+ """Return the most recent gbobjects"""
+ return {'template': template,
+ 'gbobjects': Gbobject.published.all()[:number]}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_featured_gbobjects(number=5,
+ template='objectapp/tags/featured_gbobjects.html'):
+ """Return the featured gbobjects"""
+ return {'template': template,
+ 'gbobjects': Gbobject.published.filter(featured=True)[:number]}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_random_gbobjects(number=5, template='objectapp/tags/random_gbobjects.html'):
+ """Return random gbobjects"""
+ gbobjects = Gbobject.published.all()
+ if number > len(gbobjects):
+ number = len(gbobjects)
+ return {'template': template,
+ 'gbobjects': sample(gbobjects, number)}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_popular_gbobjects(number=5, template='objectapp/tags/popular_gbobjects.html'):
+ """Return popular gbobjects"""
+ ctype = ContentType.objects.get_for_model(Gbobject)
+ query = """SELECT object_pk, COUNT(*) AS score
+ FROM %s
+ WHERE content_type_id = %%s
+ AND is_public = '1'
+ GROUP BY object_pk
+ ORDER BY score DESC""" % get_comment_model()._meta.db_table
+
+ cursor = connection.cursor()
+ cursor.execute(query, [ctype.id])
+ object_ids = [int(row[0]) for row in cursor.fetchall()]
+
+ # Use ``in_bulk`` here instead of an ``id__in`` filter, because ``id__in``
+ # would clobber the ordering.
+ object_dict = Gbobject.published.in_bulk(object_ids)
+
+ return {'template': template,
+ 'gbobjects': [object_dict[object_id]
+ for object_id in object_ids
+ if object_id in object_dict][:number]}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html', takes_context=True)
+def get_similar_gbobjects(context, number=5,
+ template='objectapp/tags/similar_gbobjects.html',
+ flush=False):
+ """Return similar gbobjects"""
+ global VECTORS
+ global CACHE_GBOBJECTS_RELATED
+
+ if VECTORS is None or flush:
+ VECTORS = VECTORS_FACTORY()
+ CACHE_GBOBJECTS_RELATED = {}
+
+ def compute_related(object_id, dataset):
+ """Compute related gbobjects to an gbobject with a dataset"""
+ object_vector = None
+ for gbobject, e_vector in dataset.items():
+ if gbobject.pk == object_id:
+ object_vector = e_vector
+
+ if not object_vector:
+ return []
+
+ gbobject_related = {}
+ for gbobject, e_vector in dataset.items():
+ if gbobject.pk != object_id:
+ score = pearson_score(object_vector, e_vector)
+ if score:
+ gbobject_related[gbobject] = score
+
+ related = sorted(gbobject_related.items(), key=lambda(k, v): (v, k))
+ return [rel[0] for rel in related]
+
+ object_id = context['object'].pk
+ columns, dataset = VECTORS()
+ key = '%s-%s' % (object_id, VECTORS.key)
+ if not key in CACHE_GBOBJECTS_RELATED.keys():
+ CACHE_GBOBJECTS_RELATED[key] = compute_related(object_id, dataset)
+
+ gbobjects = CACHE_GBOBJECTS_RELATED[key][:number]
+ return {'template': template,
+ 'gbobjects': gbobjects}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_archives_gbobjects(template='objectapp/tags/archives_gbobjects.html'):
+ """Return archives gbobjects"""
+ return {'template': template,
+ 'archives': Gbobject.published.dates('creation_date', 'month',
+ order='DESC')}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_archives_gbobjects_tree(
+ template='objectapp/tags/archives_gbobjects_tree.html'):
+ """Return archives gbobjects as a Tree"""
+ return {'template': template,
+ 'archives': Gbobject.published.dates('creation_date', 'day',
+ order='ASC')}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html', takes_context=True)
+def get_calendar_gbobjects(context, year=None, month=None,
+ template='objectapp/tags/calendar.html'):
+ """Return an HTML calendar of gbobjects"""
+ if not year or not month:
+ date_month = context.get('month') or context.get('day') or \
+ getattr(context.get('object'), 'creation_date', None) or \
+ datetime.today()
+ year, month = date_month.timetuple()[:2]
+
+ calendar = ObjectappCalendar()
+ current_month = datetime(year, month, 1)
+
+ dates = list(Gbobject.published.dates('creation_date', 'month'))
+
+ if not current_month in dates:
+ dates.append(current_month)
+ dates.sort()
+ index = dates.index(current_month)
+
+ previous_month = index > 0 and dates[index - 1] or None
+ next_month = index != len(dates) - 1 and dates[index + 1] or None
+
+ return {'template': template,
+ 'next_month': next_month,
+ 'previous_month': previous_month,
+ 'calendar': calendar.formatmonth(year, month)}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_recent_comments(number=5, template='objectapp/tags/recent_comments.html'):
+ """Return the most recent comments"""
+ # Using map(smart_unicode... fix bug related to issue #8554
+ gbobject_published_pks = map(smart_unicode,
+ Gbobject.published.values_list('id', flat=True))
+ content_type = ContentType.objects.get_for_model(Gbobject)
+
+ comments = get_comment_model().objects.filter(
+ Q(flags=None) | Q(flags__flag=CommentFlag.MODERATOR_APPROVAL),
+ content_type=content_type, object_pk__in=gbobject_published_pks,
+ is_public=True).order_by('-submit_date')[:number]
+
+ return {'template': template,
+ 'comments': comments}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_recent_linkbacks(number=5,
+ template='objectapp/tags/recent_linkbacks.html'):
+ """Return the most recent linkbacks"""
+ gbobject_published_pks = map(smart_unicode,
+ Gbobject.published.values_list('id', flat=True))
+ content_type = ContentType.objects.get_for_model(Gbobject)
+
+ linkbacks = get_comment_model().objects.filter(
+ content_type=content_type,
+ object_pk__in=gbobject_published_pks,
+ flags__flag__in=['pingback', 'trackback'],
+ is_public=True).order_by(
+ '-submit_date')[:number]
+
+ return {'template': template,
+ 'linkbacks': linkbacks}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html', takes_context=True)
+def objectapp_pagination(context, page, begin_pages=3, end_pages=3,
+ before_pages=2, after_pages=2,
+ template='objectapp/tags/pagination.html'):
+ """Return a Digg-like pagination, by splitting long list of page
+ into 3 blocks of pages"""
+ GET_string = ''
+ for key, value in context['request'].GET.items():
+ if key != 'page':
+ GET_string += '&%s=%s' % (key, value)
+
+ begin = page.paginator.page_range[:begin_pages]
+ end = page.paginator.page_range[-end_pages:]
+ middle = page.paginator.page_range[max(page.number - before_pages - 1, 0):
+ page.number + after_pages]
+
+ if set(begin) & set(end): # [1, 2, 3], [...], [2, 3, 4]
+ begin = sorted(set(begin + end)) # [1, 2, 3, 4]
+ middle, end = [], []
+ elif begin[-1] + 1 == end[0]: # [1, 2, 3], [...], [4, 5, 6]
+ begin += end # [1, 2, 3, 4, 5, 6]
+ middle, end = [], []
+ elif set(begin) & set(middle): # [1, 2, 3], [2, 3, 4], [...]
+ begin = sorted(set(begin + middle)) # [1, 2, 3, 4]
+ middle = []
+ elif begin[-1] + 1 == middle[0]: # [1, 2, 3], [4, 5, 6], [...]
+ begin += middle # [1, 2, 3, 4, 5, 6]
+ middle = []
+ elif middle[-1] + 1 == end[0]: # [...], [15, 16, 17], [18, 19, 20]
+ end = middle + end # [15, 16, 17, 18, 19, 20]
+ middle = []
+ elif set(middle) & set(end): # [...], [17, 18, 19], [18, 19, 20]
+ end = sorted(set(middle + end)) # [17, 18, 19, 20]
+ middle = []
+
+ return {'template': template, 'page': page, 'GET_string': GET_string,
+ 'begin': begin, 'middle': middle, 'end': end}
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html', takes_context=True)
+def objectapp_breadcrumbs(context, separator='/', root_name='Objects',
+ template='objectapp/tags/breadcrumbs.html',):
+ """Return a breadcrumb for the application"""
+ path = context['request'].path
+ page_object = context.get('object') or context.get('Objecttype') or \
+ context.get('tag') or context.get('author')
+ breadcrumbs = retrieve_breadcrumbs(path, page_object, root_name)
+
+ return {'template': template,
+ 'separator': separator,
+ 'breadcrumbs': breadcrumbs}
+
+
+@register.simple_tag
+def get_gravatar(email, size=80, rating='g', default=None):
+ """Return url for a Gravatar"""
+ url = 'http://www.gravatar.com/avatar/%s.jpg' % \
+ md5(email.strip().lower()).hexdigest()
+ options = {'s': size, 'r': rating}
+ if default:
+ options['d'] = default
+
+ url = '%s?%s' % (url, urlencode(options))
+ return url.replace('&', '&amp;')
+
+
+class TagsNode(Node):
+ def __init__(self, context_var):
+ self.context_var = context_var
+
+ def render(self, context):
+ context[self.context_var] = tags_published()
+ return ''
+
+
+@register.tag
+def get_tags(parser, token):
+ """{% get_tags as var %}"""
+ bits = token.split_contents()
+
+ if len(bits) != 3:
+ raise TemplateSyntaxError(
+ 'get_tags tag takes exactly two arguments')
+ if bits[1] != 'as':
+ raise TemplateSyntaxError(
+ "first argument to get_tags tag must be 'as'")
+ return TagsNode(bits[2])
+
+
+@register.inclusion_tag('objectapp/tags/dummy.html')
+def get_tag_cloud(steps=6, template='objectapp/tags/tag_cloud.html'):
+ """Return a cloud of published tags"""
+ tags = Tag.objects.usage_for_queryset(
+ Gbobject.published.all(), counts=True)
+ return {'template': template,
+ 'tags': calculate_cloud(tags, steps)}
diff --git a/objectapp/templatetags/zbreadcrumbs.py b/objectapp/templatetags/zbreadcrumbs.py
new file mode 100644
index 0000000..3fc61df
--- /dev/null
+++ b/objectapp/templatetags/zbreadcrumbs.py
@@ -0,0 +1,147 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Breadcrumb module for Objectapp templatetags"""
+import re
+from datetime import datetime
+
+from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext as _
+
+
+class Crumb(object):
+ """Part of the Breadcrumbs"""
+ def __init__(self, name, url=None):
+ self.name = name
+ self.url = url
+
+
+def year_crumb(creation_date):
+ """Crumb for a year"""
+ year = creation_date.strftime('%Y')
+ return Crumb(year, reverse('objectapp_gbobject_archive_year',
+ args=[year]))
+
+
+def month_crumb(creation_date):
+ """Crumb for a month"""
+ year = creation_date.strftime('%Y')
+ month = creation_date.strftime('%m')
+ month_text = creation_date.strftime('%b').capitalize()
+ return Crumb(month_text, reverse('objectapp_gbobject_archive_month',
+ args=[year, month]))
+
+
+def day_crumb(creation_date):
+ """Crumb for a day"""
+ year = creation_date.strftime('%Y')
+ month = creation_date.strftime('%m')
+ day = creation_date.strftime('%d')
+ return Crumb(day, reverse('objectapp_gbobject_archive_day',
+ args=[year, month, day]))
+
+
+OBJECTAPP_ROOT_URL = lambda: reverse('objectapp_gbobject_archive_index')
+
+MODEL_BREADCRUMBS = {'Tag': lambda x: [Crumb(_('Tags'),
+ reverse('objectapp_tag_list')),
+ Crumb(x.name)],
+ 'Author': lambda x: [Crumb(_('Authors'),
+ reverse('objectapp_author_list')),
+ Crumb(x.username)],
+ 'Objecttype': lambda x: [Crumb(
+ _('Objecttypes'), reverse('objectapp_Objecttype_list'))] + \
+ [Crumb(anc.title, anc.get_absolute_url())
+ for anc in x.get_ancestors()] + [Crumb(x.title)],
+ 'Gbobject': lambda x: [year_crumb(x.creation_date),
+ month_crumb(x.creation_date),
+ day_crumb(x.creation_date),
+ Crumb(x.title)]}
+
+DATE_REGEXP = re.compile(
+ r'.*(?P<year>\d{4})/(?P<month>\d{2})?/(?P<day>\d{2})?.*')
+
+
+def retrieve_breadcrumbs(path, model_instance, root_name=''):
+ """Build a semi-hardcoded breadcrumbs
+ based of the model's url handled by Objectapp"""
+ breadcrumbs = []
+
+ if root_name:
+ breadcrumbs.append(Crumb(root_name, OBJECTAPP_ROOT_URL()))
+
+ if model_instance is not None:
+ key = model_instance.__class__.__name__
+ if key in MODEL_BREADCRUMBS:
+ breadcrumbs.extend(MODEL_BREADCRUMBS[key](model_instance))
+ return breadcrumbs
+
+ date_match = DATE_REGEXP.match(path)
+ if date_match:
+ date_dict = date_match.groupdict()
+ path_date = datetime(
+ int(date_dict['year']),
+ date_dict.get('month') is not None and \
+ int(date_dict.get('month')) or 1,
+ date_dict.get('day') is not None and \
+ int(date_dict.get('day')) or 1)
+
+ date_breadcrumbs = [year_crumb(path_date)]
+ if date_dict['month']:
+ date_breadcrumbs.append(month_crumb(path_date))
+ if date_dict['day']:
+ date_breadcrumbs.append(day_crumb(path_date))
+ breadcrumbs.extend(date_breadcrumbs)
+
+ return breadcrumbs
+
+ url_components = [comp for comp in
+ path.replace(OBJECTAPP_ROOT_URL(), '').split('/') if comp]
+ if len(url_components):
+ breadcrumbs.append(Crumb(_(url_components[-1].capitalize())))
+
+ return breadcrumbs
diff --git a/objectapp/templatetags/zcalendar.py b/objectapp/templatetags/zcalendar.py
new file mode 100644
index 0000000..24c087b
--- /dev/null
+++ b/objectapp/templatetags/zcalendar.py
@@ -0,0 +1,109 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Calendar module for Objectapp templatetags"""
+from datetime import date
+from calendar import HTMLCalendar
+
+from django.utils.dates import MONTHS
+from django.utils.dates import WEEKDAYS_ABBR
+from django.utils.formats import get_format
+from django.core.urlresolvers import reverse
+
+from objectapp.models import Gbobject
+
+AMERICAN_TO_EUROPEAN_WEEK_DAYS = [6, 0, 1, 2, 3, 4, 5]
+
+
+class ObjectappCalendar(HTMLCalendar):
+ """Override of HTMLCalendar"""
+
+ def __init__(self):
+ """Retrieve and convert the localized first week day
+ at initialization"""
+ HTMLCalendar.__init__(self, AMERICAN_TO_EUROPEAN_WEEK_DAYS[
+ get_format('FIRST_DAY_OF_WEEK')])
+
+ def formatday(self, day, weekday):
+ """Return a day as a table cell with a link
+ if gbobjects are published this day"""
+ if day and day in self.day_gbobjects:
+ day_date = date(self.current_year, self.current_month, day)
+ archive_day_url = reverse('objectapp_gbobject_archive_day',
+ args=[day_date.strftime('%Y'),
+ day_date.strftime('%m'),
+ day_date.strftime('%d')])
+ return '<td class="%s gbobject"><a href="%s" '\
+ 'rel="archives">%d</a></td>' % (
+ self.cssclasses[weekday], archive_day_url, day)
+
+ return super(ObjectappCalendar, self).formatday(day, weekday)
+
+ def formatmonth(self, theyear, themonth, withyear=True):
+ """Return a formatted month as a table with
+ new attributes computed for formatting a day"""
+ self.current_year = theyear
+ self.current_month = themonth
+ self.day_gbobjects = [gbobjects.creation_date.day for gbobjects in
+ Gbobject.published.filter(
+ creation_date__year=theyear,
+ creation_date__month=themonth)]
+
+ return super(ObjectappCalendar, self).formatmonth(
+ theyear, themonth, withyear)
+
+ def formatweekday(self, day):
+ """Return a weekday name translated
+ as a table header."""
+ return '<th class="%s">%s</th>' % (self.cssclasses[day],
+ WEEKDAYS_ABBR[day].title())
+
+ def formatmonthname(self, theyear, themonth, withyear=True):
+ """Return a month name translated
+ as a table row."""
+ monthname = '%s %s' % (MONTHS[themonth].title(), theyear)
+ return '<tr><th colspan="7" class="month">%s</th></tr>' % monthname
diff --git a/objectapp/tests/__init__.py b/objectapp/tests/__init__.py
new file mode 100644
index 0000000..7e8f4fc
--- /dev/null
+++ b/objectapp/tests/__init__.py
@@ -0,0 +1,104 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Unit tests for Objectapp"""
+from unittest import TestSuite
+from unittest import TestLoader
+from django.conf import settings
+
+from objectapp.tests.gbobject import GbobjectTestCase # ~0.2s
+from objectapp.tests.gbobject import GbobjectHtmlContentTestCase # ~0.5s
+from objectapp.tests.gbobject import GbobjectGetBaseModelTestCase
+from objectapp.tests.signals import SignalsTestCase
+from objectapp.tests.Objecttype import ObjecttypeTestCase
+from objectapp.tests.admin import GbobjectAdminTestCase
+from objectapp.tests.admin import ObjecttypeAdminTestCase
+from objectapp.tests.managers import ManagersTestCase # ~1.2s
+from objectapp.tests.feeds import ObjectappFeedsTestCase # ~0.4s
+from objectapp.tests.views import ObjectappViewsTestCase # ~1.5s ouch...
+from objectapp.tests.views import ObjectappCustomDetailViews # ~0.3s
+from objectapp.tests.pingback import PingBackTestCase # ~0.3s
+from objectapp.tests.metaweblog import MetaWeblogTestCase # ~0.6s
+from objectapp.tests.comparison import ComparisonTestCase
+from objectapp.tests.quick_gbobject import QuickGbobjectTestCase # ~0.4s
+from objectapp.tests.sitemaps import ObjectappSitemapsTestCase # ~0.3s
+from objectapp.tests.ping import DirectoryPingerTestCase
+from objectapp.tests.ping import ExternalUrlsPingerTestCase
+from objectapp.tests.templatetags import TemplateTagsTestCase # ~0.4s
+from objectapp.tests.moderator import GbobjectCommentModeratorTestCase # ~0.1s
+from objectapp.tests.spam_checker import SpamCheckerTestCase
+from objectapp.tests.url_shortener import URLShortenerTestCase
+from objectapp.signals import disconnect_objectapp_signals
+# TOTAL ~ 6.6s
+
+
+def suite():
+ """Suite of TestCases for Django"""
+ suite = TestSuite()
+ loader = TestLoader()
+
+ test_cases = (ManagersTestCase, GbobjectTestCase,
+ GbobjectGetBaseModelTestCase, SignalsTestCase,
+ GbobjectHtmlContentTestCase, ObjecttypeTestCase,
+ ObjectappViewsTestCase, ObjectappFeedsTestCase,
+ ObjectappSitemapsTestCase, ComparisonTestCase,
+ DirectoryPingerTestCase, ExternalUrlsPingerTestCase,
+ TemplateTagsTestCase, QuickGbobjectTestCase,
+ URLShortenerTestCase, GbobjectCommentModeratorTestCase,
+ ObjectappCustomDetailViews, SpamCheckerTestCase,
+ GbobjectAdminTestCase, ObjecttypeAdminTestCase)
+
+ if 'django_xmlrpc' in settings.INSTALLED_APPS:
+ test_cases += (PingBackTestCase, MetaWeblogTestCase)
+
+ for test_class in test_cases:
+ tests = loader.loadTestsFromTestCase(test_class)
+ suite.addTests(tests)
+
+ return suite
+
+disconnect_objectapp_signals()
diff --git a/objectapp/tests/admin.py b/objectapp/tests/admin.py
new file mode 100644
index 0000000..7962698
--- /dev/null
+++ b/objectapp/tests/admin.py
@@ -0,0 +1,133 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test cases for Objectapp's admin"""
+from django.test import TestCase
+from django.contrib.auth.models import User
+
+from objectapp import settings
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+
+
+class GbobjectAdminTestCase(TestCase):
+ """Test case for Gbobject Admin"""
+ urls = 'objectapp.tests.urls'
+
+ def setUp(self):
+ self.original_wysiwyg = settings.WYSIWYG
+ settings.WYSIWYG = None
+ User.objects.create_superuser('admin', 'admin@example.com', 'password')
+ Objecttype_1 = Objecttype.objects.create(title='Objecttype 1', slug='cat-1')
+ Objecttype.objects.create(title='Objecttype 2', slug='cat-2',
+ parent=Objecttype_1)
+
+ self.client.login(username='admin', password='password')
+
+ def tearDown(self):
+ settings.WYSIWYG = self.original_wysiwyg
+
+ def test_gbobject_add_and_change(self):
+ """Test the insertion of an Gbobject"""
+ self.assertEquals(Gbobject.objects.count(), 0)
+ post_data = {'title': u'New gbobject',
+ 'template': u'objectapp/gbobject_detail.html',
+ 'creation_date_0': u'2011-01-01',
+ 'creation_date_1': u'12:00:00',
+ 'start_publication_0': u'2011-01-01',
+ 'start_publication_1': u'12:00:00',
+ 'end_publication_0': u'2042-03-15',
+ 'end_publication_1': u'00:00:00',
+ 'status': u'2',
+ 'sites': u'1',
+ 'content': u'My content'}
+
+ response = self.client.post('/admin/objectapp/gbobject/add/', post_data)
+ self.assertEquals(response.status_code, 200)
+ self.assertEquals(Gbobject.objects.count(), 0)
+
+ post_data.update({'slug': u'new-gbobject'})
+ response = self.client.post('/admin/objectapp/gbobject/add/',
+ post_data, follow=True)
+ self.assertEquals(response.redirect_chain,
+ [('http://testserver/admin/objectapp/gbobject/', 302)])
+ self.assertEquals(Gbobject.objects.count(), 1)
+
+
+class ObjecttypeAdminTestCase(TestCase):
+ """Test cases for Objecttype Admin"""
+ urls = 'objectapp.tests.urls'
+
+ def setUp(self):
+ User.objects.create_superuser('admin', 'admin@example.com', 'password')
+ self.client.login(username='admin', password='password')
+
+ def test_Objecttype_add_and_change(self):
+ """Test the insertion of a Objecttype, change error, and new insert"""
+ self.assertEquals(Objecttype.objects.count(), 0)
+ post_data = {'title': u'New Objecttype',
+ 'slug': u'new-Objecttype'}
+ response = self.client.post('/admin/objectapp/Objecttype/add/',
+ post_data, follow=True)
+ self.assertEquals(response.redirect_chain,
+ [('http://testserver/admin/objectapp/Objecttype/', 302)])
+ self.assertEquals(Objecttype.objects.count(), 1)
+
+ post_data.update({'parent': u'1'})
+ response = self.client.post('/admin/objectapp/Objecttype/1/', post_data)
+ self.assertEquals(response.status_code, 200)
+
+ response = self.client.post('/admin/objectapp/Objecttype/add/', post_data)
+ self.assertEquals(response.status_code, 200)
+ self.assertEquals(Objecttype.objects.count(), 1)
+
+ post_data.update({'slug': u'new-Objecttype-2'})
+ response = self.client.post('/admin/objectapp/Objecttype/add/',
+ post_data, follow=True)
+ self.assertEquals(response.redirect_chain,
+ [('http://testserver/admin/objectapp/Objecttype/', 302)])
+ self.assertEquals(Objecttype.objects.count(), 2)
diff --git a/objectapp/tests/comparison.py b/objectapp/tests/comparison.py
new file mode 100644
index 0000000..7f113be
--- /dev/null
+++ b/objectapp/tests/comparison.py
@@ -0,0 +1,50 @@
+"""Test cases for Objectapp's comparison"""
+from django.test import TestCase
+
+from objectapp.models import Gbobject
+from objectapp.comparison import pearson_score
+from objectapp.comparison import VectorBuilder
+from objectapp.comparison import ClusteredModel
+
+
+class ComparisonTestCase(TestCase):
+ """Test cases for comparison tools"""
+
+ def test_pearson_score(self):
+ self.assertEquals(pearson_score([42], [42]), 0.0)
+ self.assertEquals(pearson_score([0, 1, 2], [0, 1, 2]), 0.0)
+ self.assertEquals(pearson_score([0, 1, 3], [0, 1, 2]),
+ 0.051316701949486232)
+ self.assertEquals(pearson_score([0, 1, 2], [0, 1, 3]),
+ 0.051316701949486232)
+
+ def test_clustered_model(self):
+ params = {'title': 'My gbobject 1', 'content': 'My content 1',
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-1'}
+ Gbobject.objects.create(**params)
+ params = {'title': 'My gbobject 2', 'content': 'My content 2',
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-2'}
+ Gbobject.objects.create(**params)
+ cm = ClusteredModel(Gbobject.objects.all())
+ self.assertEquals(cm.dataset().values(), ['1', '2'])
+ cm = ClusteredModel(Gbobject.objects.all(),
+ ['title', 'excerpt', 'content'])
+ self.assertEquals(cm.dataset().values(), ['My gbobject 1 My content 1',
+ 'My gbobject 2 My content 2'])
+
+ def test_vector_builder(self):
+ vectors = VectorBuilder(Gbobject.objects.all(),
+ ['title', 'excerpt', 'content'])
+ params = {'title': 'My gbobject 1', 'content':
+ 'This is my first content',
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-1'}
+ Gbobject.objects.create(**params)
+ params = {'title': 'My gbobject 2', 'content':
+ 'My second gbobject',
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-2'}
+ Gbobject.objects.create(**params)
+ columns, dataset = vectors()
+ self.assertEquals(columns, ['content', 'This', 'my', 'is', '1',
+ 'second', '2', 'first'])
+ self.assertEquals(dataset.values(), [[1, 1, 1, 1, 1, 0, 0, 1],
+ [0, 0, 0, 0, 0, 1, 1, 0]])
diff --git a/objectapp/tests/custom_spam_checker.py b/objectapp/tests/custom_spam_checker.py
new file mode 100644
index 0000000..16ccea4
--- /dev/null
+++ b/objectapp/tests/custom_spam_checker.py
@@ -0,0 +1,10 @@
+"""Custom spam checker backend for testing Objectapp"""
+from django.core.exceptions import ImproperlyConfigured
+
+
+raise ImproperlyConfigured('This backend only exists for testing')
+
+
+def backend(gbobject):
+ """Custom spam checker backend for testing Objectapp"""
+ return False
diff --git a/objectapp/tests/custom_url_shortener.py b/objectapp/tests/custom_url_shortener.py
new file mode 100644
index 0000000..7d0aecc
--- /dev/null
+++ b/objectapp/tests/custom_url_shortener.py
@@ -0,0 +1,57 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Custom url shortener backend for testing Objectapp"""
+from django.core.exceptions import ImproperlyConfigured
+
+
+raise ImproperlyConfigured('This backend only exists for testing')
+
+
+def backend(gbobject):
+ """Custom url shortener backend for testing Objectapp"""
+ return ''
diff --git a/objectapp/tests/custom_views_detail_urls.py b/objectapp/tests/custom_views_detail_urls.py
new file mode 100644
index 0000000..6ff0e48
--- /dev/null
+++ b/objectapp/tests/custom_views_detail_urls.py
@@ -0,0 +1,91 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test urls for the objectapp project"""
+from functools import wraps
+
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+from objectapp.views.tags import tag_detail
+from objectapp.views.authors import author_detail
+from objectapp.views.objecttypes import Objecttype_detail
+from objectapp.tests.urls import urlpatterns as test_urlpatterns
+
+
+def call_with_template_and_extra_context(
+ view, template_name='objectapp/gbobject_list.html',
+ extra_context={'extra': 'context'}):
+
+ @wraps(view)
+ def wrapper(*args, **kwargs):
+ return view(template_name=template_name,
+ extra_context=extra_context,
+ *args, **kwargs)
+
+ return wrapper
+
+custom_tag_detail = call_with_template_and_extra_context(tag_detail)
+custom_author_detail = call_with_template_and_extra_context(author_detail)
+custom_Objecttype_detail = call_with_template_and_extra_context(Objecttype_detail)
+
+
+urlpatterns = patterns(
+ '',
+ url(r'^authors/(?P<username>[.+-@\w]+)/$',
+ custom_author_detail, name='objectapp_author_detail'),
+ url(r'^authors/(?P<username>[.+-@\w]+)/page/(?P<page>\d+)/$',
+ custom_author_detail, name='objectapp_author_detail_paginated'),
+ url(r'^objecttypes/(?P<path>[-\/\w]+)/page/(?P<page>\d+)/$',
+ custom_Objecttype_detail, name='objectapp_Objecttype_detail_paginated'),
+ url(r'^objecttypes/(?P<path>[-\/\w]+)/$',
+ custom_Objecttype_detail, name='objectapp_Objecttype_detail'),
+ url(r'^tags/(?P<tag>[- \w]+)/$',
+ custom_tag_detail, name='objectapp_tag_detail'),
+ url(r'^tags/(?P<tag>[- \w]+)/page/(?P<page>\d+)/$',
+ custom_tag_detail, name='objectapp_tag_detail_paginated'),
+ ) + test_urlpatterns
diff --git a/objectapp/tests/feeds.py b/objectapp/tests/feeds.py
new file mode 100644
index 0000000..3223fe9
--- /dev/null
+++ b/objectapp/tests/feeds.py
@@ -0,0 +1,343 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test cases for Objectapp's feeds"""
+from datetime import datetime
+
+from django.test import TestCase
+from django.conf import settings
+from django.contrib import comments
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.utils.translation import ugettext as _
+from django.utils.feedgenerator import Atom1Feed
+from django.utils.feedgenerator import DefaultFeed
+from django.core.exceptions import ObjectDoesNotExist
+from django.contrib.contenttypes.models import ContentType
+
+from tagging.models import Tag
+
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.managers import PUBLISHED
+from objectapp import feeds
+from objectapp.feeds import GbobjectFeed
+from objectapp.feeds import LatestGbobjects
+from objectapp.feeds import ObjecttypeGbobjects
+from objectapp.feeds import AuthorGbobjects
+from objectapp.feeds import TagGbobjects
+from objectapp.feeds import SearchGbobjects
+from objectapp.feeds import GbobjectDiscussions
+from objectapp.feeds import GbobjectComments
+from objectapp.feeds import GbobjectPingbacks
+from objectapp.feeds import GbobjectTrackbacks
+
+
+class ObjectappFeedsTestCase(TestCase):
+ """Test cases for the Feed classes provided"""
+ urls = 'objectapp.tests.urls'
+
+ def setUp(self):
+ self.site = Site.objects.get_current()
+ self.author = User.objects.create(username='admin',
+ email='admin@example.com')
+ self.Objecttype = Objecttype.objects.create(title='Tests', slug='tests')
+ self.gbobject_ct_id = ContentType.objects.get_for_model(Gbobject).pk
+
+ def create_published_gbobject(self):
+ params = {'title': 'My test gbobject',
+ 'content': 'My test content with image '
+ '<img src="/image.jpg" />',
+ 'slug': 'my-test-gbobject',
+ 'tags': 'tests',
+ 'creation_date': datetime(2010, 1, 1),
+ 'status': PUBLISHED}
+ gbobject = Gbobject.objects.create(**params)
+ gbobject.sites.add(self.site)
+ gbobject.objecttypes.add(self.Objecttype)
+ gbobject.authors.add(self.author)
+ return gbobject
+
+ def create_discussions(self, gbobject):
+ comment = comments.get_model().objects.create(comment='My Comment',
+ user=self.author,
+ content_object=gbobject,
+ site=self.site)
+ pingback = comments.get_model().objects.create(comment='My Pingback',
+ user=self.author,
+ content_object=gbobject,
+ site=self.site)
+ pingback.flags.create(user=self.author, flag='pingback')
+ trackback = comments.get_model().objects.create(comment='My Trackback',
+ user=self.author,
+ content_object=gbobject,
+ site=self.site)
+ trackback.flags.create(user=self.author, flag='trackback')
+ return [comment, pingback, trackback]
+
+ def test_gbobject_feed(self):
+ original_feeds_format = feeds.FEEDS_FORMAT
+ feeds.FEEDS_FORMAT = ''
+ gbobject = self.create_published_gbobject()
+ feed = GbobjectFeed()
+ self.assertEquals(feed.item_pubdate(gbobject), gbobject.creation_date)
+ self.assertEquals(feed.item_objecttypes(gbobject), [self.Objecttype.title])
+ self.assertEquals(feed.item_author_name(gbobject), self.author.username)
+ self.assertEquals(feed.item_author_email(gbobject), self.author.email)
+ self.assertEquals(
+ feed.item_author_link(gbobject),
+ 'http://example.com/authors/%s/' % self.author.username)
+ # Test a NoReverseMatch for item_author_link
+ self.author.username = '[]'
+ self.author.save()
+ feed.item_author_name(gbobject)
+ self.assertEquals(feed.item_author_link(gbobject), 'http://example.com')
+ feeds.FEEDS_FORMAT = original_feeds_format
+
+ def test_gbobject_feed_enclosure(self):
+ original_feeds_format = feeds.FEEDS_FORMAT
+ feeds.FEEDS_FORMAT = ''
+ gbobject = self.create_published_gbobject()
+ feed = GbobjectFeed()
+ self.assertEquals(
+ feed.item_enclosure_url(gbobject), 'http://example.com/image.jpg')
+ gbobject.content = 'My test content with image <img src="image.jpg" />',
+ gbobject.save()
+ self.assertEquals(
+ feed.item_enclosure_url(gbobject), 'http://example.com/image.jpg')
+ gbobject.content = 'My test content with image ' \
+ '<img src="http://test.com/image.jpg" />'
+ gbobject.save()
+ self.assertEquals(
+ feed.item_enclosure_url(gbobject), 'http://test.com/image.jpg')
+ gbobject.image = 'image_field.jpg'
+ gbobject.save()
+ self.assertEquals(feed.item_enclosure_url(gbobject),
+ '%simage_field.jpg' % settings.MEDIA_URL)
+ self.assertEquals(feed.item_enclosure_length(gbobject), '100000')
+ self.assertEquals(feed.item_enclosure_mime_type(gbobject), 'image/jpeg')
+ feeds.FEEDS_FORMAT = original_feeds_format
+
+ def test_latest_gbobjects(self):
+ self.create_published_gbobject()
+ feed = LatestGbobjects()
+ self.assertEquals(feed.link(), '/')
+ self.assertEquals(len(feed.items()), 1)
+ self.assertEquals(feed.title(),
+ 'example.com - %s' % _('Latest gbobjects'))
+ self.assertEquals(
+ feed.description(),
+ _('The latest gbobjects for the site %s') % 'example.com')
+
+ def test_Objecttype_gbobjects(self):
+ self.create_published_gbobject()
+ feed = ObjecttypeGbobjects()
+ self.assertEquals(feed.get_object('request', '/tests/'), self.Objecttype)
+ self.assertEquals(len(feed.items(self.Objecttype)), 1)
+ self.assertEquals(feed.link(self.Objecttype), '/objecttypes/tests/')
+ self.assertEquals(
+ feed.title(self.Objecttype),
+ _('Gbobjects for the Objecttype %s') % self.Objecttype.title)
+ self.assertEquals(
+ feed.description(self.Objecttype),
+ _('The latest gbobjects for the Objecttype %s') % self.Objecttype.title)
+
+ def test_author_gbobjects(self):
+ self.create_published_gbobject()
+ feed = AuthorGbobjects()
+ self.assertEquals(feed.get_object('request', 'admin'), self.author)
+ self.assertEquals(len(feed.items(self.author)), 1)
+ self.assertEquals(feed.link(self.author), '/authors/admin/')
+ self.assertEquals(feed.title(self.author),
+ _('Gbobjects for author %s') % self.author.username)
+ self.assertEquals(feed.description(self.author),
+ _('The latest gbobjects by %s') % self.author.username)
+
+ def test_tag_gbobjects(self):
+ self.create_published_gbobject()
+ feed = TagGbobjects()
+ tag = Tag(name='tests')
+ self.assertEquals(feed.get_object('request', 'tests').name, 'tests')
+ self.assertEquals(len(feed.items('tests')), 1)
+ self.assertEquals(feed.link(tag), '/tags/tests/')
+ self.assertEquals(feed.title(tag),
+ _('Gbobjects for the tag %s') % tag.name)
+ self.assertEquals(feed.description(tag),
+ _('The latest gbobjects for the tag %s') % tag.name)
+
+ def test_search_gbobjects(self):
+ class FakeRequest:
+ def __init__(self, val):
+ self.GET = {'pattern': val}
+ self.create_published_gbobject()
+ feed = SearchGbobjects()
+ self.assertRaises(ObjectDoesNotExist,
+ feed.get_object, FakeRequest('te'))
+ self.assertEquals(feed.get_object(FakeRequest('test')), 'test')
+ self.assertEquals(len(feed.items('test')), 1)
+ self.assertEquals(feed.link('test'), '/search/?pattern=test')
+ self.assertEquals(feed.title('test'),
+ _("Results of the search for '%s'") % 'test')
+ self.assertEquals(
+ feed.description('test'),
+ _("The gbobjects containing the pattern '%s'") % 'test')
+
+ def test_gbobject_discussions(self):
+ gbobject = self.create_published_gbobject()
+ comments = self.create_discussions(gbobject)
+ feed = GbobjectDiscussions()
+ self.assertEquals(feed.get_object(
+ 'request', 2010, 1, 1, gbobject.slug), gbobject)
+ self.assertEquals(feed.link(gbobject), '/2010/01/01/my-test-gbobject/')
+ self.assertEquals(len(feed.items(gbobject)), 3)
+ self.assertEquals(feed.item_pubdate(comments[0]),
+ comments[0].submit_date)
+ self.assertEquals(feed.item_link(comments[0]),
+ '/comments/cr/%i/1/#c1' % self.gbobject_ct_id)
+ self.assertEquals(feed.item_author_name(comments[0]), 'admin')
+ self.assertEquals(feed.item_author_email(comments[0]),
+ 'admin@example.com')
+ self.assertEquals(feed.item_author_link(comments[0]), '')
+ self.assertEquals(feed.title(gbobject),
+ _('Discussions on %s') % gbobject.title)
+ self.assertEquals(
+ feed.description(gbobject),
+ _('The latest discussions for the gbobject %s') % gbobject.title)
+
+ def test_gbobject_comments(self):
+ gbobject = self.create_published_gbobject()
+ comments = self.create_discussions(gbobject)
+ feed = GbobjectComments()
+ self.assertEquals(list(feed.items(gbobject)), [comments[0]])
+ self.assertEquals(feed.item_link(comments[0]),
+ '/comments/cr/%i/1/#comment_1' % self.gbobject_ct_id)
+ self.assertEquals(feed.title(gbobject),
+ _('Comments on %s') % gbobject.title)
+ self.assertEquals(
+ feed.description(gbobject),
+ _('The latest comments for the gbobject %s') % gbobject.title)
+ self.assertEquals(
+ feed.item_enclosure_url(comments[0]),
+ 'http://www.gravatar.com/avatar/e64c7d89f26b'
+ 'd1972efa854d13d7dd61.jpg?s=80&amp;r=g')
+ self.assertEquals(feed.item_enclosure_length(gbobject), '100000')
+ self.assertEquals(feed.item_enclosure_mime_type(gbobject), 'image/jpeg')
+
+ def test_gbobject_pingbacks(self):
+ gbobject = self.create_published_gbobject()
+ comments = self.create_discussions(gbobject)
+ feed = GbobjectPingbacks()
+ self.assertEquals(list(feed.items(gbobject)), [comments[1]])
+ self.assertEquals(feed.item_link(comments[1]),
+ '/comments/cr/%i/1/#pingback_2' % self.gbobject_ct_id)
+ self.assertEquals(feed.title(gbobject),
+ _('Pingbacks on %s') % gbobject.title)
+ self.assertEquals(
+ feed.description(gbobject),
+ _('The latest pingbacks for the gbobject %s') % gbobject.title)
+
+ def test_gbobject_trackbacks(self):
+ gbobject = self.create_published_gbobject()
+ comments = self.create_discussions(gbobject)
+ feed = GbobjectTrackbacks()
+ self.assertEquals(list(feed.items(gbobject)), [comments[2]])
+ self.assertEquals(feed.item_link(comments[2]),
+ '/comments/cr/%i/1/#trackback_3' % self.gbobject_ct_id)
+ self.assertEquals(feed.title(gbobject),
+ _('Trackbacks on %s') % gbobject.title)
+ self.assertEquals(
+ feed.description(gbobject),
+ _('The latest trackbacks for the gbobject %s') % gbobject.title)
+
+ def test_gbobject_feed_no_authors(self):
+ original_feeds_format = feeds.FEEDS_FORMAT
+ feeds.FEEDS_FORMAT = ''
+ gbobject = self.create_published_gbobject()
+ gbobject.authors.clear()
+ feed = GbobjectFeed()
+ self.assertEquals(feed.item_author_name(gbobject), None)
+ feeds.FEEDS_FORMAT = original_feeds_format
+
+ def test_gbobject_feed_rss_or_atom(self):
+ original_feeds_format = feeds.FEEDS_FORMAT
+ feeds.FEEDS_FORMAT = ''
+ feed = LatestGbobjects()
+ self.assertEquals(feed.feed_type, DefaultFeed)
+ feeds.FEEDS_FORMAT = 'atom'
+ feed = LatestGbobjects()
+ self.assertEquals(feed.feed_type, Atom1Feed)
+ self.assertEquals(feed.subtitle, feed.description)
+ feeds.FEEDS_FORMAT = original_feeds_format
+
+ def test_discussion_feed_with_same_slugs(self):
+ """
+ https://github.com/Fantomas42/django-blog-objectapp/issues/104
+
+ OK, Here I will reproduce the original case: getting a discussion
+ type feed, with a same slug.
+
+ The correction of this case, will need some changes in the
+ get_object method.
+ """
+ gbobject = self.create_published_gbobject()
+
+ feed = GbobjectDiscussions()
+ self.assertEquals(feed.get_object(
+ 'request', 2010, 1, 1, gbobject.slug), gbobject)
+
+ params = {'title': 'My test gbobject, part II',
+ 'content': 'My content ',
+ 'slug': 'my-test-gbobject',
+ 'tags': 'tests',
+ 'creation_date': datetime(2010, 2, 1),
+ 'status': PUBLISHED}
+ gbobject_same_slug = Gbobject.objects.create(**params)
+ gbobject_same_slug.sites.add(self.site)
+ gbobject_same_slug.authors.add(self.author)
+
+ self.assertEquals(feed.get_object(
+ 'request', 2010, 2, 1, gbobject_same_slug.slug), gbobject_same_slug)
diff --git a/objectapp/tests/gbobject.py b/objectapp/tests/gbobject.py
new file mode 100644
index 0000000..c5746bd
--- /dev/null
+++ b/objectapp/tests/gbobject.py
@@ -0,0 +1,331 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test cases for Objectapp's Gbobject"""
+from __future__ import with_statement
+import warnings
+from datetime import datetime
+from datetime import timedelta
+
+from django.test import TestCase
+from django.conf import settings
+from django.contrib import comments
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import reverse
+from django.contrib.comments.models import CommentFlag
+
+from objectapp import models
+from objectapp.models import Gbobject
+from objectapp.managers import PUBLISHED
+from objectapp.models import get_base_model
+from objectapp.models import GbobjectAbstractClass
+from objectapp import models as models_settings
+from objectapp import url_shortener as shortener_settings
+
+
+class GbobjectTestCase(TestCase):
+
+ def setUp(self):
+ params = {'title': 'My gbobject',
+ 'content': 'My content',
+ 'slug': 'my-gbobject'}
+ self.gbobject = Gbobject.objects.create(**params)
+
+ def test_discussions(self):
+ site = Site.objects.get_current()
+ self.assertEquals(self.gbobject.discussions.count(), 0)
+ self.assertEquals(self.gbobject.comments.count(), 0)
+ self.assertEquals(self.gbobject.pingbacks.count(), 0)
+ self.assertEquals(self.gbobject.trackbacks.count(), 0)
+
+ comments.get_model().objects.create(comment='My Comment 1',
+ content_object=self.gbobject,
+ site=site)
+ self.assertEquals(self.gbobject.discussions.count(), 1)
+ self.assertEquals(self.gbobject.comments.count(), 1)
+ self.assertEquals(self.gbobject.pingbacks.count(), 0)
+ self.assertEquals(self.gbobject.trackbacks.count(), 0)
+
+ comments.get_model().objects.create(comment='My Comment 2',
+ content_object=self.gbobject,
+ site=site, is_public=False)
+ self.assertEquals(self.gbobject.discussions.count(), 1)
+ self.assertEquals(self.gbobject.comments.count(), 1)
+ self.assertEquals(self.gbobject.pingbacks.count(), 0)
+ self.assertEquals(self.gbobject.trackbacks.count(), 0)
+
+ author = User.objects.create_user(username='webmaster',
+ email='webmaster@example.com')
+
+ comment = comments.get_model().objects.create(
+ comment='My Comment 3',
+ content_object=self.gbobject,
+ site=Site.objects.create(domain='http://toto.com',
+ name='Toto.com'))
+ comment.flags.create(user=author, flag=CommentFlag.MODERATOR_APPROVAL)
+ self.assertEquals(self.gbobject.discussions.count(), 2)
+ self.assertEquals(self.gbobject.comments.count(), 2)
+ self.assertEquals(self.gbobject.pingbacks.count(), 0)
+ self.assertEquals(self.gbobject.trackbacks.count(), 0)
+
+ comment = comments.get_model().objects.create(
+ comment='My Pingback 1', content_object=self.gbobject, site=site)
+ comment.flags.create(user=author, flag='pingback')
+ self.assertEquals(self.gbobject.discussions.count(), 3)
+ self.assertEquals(self.gbobject.comments.count(), 2)
+ self.assertEquals(self.gbobject.pingbacks.count(), 1)
+ self.assertEquals(self.gbobject.trackbacks.count(), 0)
+
+ comment = comments.get_model().objects.create(
+ comment='My Trackback 1', content_object=self.gbobject, site=site)
+ comment.flags.create(user=author, flag='trackback')
+ self.assertEquals(self.gbobject.discussions.count(), 4)
+ self.assertEquals(self.gbobject.comments.count(), 2)
+ self.assertEquals(self.gbobject.pingbacks.count(), 1)
+ self.assertEquals(self.gbobject.trackbacks.count(), 1)
+
+ def test_str(self):
+ self.assertEquals(str(self.gbobject), 'My gbobject: draft')
+
+ def test_word_count(self):
+ self.assertEquals(self.gbobject.word_count, 2)
+
+ def test_comments_are_open(self):
+ original_auto_close = models.AUTO_CLOSE_COMMENTS_AFTER
+ models.AUTO_CLOSE_COMMENTS_AFTER = None
+ self.assertEquals(self.gbobject.comments_are_open, True)
+ models.AUTO_CLOSE_COMMENTS_AFTER = 5
+ self.gbobject.start_publication = datetime.now() - timedelta(days=7)
+ self.gbobject.save()
+ self.assertEquals(self.gbobject.comments_are_open, False)
+
+ models.AUTO_CLOSE_COMMENTS_AFTER = original_auto_close
+
+ def test_is_actual(self):
+ self.assertTrue(self.gbobject.is_actual)
+ self.gbobject.start_publication = datetime(2020, 3, 15)
+ self.assertFalse(self.gbobject.is_actual)
+ self.gbobject.start_publication = datetime.now()
+ self.assertTrue(self.gbobject.is_actual)
+ self.gbobject.end_publication = datetime(2000, 3, 15)
+ self.assertFalse(self.gbobject.is_actual)
+
+ def test_is_visible(self):
+ self.assertFalse(self.gbobject.is_visible)
+ self.gbobject.status = PUBLISHED
+ self.assertTrue(self.gbobject.is_visible)
+ self.gbobject.start_publication = datetime(2020, 3, 15)
+ self.assertFalse(self.gbobject.is_visible)
+
+ def test_short_url(self):
+ original_shortener = shortener_settings.URL_SHORTENER_BACKEND
+ shortener_settings.URL_SHORTENER_BACKEND = 'objectapp.url_shortener.'\
+ 'backends.default'
+ self.assertEquals(self.gbobject.short_url,
+ 'http://example.com' +
+ reverse('objectapp_gbobject_shortlink',
+ args=[self.gbobject.pk]))
+ shortener_settings.URL_SHORTENER_BACKEND = original_shortener
+
+ def test_previous_gbobject(self):
+ site = Site.objects.get_current()
+ self.assertFalse(self.gbobject.previous_gbobject)
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content',
+ 'slug': 'my-second-gbobject',
+ 'creation_date': datetime(2000, 1, 1),
+ 'status': PUBLISHED}
+ self.second_gbobject = Gbobject.objects.create(**params)
+ self.second_gbobject.sites.add(site)
+ self.assertEquals(self.gbobject.previous_gbobject, self.second_gbobject)
+ params = {'title': 'My third gbobject',
+ 'content': 'My third content',
+ 'slug': 'my-third-gbobject',
+ 'creation_date': datetime(2001, 1, 1),
+ 'status': PUBLISHED}
+ self.third_gbobject = Gbobject.objects.create(**params)
+ self.third_gbobject.sites.add(site)
+ self.assertEquals(self.gbobject.previous_gbobject, self.third_gbobject)
+ self.assertEquals(self.third_gbobject.previous_gbobject, self.second_gbobject)
+
+ def test_next_gbobject(self):
+ site = Site.objects.get_current()
+ self.assertFalse(self.gbobject.next_gbobject)
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content',
+ 'slug': 'my-second-gbobject',
+ 'creation_date': datetime(2100, 1, 1),
+ 'status': PUBLISHED}
+ self.second_gbobject = Gbobject.objects.create(**params)
+ self.second_gbobject.sites.add(site)
+ self.assertEquals(self.gbobject.next_gbobject, self.second_gbobject)
+ params = {'title': 'My third gbobject',
+ 'content': 'My third content',
+ 'slug': 'my-third-gbobject',
+ 'creation_date': datetime(2050, 1, 1),
+ 'status': PUBLISHED}
+ self.third_gbobject = Gbobject.objects.create(**params)
+ self.third_gbobject.sites.add(site)
+ self.assertEquals(self.gbobject.next_gbobject, self.third_gbobject)
+ self.assertEquals(self.third_gbobject.next_gbobject, self.second_gbobject)
+
+ def test_related_published(self):
+ site = Site.objects.get_current()
+ self.assertFalse(self.gbobject.related_published)
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content',
+ 'slug': 'my-second-gbobject',
+ 'status': PUBLISHED}
+ self.second_gbobject = Gbobject.objects.create(**params)
+ self.second_gbobject.related.add(self.gbobject)
+ self.assertEquals(len(self.gbobject.related_published), 0)
+
+ self.second_gbobject.sites.add(site)
+ self.assertEquals(len(self.gbobject.related_published), 1)
+ self.assertEquals(len(self.second_gbobject.related_published), 0)
+
+ self.gbobject.status = PUBLISHED
+ self.gbobject.save()
+ self.gbobject.sites.add(site)
+ self.assertEquals(len(self.gbobject.related_published), 1)
+ self.assertEquals(len(self.second_gbobject.related_published), 1)
+
+
+class GbobjectHtmlContentTestCase(TestCase):
+
+ def setUp(self):
+ params = {'title': 'My gbobject',
+ 'content': 'My content',
+ 'slug': 'my-gbobject'}
+ self.gbobject = Gbobject(**params)
+ self.original_debug = settings.DEBUG
+ self.original_rendering = models_settings.MARKUP_LANGUAGE
+ settings.DEBUG = False
+
+ def tearDown(self):
+ settings.DEBUG = self.original_debug
+ models_settings.MARKUP_LANGUAGE = self.original_rendering
+
+ def test_html_content_default(self):
+ models_settings.MARKUP_LANGUAGE = None
+ self.assertEquals(self.gbobject.html_content, '<p>My content</p>')
+
+ self.gbobject.content = 'Hello world !\n' \
+ ' this is my content'
+ self.assertEquals(self.gbobject.html_content,
+ '<p>Hello world !<br /> this is my content</p>')
+
+ def test_html_content_textitle(self):
+ models_settings.MARKUP_LANGUAGE = 'textile'
+ self.gbobject.content = 'Hello world !\n\n' \
+ 'this is my content :\n\n' \
+ '* Item 1\n* Item 2'
+ html_content = self.gbobject.html_content
+ try:
+ self.assertEquals(html_content,
+ '\t<p>Hello world !</p>\n\n\t' \
+ '<p>this is my content :</p>\n\n\t' \
+ '<ul>\n\t\t<li>Item 1</li>\n\t\t' \
+ '<li>Item 2</li>\n\t</ul>')
+ except AssertionError:
+ self.assertEquals(html_content, self.gbobject.content)
+
+ def test_html_content_markdown(self):
+ models_settings.MARKUP_LANGUAGE = 'markdown'
+ self.gbobject.content = 'Hello world !\n\n' \
+ 'this is my content :\n\n' \
+ '* Item 1\n* Item 2'
+ html_content = self.gbobject.html_content
+ try:
+ self.assertEquals(html_content,
+ '<p>Hello world !</p>\n' \
+ '<p>this is my content :</p>'\
+ '\n<ul>\n<li>Item 1</li>\n' \
+ '<li>Item 2</li>\n</ul>')
+ except AssertionError:
+ self.assertEquals(html_content, self.gbobject.content)
+
+ def test_html_content_restructuredtext(self):
+ models_settings.MARKUP_LANGUAGE = 'restructuredtext'
+ self.gbobject.content = 'Hello world !\n\n' \
+ 'this is my content :\n\n' \
+ '* Item 1\n* Item 2'
+ html_content = self.gbobject.html_content
+ try:
+ self.assertEquals(html_content,
+ '<p>Hello world !</p>\n' \
+ '<p>this is my content :</p>'\
+ '\n<ul class="simple">\n<li>Item 1</li>\n' \
+ '<li>Item 2</li>\n</ul>\n')
+ except AssertionError:
+ self.assertEquals(html_content, self.gbobject.content)
+
+
+class GbobjectGetBaseModelTestCase(TestCase):
+
+ def setUp(self):
+ self.original_gbobject_base_model = models_settings.GBOBJECT_BASE_MODEL
+
+ def tearDown(self):
+ models_settings.GBOBJECT_BASE_MODEL = self.original_gbobject_base_model
+
+ def test_get_base_model(self):
+ models_settings.GBOBJECT_BASE_MODEL = ''
+ self.assertEquals(get_base_model(), GbobjectAbstractClass)
+
+ models_settings.GBOBJECT_BASE_MODEL = 'mymodule.myclass'
+ try:
+ with warnings.catch_warnings(record=True) as w:
+ self.assertEquals(get_base_model(), GbobjectAbstractClass)
+ self.assertTrue(issubclass(w[-1].Objecttype, RuntimeWarning))
+ except AttributeError:
+ # Fail under Python2.5, because of'warnings.catch_warnings'
+ pass
+
+ models_settings.GBOBJECT_BASE_MODEL = 'objectapp.models.GbobjectAbstractClass'
+ self.assertEquals(get_base_model(), GbobjectAbstractClass)
diff --git a/objectapp/tests/managers.py b/objectapp/tests/managers.py
new file mode 100644
index 0000000..abc6aa5
--- /dev/null
+++ b/objectapp/tests/managers.py
@@ -0,0 +1,296 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test cases for Objectapp's managers"""
+from datetime import datetime
+
+from django.test import TestCase
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+
+from tagging.models import Tag
+
+from objectapp.models import Gbobject
+from objectapp.models import Author
+from objectapp.models import Objecttype
+from objectapp.managers import PUBLISHED
+from objectapp.managers import tags_published
+from objectapp.managers import gbobjects_published
+
+
+class ManagersTestCase(TestCase):
+
+ def setUp(self):
+ self.sites = [
+ Site.objects.get_current(),
+ Site.objects.create(domain='http://domain.com',
+ name='Domain.com')]
+ self.authors = [
+ User.objects.create_user(username='webmaster',
+ email='webmaster@example.com'),
+ User.objects.create_user(username='contributor',
+ email='contributor@example.com')]
+ self.objecttypes = [
+ Objecttype.objects.create(title='Objecttype 1',
+ slug='Objecttype-1'),
+ Objecttype.objects.create(title='Objecttype 2',
+ slug='Objecttype-2')]
+
+ params = {'title': 'My gbobject 1', 'content': 'My content 1',
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-1',
+ 'status': PUBLISHED}
+ self.gbobject_1 = Gbobject.objects.create(**params)
+ self.gbobject_1.authors.add(self.authors[0])
+ self.gbobject_1.objecttypes.add(*self.objecttypes)
+ self.gbobject_1.sites.add(*self.sites)
+
+ params = {'title': 'My gbobject 2', 'content': 'My content 2',
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-2'}
+ self.gbobject_2 = Gbobject.objects.create(**params)
+ self.gbobject_2.authors.add(*self.authors)
+ self.gbobject_2.objecttypes.add(self.objecttypes[0])
+ self.gbobject_2.sites.add(self.sites[0])
+
+ def test_tags_published(self):
+ self.assertEquals(tags_published().count(), Tag.objects.count())
+ Tag.objects.create(name='out')
+ self.assertNotEquals(tags_published().count(), Tag.objects.count())
+
+ def test_author_published_manager_get_query_set(self):
+ self.assertEquals(Author.published.count(), 1)
+ self.gbobject_2.status = PUBLISHED
+ self.gbobject_2.save()
+ self.assertEquals(Author.published.count(), 2)
+ self.gbobject_2.sites.remove(self.sites[0])
+ self.gbobject_2.sites.add(self.sites[1])
+ self.assertEquals(Author.published.count(), 1)
+
+ def test_gbobjects_published(self):
+ self.assertEquals(gbobjects_published(Gbobject.objects.all()).count(), 1)
+ self.gbobject_2.status = PUBLISHED
+ self.gbobject_2.save()
+ self.assertEquals(gbobjects_published(Gbobject.objects.all()).count(), 2)
+ self.gbobject_1.sites.clear()
+ self.assertEquals(gbobjects_published(Gbobject.objects.all()).count(), 1)
+ self.gbobject_1.sites.add(*self.sites)
+ self.gbobject_1.start_publication = datetime(2020, 1, 1)
+ self.gbobject_1.save()
+ self.assertEquals(gbobjects_published(Gbobject.objects.all()).count(), 1)
+ self.gbobject_1.start_publication = datetime(2000, 1, 1)
+ self.gbobject_1.save()
+ self.assertEquals(gbobjects_published(Gbobject.objects.all()).count(), 2)
+ self.gbobject_1.end_publication = datetime(2000, 1, 1)
+ self.gbobject_1.save()
+ self.assertEquals(gbobjects_published(Gbobject.objects.all()).count(), 1)
+ self.gbobject_1.end_publication = datetime(2020, 1, 1)
+ self.gbobject_1.save()
+ self.assertEquals(gbobjects_published(Gbobject.objects.all()).count(), 2)
+
+ def test_gbobject_published_manager_get_query_set(self):
+ self.assertEquals(Gbobject.published.count(), 1)
+ self.gbobject_2.status = PUBLISHED
+ self.gbobject_2.save()
+ self.assertEquals(Gbobject.published.count(), 2)
+ self.gbobject_1.sites.clear()
+ self.assertEquals(Gbobject.published.count(), 1)
+ self.gbobject_1.sites.add(*self.sites)
+ self.gbobject_1.start_publication = datetime(2020, 1, 1)
+ self.gbobject_1.save()
+ self.assertEquals(Gbobject.published.count(), 1)
+ self.gbobject_1.start_publication = datetime(2000, 1, 1)
+ self.gbobject_1.save()
+ self.assertEquals(Gbobject.published.count(), 2)
+ self.gbobject_1.end_publication = datetime(2000, 1, 1)
+ self.gbobject_1.save()
+ self.assertEquals(Gbobject.published.count(), 1)
+ self.gbobject_1.end_publication = datetime(2020, 1, 1)
+ self.gbobject_1.save()
+ self.assertEquals(Gbobject.published.count(), 2)
+
+ def test_gbobject_published_manager_on_site(self):
+ self.assertEquals(Gbobject.published.on_site().count(), 2)
+ self.gbobject_2.sites.clear()
+ self.gbobject_2.sites.add(self.sites[1])
+ self.assertEquals(Gbobject.published.on_site().count(), 1)
+ self.gbobject_1.sites.clear()
+ self.assertEquals(Gbobject.published.on_site().count(), 0)
+
+ def test_gbobject_published_manager_basic_search(self):
+ self.assertEquals(Gbobject.published.basic_search('My ').count(), 1)
+ self.gbobject_2.status = PUBLISHED
+ self.gbobject_2.save()
+ self.assertEquals(Gbobject.published.basic_search('My ').count(), 2)
+ self.assertEquals(Gbobject.published.basic_search('1').count(), 1)
+ self.assertEquals(Gbobject.published.basic_search('content 1').count(), 2)
+
+ def test_gbobject_published_manager_advanced_search(self):
+ Objecttype = Objecttype.objects.create(
+ title='SimpleObjecttype', slug='simple')
+ self.gbobject_2.objecttypes.add(Objecttype)
+ self.gbobject_2.tags = self.gbobject_2.tags + ', custom'
+ self.gbobject_2.status = PUBLISHED
+ self.gbobject_2.save()
+ self.assertEquals(
+ Gbobject.published.advanced_search('content').count(), 2)
+ search = Gbobject.published.advanced_search('content 1')
+ self.assertEquals(search.count(), 1)
+ self.assertEquals(search.all()[0], self.gbobject_1)
+ self.assertEquals(
+ Gbobject.published.advanced_search('content 1 or 2').count(), 2)
+ self.assertEquals(
+ Gbobject.published.advanced_search('content 1 and 2').count(), 0)
+ self.assertEquals(
+ Gbobject.published.advanced_search('content 1 2').count(), 0)
+ self.assertEquals(
+ Gbobject.published.advanced_search('"My content" 1 or 2').count(), 2)
+ self.assertEquals(
+ Gbobject.published.advanced_search('-"My content" 2').count(), 0)
+ search = Gbobject.published.advanced_search('content -1')
+ self.assertEquals(search.count(), 1)
+ self.assertEquals(search.all()[0], self.gbobject_2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content Objecttype:SimpleObjecttype').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content Objecttype:simple').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content Objecttype:"Objecttype 1"').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content Objecttype:"Objecttype-1"').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content Objecttype:"Objecttype-2"').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content tag:objectapp').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content tag:custom').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content author:webmaster').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content author:contributor').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content author:webmaster tag:objectapp').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content author:webmaster tag:custom').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content 1 or 2 author:webmaster').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'content 1 or 2 author:webmaster').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:webmaster content) my').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:webmaster) or (author:contributor)').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:webmaster) (author:contributor)').count(), 0)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:webmaster content) 1').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:webmaster content) or 2').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:contributor content) or 1').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:contributor content) or 2').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:webmaster or ("hello world")) and 2').count(), 1)
+
+ # Complex queries
+ self.assertEquals(Gbobject.published.advanced_search(
+ '(author:admin and "content 1") or author:webmaster').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'author:admin and ("content 1" or author:webmaster)').count(), 0)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'author:admin and "content 1" or author:webmaster').count(), 0)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '-(author:webmaster and "content 1")').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '-(-author:webmaster and "content 1")').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'Objecttype:"Objecttype -1" or author:"web master"').count(), 0)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'Objecttype:"Objecttype-1" or author:"webmaster"').count(), 2)
+
+ # Wildcards
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'author:webm*').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'author:*bmas*').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'author:*master').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'author:*master Objecttype:*ory-2').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'author:*master or Objecttype:cate*').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'Objecttype:*ate*').count(), 2)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'author:"webmast*"').count(), 0)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'tag:"objectapp*"').count(), 0)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'tag:*inni*').count(), 2)
+
+ def test_gbobject_published_manager_advanced_search_with_punctuation(self):
+ self.gbobject_2.content = 'How are you today ? Fine thank you ! OK.'
+ self.gbobject_2.status = PUBLISHED
+ self.gbobject_2.save()
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'today ?').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ 'today or ! or .').count(), 1)
+ self.assertEquals(Gbobject.published.advanced_search(
+ '"you today ?"').count(), 1)
+
+ def test_gbobject_published_manager_search(self):
+ self.gbobject_2.content = self.gbobject_2.content + ' * '
+ self.gbobject_2.status = PUBLISHED
+ self.gbobject_2.save()
+ # Be sure that basic_search does not return
+ # the same results of advanced_search
+ self.assertNotEquals(
+ Gbobject.published.basic_search('content 1').count(),
+ Gbobject.published.advanced_search('content 1').count())
+ # Now check the fallback with the '*' pattern
+ # which will fails advanced search
+ self.assertEquals(Gbobject.published.search('*').count(), 1)
diff --git a/objectapp/tests/metaweblog.py b/objectapp/tests/metaweblog.py
new file mode 100644
index 0000000..7ca0189
--- /dev/null
+++ b/objectapp/tests/metaweblog.py
@@ -0,0 +1,343 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test cases for Objectapp's MetaWeblog API"""
+from xmlrpclib import Binary
+from xmlrpclib import Fault
+from xmlrpclib import ServerProxy
+from datetime import datetime
+from tempfile import TemporaryFile
+
+from django.test import TestCase
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.core.files.storage import default_storage
+
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.managers import DRAFT
+from objectapp.managers import PUBLISHED
+from objectapp.settings import UPLOAD_TO
+from objectapp.xmlrpc.metaweblog import authenticate
+from objectapp.xmlrpc.metaweblog import post_structure
+from objectapp.tests.utils import TestTransport
+
+
+class MetaWeblogTestCase(TestCase):
+ """Test cases for MetaWeblog"""
+ urls = 'objectapp.tests.urls'
+
+ def setUp(self):
+ # Create data
+ self.webmaster = User.objects.create_superuser(
+ username='webmaster',
+ email='webmaster@example.com',
+ password='password')
+ self.contributor = User.objects.create_user(
+ username='contributor',
+ email='contributor@example.com',
+ password='password')
+ self.site = Site.objects.get_current()
+ self.objecttypes = [
+ Objecttype.objects.create(title='Objecttype 1',
+ slug='Objecttype-1'),
+ Objecttype.objects.create(title='Objecttype 2',
+ slug='Objecttype-2')]
+ params = {'title': 'My gbobject 1', 'content': 'My content 1',
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-1',
+ 'creation_date': datetime(2010, 1, 1),
+ 'status': PUBLISHED}
+ self.gbobject_1 = Gbobject.objects.create(**params)
+ self.gbobject_1.authors.add(self.webmaster)
+ self.gbobject_1.objecttypes.add(*self.objecttypes)
+ self.gbobject_1.sites.add(self.site)
+
+ params = {'title': 'My gbobject 2', 'content': 'My content 2',
+ 'creation_date': datetime(2010, 3, 15),
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-2'}
+ self.gbobject_2 = Gbobject.objects.create(**params)
+ self.gbobject_2.authors.add(self.webmaster)
+ self.gbobject_2.objecttypes.add(self.objecttypes[0])
+ self.gbobject_2.sites.add(self.site)
+ # Instanciating the server proxy
+ self.server = ServerProxy('http://localhost:8000/xmlrpc/',
+ transport=TestTransport())
+
+ def test_authenticate(self):
+ self.assertRaises(Fault, authenticate, 'badcontributor', 'badpassword')
+ self.assertRaises(Fault, authenticate, 'contributor', 'badpassword')
+ self.assertRaises(Fault, authenticate, 'contributor', 'password')
+ self.contributor.is_staff = True
+ self.contributor.save()
+ self.assertEquals(authenticate('contributor', 'password'),
+ self.contributor)
+ self.assertRaises(Fault, authenticate, 'contributor',
+ 'password', 'objectapp.change_gbobject')
+ self.assertEquals(authenticate('webmaster', 'password'),
+ self.webmaster)
+ self.assertEquals(authenticate('webmaster', 'password',
+ 'objectapp.change_gbobject'),
+ self.webmaster)
+
+ def test_get_users_blogs(self):
+ self.assertRaises(Fault, self.server.blogger.getUsersBlogs,
+ 'apikey', 'contributor', 'password')
+ self.assertEquals(self.server.blogger.getUsersBlogs(
+ 'apikey', 'webmaster', 'password'),
+ [{'url': 'http://example.com/',
+ 'blogid': 1,
+ 'blogName': 'example.com'}])
+
+ def test_get_user_info(self):
+ self.assertRaises(Fault, self.server.blogger.getUserInfo,
+ 'apikey', 'contributor', 'password')
+ self.webmaster.first_name = 'John'
+ self.webmaster.last_name = 'Doe'
+ self.webmaster.save()
+ self.assertEquals(self.server.blogger.getUserInfo(
+ 'apikey', 'webmaster', 'password'),
+ {'firstname': 'John', 'lastname': 'Doe',
+ 'url': 'http://example.com/authors/webmaster/',
+ 'userid': self.webmaster.pk,
+ 'nickname': 'webmaster',
+ 'email': 'webmaster@example.com'})
+
+ def test_get_authors(self):
+ self.assertRaises(Fault, self.server.wp.getAuthors,
+ 'apikey', 'contributor', 'password')
+ self.assertEquals(self.server.wp.getAuthors(
+ 'apikey', 'webmaster', 'password'), [
+ {'user_login': 'webmaster',
+ 'user_id': self.webmaster.pk,
+ 'user_email': 'webmaster@example.com',
+ 'display_name': 'webmaster'}])
+
+ def test_get_objecttypes(self):
+ self.assertRaises(Fault, self.server.metaWeblog.getObjecttypes,
+ 1, 'contributor', 'password')
+ self.assertEquals(
+ self.server.metaWeblog.getObjecttypes('apikey',
+ 'webmaster', 'password'),
+ [{'rssUrl': 'http://example.com/feeds/objecttypes/Objecttype-1/',
+ 'description': 'Objecttype 1',
+ 'htmlUrl': 'http://example.com/objecttypes/Objecttype-1/',
+ 'ObjecttypeId': 1, 'parentId': 0,
+ 'ObjecttypeName': 'Objecttype 1',
+ 'ObjecttypeDescription': ''},
+ {'rssUrl': 'http://example.com/feeds/objecttypes/Objecttype-2/',
+ 'description': 'Objecttype 2',
+ 'htmlUrl': 'http://example.com/objecttypes/Objecttype-2/',
+ 'ObjecttypeId': 2, 'parentId': 0,
+ 'ObjecttypeName': 'Objecttype 2',
+ 'ObjecttypeDescription': ''}])
+ self.objecttypes[1].parent = self.objecttypes[0]
+ self.objecttypes[1].description = 'Objecttype 2 description'
+ self.objecttypes[1].save()
+ self.assertEquals(
+ self.server.metaWeblog.getObjecttypes('apikey',
+ 'webmaster', 'password'),
+ [{'rssUrl': 'http://example.com/feeds/objecttypes/Objecttype-1/',
+ 'description': 'Objecttype 1',
+ 'htmlUrl': 'http://example.com/objecttypes/Objecttype-1/',
+ 'ObjecttypeId': 1, 'parentId': 0,
+ 'ObjecttypeName': 'Objecttype 1',
+ 'ObjecttypeDescription': ''},
+ {'rssUrl':
+ 'http://example.com/feeds/objecttypes/Objecttype-1/Objecttype-2/',
+ 'description': 'Objecttype 2',
+ 'htmlUrl':
+ 'http://example.com/objecttypes/Objecttype-1/Objecttype-2/',
+ 'ObjecttypeId': 2, 'parentId': 1,
+ 'ObjecttypeName': 'Objecttype 2',
+ 'ObjecttypeDescription': 'Objecttype 2 description'}])
+
+ def test_new_Objecttype(self):
+ Objecttype_struct = {'name': 'Objecttype 3', 'slug': 'Objecttype-3',
+ 'description': 'Objecttype 3 description',
+ 'parent_id': self.objecttypes[0].pk}
+ self.assertRaises(Fault, self.server.wp.newObjecttype,
+ 1, 'contributor', 'password', Objecttype_struct)
+ self.assertEquals(Objecttype.objects.count(), 2)
+ new_Objecttype_id = self.server.wp.newObjecttype(
+ 1, 'webmaster', 'password', Objecttype_struct)
+ self.assertEquals(Objecttype.objects.count(), 3)
+ Objecttype = Objecttype.objects.get(pk=new_Objecttype_id)
+ self.assertEquals(Objecttype.title, 'Objecttype 3')
+ self.assertEquals(Objecttype.description, 'Objecttype 3 description')
+ self.assertEquals(Objecttype.slug, 'Objecttype-3')
+ self.assertEquals(Objecttype.parent.pk, 1)
+
+ def test_get_recent_posts(self):
+ self.assertRaises(Fault, self.server.metaWeblog.getRecentPosts,
+ 1, 'contributor', 'password', 10)
+ self.assertEquals(len(self.server.metaWeblog.getRecentPosts(
+ 1, 'webmaster', 'password', 10)), 2)
+
+ def test_delete_post(self):
+ self.assertRaises(Fault, self.server.blogger.deletePost,
+ 'apikey', 1, 'contributor', 'password', 'publish')
+ self.assertEquals(Gbobject.objects.count(), 2)
+ self.assertTrue(
+ self.server.blogger.deletePost(
+ 'apikey', self.gbobject_1.pk, 'webmaster', 'password', 'publish'))
+ self.assertEquals(Gbobject.objects.count(), 1)
+
+ def test_get_post(self):
+ self.assertRaises(Fault, self.server.metaWeblog.getPost,
+ 1, 'contributor', 'password')
+ post = self.server.metaWeblog.getPost(
+ self.gbobject_1.pk, 'webmaster', 'password')
+ self.assertEquals(post['title'], self.gbobject_1.title)
+ self.assertEquals(post['description'], '<p>My content 1</p>')
+ self.assertEquals(post['objecttypes'], ['Objecttype 1', 'Objecttype 2'])
+ self.assertEquals(post['dateCreated'].value, '2010-01-01T00:00:00')
+ self.assertEquals(post['link'],
+ 'http://example.com/2010/01/01/my-gbobject-1/')
+ self.assertEquals(post['permaLink'],
+ 'http://example.com/2010/01/01/my-gbobject-1/')
+ self.assertEquals(post['postid'], self.gbobject_1.pk)
+ self.assertEquals(post['userid'], 'webmaster')
+ self.assertEquals(post['mt_excerpt'], '')
+ self.assertEquals(post['mt_allow_comments'], 1)
+ self.assertEquals(post['mt_allow_pings'], 1)
+ self.assertEquals(post['mt_keywords'], self.gbobject_1.tags)
+ self.assertEquals(post['wp_author'], 'webmaster')
+ self.assertEquals(post['wp_author_id'], self.webmaster.pk)
+ self.assertEquals(post['wp_author_display_name'], 'webmaster')
+ self.assertEquals(post['wp_password'], '')
+ self.assertEquals(post['wp_slug'], self.gbobject_1.slug)
+
+ def test_new_post(self):
+ post = post_structure(self.gbobject_2, self.site)
+ self.assertRaises(Fault, self.server.metaWeblog.newPost,
+ 1, 'contributor', 'password', post, 1)
+ self.assertEquals(Gbobject.objects.count(), 2)
+ self.assertEquals(Gbobject.published.count(), 1)
+ self.server.metaWeblog.newPost(
+ 1, 'webmaster', 'password', post, 1)
+ self.assertEquals(Gbobject.objects.count(), 3)
+ self.assertEquals(Gbobject.published.count(), 2)
+ del post['dateCreated']
+ post['wp_author_id'] = self.contributor.pk
+ self.server.metaWeblog.newPost(
+ 1, 'webmaster', 'password', post, 0)
+ self.assertEquals(Gbobject.objects.count(), 4)
+ self.assertEquals(Gbobject.published.count(), 2)
+
+ def test_edit_post(self):
+ post = post_structure(self.gbobject_2, self.site)
+ self.assertRaises(Fault, self.server.metaWeblog.editPost,
+ 1, 'contributor', 'password', post, 1)
+ new_post_id = self.server.metaWeblog.newPost(
+ 1, 'webmaster', 'password', post, 0)
+
+ gbobject = Gbobject.objects.get(pk=new_post_id)
+ self.assertEquals(gbobject.title, self.gbobject_2.title)
+ self.assertEquals(gbobject.content, self.gbobject_2.html_content)
+ self.assertEquals(gbobject.excerpt, self.gbobject_2.excerpt)
+ self.assertEquals(gbobject.slug, self.gbobject_2.slug)
+ self.assertEquals(gbobject.status, DRAFT)
+ self.assertEquals(gbobject.password, self.gbobject_2.password)
+ self.assertEquals(gbobject.comment_enabled, True)
+ self.assertEquals(gbobject.pingback_enabled, True)
+ self.assertEquals(gbobject.objecttypes.count(), 1)
+ self.assertEquals(gbobject.authors.count(), 1)
+ self.assertEquals(gbobject.authors.all()[0], self.webmaster)
+ self.assertEquals(gbobject.creation_date, self.gbobject_2.creation_date)
+
+ gbobject.title = 'Title edited'
+ gbobject.creation_date = datetime(2000, 1, 1)
+ post = post_structure(gbobject, self.site)
+ post['objecttypes'] = ''
+ post['description'] = 'Content edited'
+ post['mt_excerpt'] = 'Content edited'
+ post['wp_slug'] = 'slug-edited'
+ post['wp_password'] = 'password'
+ post['mt_allow_comments'] = 2
+ post['mt_allow_pings'] = 0
+
+ response = self.server.metaWeblog.editPost(
+ new_post_id, 'webmaster', 'password', post, 1)
+ self.assertEquals(response, True)
+ gbobject = Gbobject.objects.get(pk=new_post_id)
+ self.assertEquals(gbobject.title, post['title'])
+ self.assertEquals(gbobject.content, post['description'])
+ self.assertEquals(gbobject.excerpt, post['mt_excerpt'])
+ self.assertEquals(gbobject.slug, 'slug-edited')
+ self.assertEquals(gbobject.status, PUBLISHED)
+ self.assertEquals(gbobject.password, 'password')
+ self.assertEquals(gbobject.comment_enabled, False)
+ self.assertEquals(gbobject.pingback_enabled, False)
+ self.assertEquals(gbobject.objecttypes.count(), 0)
+ self.assertEquals(gbobject.creation_date, datetime(2000, 1, 1))
+
+ del post['dateCreated']
+ post['wp_author_id'] = self.contributor.pk
+
+ response = self.server.metaWeblog.editPost(
+ new_post_id, 'webmaster', 'password', post, 1)
+ gbobject = Gbobject.objects.get(pk=new_post_id)
+ self.assertEquals(gbobject.authors.count(), 1)
+ self.assertEquals(gbobject.authors.all()[0], self.contributor)
+ self.assertEquals(gbobject.creation_date, datetime(2000, 1, 1))
+
+ def test_new_media_object(self):
+ file_ = TemporaryFile()
+ file_.write('My test content')
+ file_.seek(0)
+ media = {'name': 'objectapp_test_file.txt',
+ 'type': 'text/plain',
+ 'bits': Binary(file_.read())}
+ file_.close()
+
+ self.assertRaises(Fault, self.server.metaWeblog.newMediaObject,
+ 1, 'contributor', 'password', media)
+ new_media = self.server.metaWeblog.newMediaObject(
+ 1, 'webmaster', 'password', media)
+ self.assertTrue('/objectapp_test_file' in new_media['url'])
+ default_storage.delete('/'.join([
+ UPLOAD_TO, new_media['url'].split('/')[-1]]))
diff --git a/objectapp/tests/moderator.py b/objectapp/tests/moderator.py
new file mode 100644
index 0000000..57dac52
--- /dev/null
+++ b/objectapp/tests/moderator.py
@@ -0,0 +1,186 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test cases for Objectapp's moderator"""
+from django.core import mail
+from django.test import TestCase
+from django.contrib import comments
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.contrib.contenttypes.models import ContentType
+
+from objectapp.models import Gbobject
+from objectapp.managers import PUBLISHED
+from objectapp.moderator import GbobjectCommentModerator
+
+
+class GbobjectCommentModeratorTestCase(TestCase):
+ """Test cases for the moderator"""
+
+ def setUp(self):
+ self.site = Site.objects.get_current()
+ self.author = User.objects.create(username='admin',
+ email='admin@example.com')
+ self.gbobject_ct_id = ContentType.objects.get_for_model(Gbobject).pk
+
+ params = {'title': 'My test gbobject',
+ 'content': 'My test gbobject',
+ 'slug': 'my-test-gbobject',
+ 'status': PUBLISHED}
+ self.gbobject = Gbobject.objects.create(**params)
+ self.gbobject.sites.add(self.site)
+ self.gbobject.authors.add(self.author)
+
+ def test_email(self):
+ comment = comments.get_model().objects.create(
+ comment='My Comment', user=self.author, is_public=True,
+ content_object=self.gbobject, site=self.site)
+ self.assertEquals(len(mail.outbox), 0)
+ moderator = GbobjectCommentModerator(Gbobject)
+ moderator.email_reply = False
+ moderator.email_authors = False
+ moderator.mail_comment_notification_recipients = []
+ moderator.email(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 0)
+ moderator.email_reply = True
+ moderator.email_authors = True
+ moderator.mail_comment_notification_recipients = ['admin@example.com']
+ moderator.email(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 1)
+
+ def test_do_email_notification(self):
+ comment = comments.get_model().objects.create(
+ comment='My Comment', user=self.author, is_public=True,
+ content_object=self.gbobject, site=self.site)
+ self.assertEquals(len(mail.outbox), 0)
+ moderator = GbobjectCommentModerator(Gbobject)
+ moderator.mail_comment_notification_recipients = ['admin@example.com']
+ moderator.do_email_notification(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 1)
+
+ def test_do_email_authors(self):
+ comment = comments.get_model().objects.create(
+ comment='My Comment', user=self.author, is_public=True,
+ content_object=self.gbobject, site=self.site)
+ self.assertEquals(len(mail.outbox), 0)
+ moderator = GbobjectCommentModerator(Gbobject)
+ moderator.email_authors = True
+ moderator.mail_comment_notification_recipients = ['admin@example.com']
+ moderator.do_email_authors(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 0)
+ moderator.mail_comment_notification_recipients = []
+ moderator.do_email_authors(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 1)
+
+ def test_do_email_reply(self):
+ comment = comments.get_model().objects.create(
+ comment='My Comment 1', user=self.author, is_public=True,
+ content_object=self.gbobject, site=self.site)
+ moderator = GbobjectCommentModerator(Gbobject)
+ moderator.email_notification_reply = True
+ moderator.mail_comment_notification_recipients = ['admin@example.com']
+ moderator.do_email_reply(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 0)
+
+ comment = comments.get_model().objects.create(
+ comment='My Comment 2', user_email='user_1@example.com',
+ content_object=self.gbobject, is_public=True, site=self.site)
+ moderator.do_email_reply(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 0)
+
+ comment = comments.get_model().objects.create(
+ comment='My Comment 3', user_email='user_2@example.com',
+ content_object=self.gbobject, is_public=True, site=self.site)
+ moderator.do_email_reply(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 1)
+ self.assertEquals(mail.outbox[0].bcc, [u'user_1@example.com'])
+
+ comment = comments.get_model().objects.create(
+ comment='My Comment 4', user=self.author, is_public=True,
+ content_object=self.gbobject, site=self.site)
+ moderator.do_email_reply(comment, self.gbobject, 'request')
+ self.assertEquals(len(mail.outbox), 2)
+ self.assertEquals(mail.outbox[1].bcc, [u'user_1@example.com',
+ u'user_2@example.com'])
+
+ def test_moderate(self):
+ comment = comments.get_model().objects.create(
+ comment='My Comment', user=self.author, is_public=True,
+ content_object=self.gbobject, site=self.site)
+ moderator = GbobjectCommentModerator(Gbobject)
+ moderator.auto_moderate_comments = True
+ moderator.spam_checker_backends = ()
+ self.assertEquals(moderator.moderate(comment, self.gbobject, 'request'),
+ True)
+ moderator.auto_moderate_comments = False
+ self.assertEquals(moderator.moderate(comment, self.gbobject, 'request'),
+ False)
+ self.assertEquals(comments.get_model().objects.filter(
+ flags__flag='spam').count(), 0)
+ moderator.spam_checker_backends = (
+ 'objectapp.spam_checker.backends.all_is_spam',)
+ self.assertEquals(moderator.moderate(comment, self.gbobject, 'request'),
+ True)
+ self.assertEquals(comments.get_model().objects.filter(
+ flags__flag='spam').count(), 1)
diff --git a/objectapp/tests/objecttype.py b/objectapp/tests/objecttype.py
new file mode 100644
index 0000000..73e8d45
--- /dev/null
+++ b/objectapp/tests/objecttype.py
@@ -0,0 +1,116 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test cases for Objectapp's Objecttype"""
+from django.test import TestCase
+from django.contrib.sites.models import Site
+
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.managers import PUBLISHED
+
+
+class ObjecttypeTestCase(TestCase):
+
+ def setUp(self):
+ self.site = Site.objects.get_current()
+ self.objecttypes = [Objecttype.objects.create(title='Objecttype 1',
+ slug='Objecttype-1'),
+ Objecttype.objects.create(title='Objecttype 2',
+ slug='Objecttype-2')]
+ params = {'title': 'My gbobject',
+ 'content': 'My content',
+ 'tags': 'objectapp, test',
+ 'slug': 'my-gbobject'}
+
+ self.gbobject = Gbobject.objects.create(**params)
+ self.gbobject.objecttypes.add(*self.objecttypes)
+ self.gbobject.sites.add(self.site)
+
+ def test_gbobjects_published(self):
+ Objecttype = self.objecttypes[0]
+ self.assertEqual(Objecttype.gbobjects_published().count(), 0)
+ self.gbobject.status = PUBLISHED
+ self.gbobject.save()
+ self.assertEqual(Objecttype.gbobjects_published().count(), 1)
+
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content',
+ 'tags': 'objectapp, test',
+ 'status': PUBLISHED,
+ 'slug': 'my-second-gbobject'}
+
+ new_gbobject = Gbobject.objects.create(**params)
+ new_gbobject.sites.add(self.site)
+ new_gbobject.objecttypes.add(self.objecttypes[0])
+
+ self.assertEqual(self.objecttypes[0].gbobjects_published().count(), 2)
+ self.assertEqual(self.objecttypes[1].gbobjects_published().count(), 1)
+
+ def test_gbobjects_tree_path(self):
+ self.assertEqual(self.objecttypes[0].tree_path, 'Objecttype-1')
+ self.assertEqual(self.objecttypes[1].tree_path, 'Objecttype-2')
+ self.objecttypes[1].parent = self.objecttypes[0]
+ self.objecttypes[1].save()
+ self.assertEqual(self.objecttypes[1].tree_path, 'Objecttype-1/Objecttype-2')
diff --git a/objectapp/tests/ping.py b/objectapp/tests/ping.py
new file mode 100644
index 0000000..9bdcb17
--- /dev/null
+++ b/objectapp/tests/ping.py
@@ -0,0 +1,187 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test cases for Objectapp's ping"""
+import cStringIO
+from urllib2 import URLError
+from urllib import addinfourl
+from django.test import TestCase
+
+from objectapp.models import Gbobject
+from objectapp.ping import URLRessources
+from objectapp.ping import DirectoryPinger
+from objectapp.ping import ExternalUrlsPinger
+
+
+class DirectoryPingerTestCase(TestCase):
+ """Test cases for DirectoryPinger"""
+ def setUp(self):
+ params = {'title': 'My gbobject',
+ 'content': 'My content',
+ 'tags': 'objectapp, test',
+ 'slug': 'my-gbobject'}
+ self.gbobject = Gbobject.objects.create(**params)
+ self.pinger = DirectoryPinger('http://localhost', [self.gbobject],
+ start_now=False)
+
+ def test_ping_gbobject(self):
+ self.assertEquals(
+ self.pinger.ping_gbobject(self.gbobject),
+ {'message': 'http://localhost is an invalid directory.',
+ 'flerror': True})
+
+
+class ExternalUrlsPingerTestCase(TestCase):
+ """Test cases for ExternalUrlsPinger"""
+
+ def setUp(self):
+ params = {'title': 'My gbobject',
+ 'content': 'My content',
+ 'tags': 'objectapp, test',
+ 'slug': 'my-gbobject'}
+ self.gbobject = Gbobject.objects.create(**params)
+ self.pinger = ExternalUrlsPinger(self.gbobject, start_now=False)
+
+ def test_is_external_url(self):
+ r = URLRessources()
+ self.assertEquals(self.pinger.is_external_url(
+ 'http://example.com/', 'http://google.com/'), True)
+ self.assertEquals(self.pinger.is_external_url(
+ 'http://example.com/toto/', 'http://google.com/titi/'), True)
+ self.assertEquals(self.pinger.is_external_url(
+ 'http://example.com/blog/', 'http://example.com/page/'), False)
+ self.assertEquals(self.pinger.is_external_url(
+ '%s/blog/' % r.site_url, r.site_url), False)
+ self.assertEquals(self.pinger.is_external_url(
+ 'http://google.com/', r.site_url), True)
+ self.assertEquals(self.pinger.is_external_url(
+ '/blog/', r.site_url), False)
+
+ def test_find_external_urls(self):
+ r = URLRessources()
+ external_urls = self.pinger.find_external_urls(self.gbobject)
+ self.assertEquals(external_urls, [])
+ self.gbobject.content = """
+ <p>This is a <a href="http://fantomas.willbreak.it/">link</a>
+ to a site.</p>
+ <p>This is a <a href="%s/blog/">link</a> within my site.</p>
+ <p>This is a <a href="/blog/">relative link</a> within my site.</p>
+ """ % r.site_url
+ self.gbobject.save()
+ external_urls = self.pinger.find_external_urls(self.gbobject)
+ self.assertEquals(external_urls, ['http://fantomas.willbreak.it/'])
+
+ def test_find_pingback_href(self):
+ result = self.pinger.find_pingback_href('')
+ self.assertEquals(result, None)
+ result = self.pinger.find_pingback_href("""
+ <html><head><link rel="pingback" href="/xmlrpc/" /></head>
+ <body></body></html>
+ """)
+ self.assertEquals(result, '/xmlrpc/')
+ result = self.pinger.find_pingback_href("""
+ <html><head><LINK hrEF="/xmlrpc/" REL="PingBack" /></head>
+ <body></body></html>
+ """)
+ self.assertEquals(result, '/xmlrpc/')
+ result = self.pinger.find_pingback_href("""
+ <html><head><LINK REL="PingBack" /></head><body></body></html>
+ """)
+ self.assertEquals(result, None)
+
+ def fake_urlopen(self, url):
+ """Fake urlopen using test client"""
+ if 'example' in url:
+ response = cStringIO.StringIO('')
+ return addinfourl(response, {'X-Pingback': '/xmlrpc.php',
+ 'Content-Type': 'text/html'}, url)
+ elif 'localhost' in url:
+ response = cStringIO.StringIO(
+ '<link rel="pingback" href="/xmlrpc/">')
+ return addinfourl(response, {'Content-Type': 'text/xhtml'}, url)
+ elif 'google' in url:
+ response = cStringIO.StringIO('PNG CONTENT')
+ return addinfourl(response, {'content-type': 'image/png'}, url)
+ elif 'error' in url:
+ raise URLError('Invalid ressource')
+
+ def test_find_pingback_urls(self):
+ # Set up a stub around urlopen
+ import objectapp.ping
+ self.original_urlopen = objectapp.ping.urlopen
+ objectapp.ping.urlopen = self.fake_urlopen
+
+ urls = ['http://localhost/', 'http://example.com/', 'http://error',
+ 'http://www.google.co.uk/images/nav_logo72.png']
+ self.assertEquals(
+ self.pinger.find_pingback_urls(urls),
+ {'http://localhost/': 'http://localhost/xmlrpc/',
+ 'http://example.com/': 'http://example.com/xmlrpc.php'})
+ # Remove stub
+ objectapp.ping.urlopen = self.original_urlopen
+
+ def test_pingback_url(self):
+ self.assertEquals(self.pinger.pingback_url('http://localhost',
+ 'http://error.com'),
+ 'http://error.com cannot be pinged.')
diff --git a/objectapp/tests/pingback.py b/objectapp/tests/pingback.py
new file mode 100644
index 0000000..d003b27
--- /dev/null
+++ b/objectapp/tests/pingback.py
@@ -0,0 +1,253 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test cases for Objectapp's PingBack API"""
+import cStringIO
+from datetime import datetime
+from urlparse import urlsplit
+from urllib2 import HTTPError
+from xmlrpclib import ServerProxy
+
+from django.test import TestCase
+from django.contrib import comments
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.contrib.contenttypes.models import ContentType
+
+from BeautifulSoup import BeautifulSoup
+
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.managers import PUBLISHED
+from objectapp.tests.utils import TestTransport
+from objectapp.xmlrpc.pingback import generate_pingback_content
+from objectapp import url_shortener as shortener_settings
+
+
+class PingBackTestCase(TestCase):
+ """Test cases for pingbacks"""
+ urls = 'objectapp.tests.urls'
+
+ def fake_urlopen(self, url):
+ """Fake urlopen using client if domain
+ correspond to current_site else HTTPError"""
+ scheme, netloc, path, query, fragment = urlsplit(url)
+ if not netloc:
+ raise
+ if self.site.domain == netloc:
+ response = cStringIO.StringIO(self.client.get(url).content)
+ return response
+ raise HTTPError(url, 404, 'unavailable url', {}, None)
+
+ def setUp(self):
+ # Use default URL shortener backend, to avoid networks errors
+ self.original_shortener = shortener_settings.URL_SHORTENER_BACKEND
+ shortener_settings.URL_SHORTENER_BACKEND = 'objectapp.url_shortener.'\
+ 'backends.default'
+ # Set up a stub around urlopen
+ import objectapp.xmlrpc.pingback
+ self.original_urlopen = objectapp.xmlrpc.pingback.urlopen
+ objectapp.xmlrpc.pingback.urlopen = self.fake_urlopen
+ # Preparing site
+ self.site = Site.objects.get_current()
+ self.site.domain = 'localhost:8000'
+ self.site.save()
+ # Creating tests gbobjects
+ self.author = User.objects.create_user(username='webmaster',
+ email='webmaster@example.com')
+ self.Objecttype = Objecttype.objects.create(title='test', slug='test')
+ params = {'title': 'My first gbobject',
+ 'content': 'My first content',
+ 'slug': 'my-first-gbobject',
+ 'creation_date': datetime(2010, 1, 1),
+ 'status': PUBLISHED}
+ self.first_gbobject = Gbobject.objects.create(**params)
+ self.first_gbobject.sites.add(self.site)
+ self.first_gbobject.objecttypes.add(self.Objecttype)
+ self.first_gbobject.authors.add(self.author)
+
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content with link '
+ 'to <a href="http://%s%s">first gbobject</a>'
+ ' and other links : %s %s.' % (
+ self.site.domain,
+ self.first_gbobject.get_absolute_url(),
+ 'http://localhost:8000/error-404/',
+ 'http://example.com/'),
+ 'slug': 'my-second-gbobject',
+ 'creation_date': datetime(2010, 1, 1),
+ 'status': PUBLISHED}
+ self.second_gbobject = Gbobject.objects.create(**params)
+ self.second_gbobject.sites.add(self.site)
+ self.second_gbobject.objecttypes.add(self.Objecttype)
+ self.second_gbobject.authors.add(self.author)
+ # Instanciating the server proxy
+ self.server = ServerProxy('http://localhost:8000/xmlrpc/',
+ transport=TestTransport())
+
+ def tearDown(self):
+ import objectapp.xmlrpc.pingback
+ objectapp.xmlrpc.pingback.urlopen = self.original_urlopen
+ shortener_settings.URL_SHORTENER_BACKEND = self.original_shortener
+
+ def test_generate_pingback_content(self):
+ soup = BeautifulSoup(self.second_gbobject.content)
+ target = 'http://%s%s' % (self.site.domain,
+ self.first_gbobject.get_absolute_url())
+
+ self.assertEquals(
+ generate_pingback_content(soup, target, 1000),
+ 'My second content with link to first gbobject and other links : '
+ 'http://localhost:8000/error-404/ http://example.com/.')
+ self.assertEquals(
+ generate_pingback_content(soup, target, 50),
+ '...ond content with link to first gbobject and other lin...')
+
+ soup = BeautifulSoup('<a href="%s">test link</a>' % target)
+ self.assertEquals(
+ generate_pingback_content(soup, target, 6), 'test l...')
+
+ soup = BeautifulSoup('test <a href="%s">link</a>' % target)
+ self.assertEquals(
+ generate_pingback_content(soup, target, 8), '...est link')
+ self.assertEquals(
+ generate_pingback_content(soup, target, 9), 'test link')
+
+ def test_pingback_ping(self):
+ target = 'http://%s%s' % (
+ self.site.domain, self.first_gbobject.get_absolute_url())
+ source = 'http://%s%s' % (
+ self.site.domain, self.second_gbobject.get_absolute_url())
+
+ # Error code 0 : A generic fault code
+ response = self.server.pingback.ping('toto', 'titi')
+ self.assertEquals(response, 0)
+ response = self.server.pingback.ping('http://%s/' % self.site.domain,
+ 'http://%s/' % self.site.domain)
+ self.assertEquals(response, 0)
+
+ # Error code 16 : The source URI does not exist.
+ response = self.server.pingback.ping('http://example.com/', target)
+ self.assertEquals(response, 16)
+
+ # Error code 17 : The source URI does not contain a link to
+ # the target URI and so cannot be used as a source.
+ response = self.server.pingback.ping(source, 'toto')
+ self.assertEquals(response, 17)
+
+ # Error code 32 : The target URI does not exist.
+ response = self.server.pingback.ping(
+ source, 'http://localhost:8000/error-404/')
+ self.assertEquals(response, 32)
+ response = self.server.pingback.ping(source, 'http://example.com/')
+ self.assertEquals(response, 32)
+
+ # Error code 33 : The target URI cannot be used as a target.
+ response = self.server.pingback.ping(source, 'http://localhost:8000/')
+ self.assertEquals(response, 33)
+ self.first_gbobject.pingback_enabled = False
+ self.first_gbobject.save()
+ response = self.server.pingback.ping(source, target)
+ self.assertEquals(response, 33)
+
+ # Validate pingback
+ self.assertEquals(self.first_gbobject.comments.count(), 0)
+ self.first_gbobject.pingback_enabled = True
+ self.first_gbobject.save()
+ response = self.server.pingback.ping(source, target)
+ self.assertEquals(
+ response,
+ 'Pingback from %s to %s registered.' % (source, target))
+ self.assertEquals(self.first_gbobject.pingbacks.count(), 1)
+ self.assertTrue(self.second_gbobject.title in \
+ self.first_gbobject.pingbacks[0].user_name)
+
+ # Error code 48 : The pingback has already been registered.
+ response = self.server.pingback.ping(source, target)
+ self.assertEquals(response, 48)
+
+ def test_pingback_extensions_get_pingbacks(self):
+ target = 'http://%s%s' % (
+ self.site.domain, self.first_gbobject.get_absolute_url())
+ source = 'http://%s%s' % (
+ self.site.domain, self.second_gbobject.get_absolute_url())
+
+ response = self.server.pingback.ping(source, target)
+ self.assertEquals(
+ response, 'Pingback from %s to %s registered.' % (source, target))
+
+ response = self.server.pingback.extensions.getPingbacks(
+ 'http://example.com/')
+ self.assertEquals(response, 32)
+
+ response = self.server.pingback.extensions.getPingbacks(
+ 'http://localhost:8000/error-404/')
+ self.assertEquals(response, 32)
+
+ response = self.server.pingback.extensions.getPingbacks(
+ 'http://localhost:8000/2010/')
+ self.assertEquals(response, 33)
+
+ response = self.server.pingback.extensions.getPingbacks(source)
+ self.assertEquals(response, [])
+
+ response = self.server.pingback.extensions.getPingbacks(target)
+ self.assertEquals(response, [
+ 'http://localhost:8000/2010/01/01/my-second-gbobject/'])
+
+ comment = comments.get_model().objects.create(
+ content_type=ContentType.objects.get_for_model(Gbobject),
+ object_pk=self.first_gbobject.pk,
+ site=self.site, comment='Test pingback',
+ user_url='http://example.com/blog/1/',
+ user_name='Test pingback')
+ comment.flags.create(user=self.author, flag='pingback')
+
+ response = self.server.pingback.extensions.getPingbacks(target)
+ self.assertEquals(response, [
+ 'http://localhost:8000/2010/01/01/my-second-gbobject/',
+ 'http://example.com/blog/1/'])
diff --git a/objectapp/tests/quick_gbobject.py b/objectapp/tests/quick_gbobject.py
new file mode 100644
index 0000000..888ae8e
--- /dev/null
+++ b/objectapp/tests/quick_gbobject.py
@@ -0,0 +1,120 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Test cases for Objectapp's quick gbobject"""
+from django.test import TestCase
+from django.contrib.auth.models import User
+
+from objectapp import settings
+from objectapp.models import Gbobject
+from objectapp.managers import DRAFT
+
+
+class QuickGbobjectTestCase(TestCase):
+ """Test cases for quick_gbobject view"""
+ urls = 'objectapp.tests.urls'
+
+ def setUp(self):
+ self.original_wysiwyg = settings.WYSIWYG
+ settings.WYSIWYG = None
+
+ def tearDown(self):
+ settings.WYSIWYG = self.original_wysiwyg
+
+ def test_quick_gbobject(self):
+ User.objects.create_user('user', 'user@example.com', 'password')
+ User.objects.create_superuser('admin', 'admin@example.com', 'password')
+
+ response = self.client.get('/quick_gbobject/', follow=True)
+ self.assertEquals(
+ response.redirect_chain,
+ [('http://testserver/accounts/login/?next=/quick_gbobject/', 302)])
+ self.client.login(username='user', password='password')
+ response = self.client.get('/quick_gbobject/', follow=True)
+ self.assertEquals(
+ response.redirect_chain,
+ [('http://testserver/accounts/login/?next=/quick_gbobject/', 302)])
+ self.client.logout()
+ self.client.login(username='admin', password='password')
+ response = self.client.get('/quick_gbobject/', follow=True)
+ self.assertEquals(response.redirect_chain,
+ [('http://testserver/admin/objectapp/gbobject/add/', 302)])
+ response = self.client.post('/quick_gbobject/', {'title': 'test'},
+ follow=True)
+ self.assertEquals(response.redirect_chain,
+ [('http://testserver/admin/objectapp/gbobject/add/' \
+ '?tags=&title=test&sites=1&content=' \
+ '%3Cp%3E%3C%2Fp%3E&authors=2&slug=test', 302)])
+ response = self.client.post('/quick_gbobject/',
+ {'title': 'test', 'tags': 'test',
+ 'content': 'Test content',
+ 'save_draft': ''}, follow=True)
+ gbobject = Gbobject.objects.get(title='test')
+ self.assertEquals(response.redirect_chain,
+ [('http://testserver%s' % gbobject.get_absolute_url(),
+ 302)])
+ self.assertEquals(gbobject.status, DRAFT)
+ self.assertEquals(gbobject.title, 'test')
+ self.assertEquals(gbobject.tags, 'test')
+ self.assertEquals(gbobject.content, '<p>Test content</p>')
diff --git a/objectapp/tests/signals.py b/objectapp/tests/signals.py
new file mode 100644
index 0000000..c6f6b06
--- /dev/null
+++ b/objectapp/tests/signals.py
@@ -0,0 +1,154 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test cases for Objectapp's signals"""
+from django.test import TestCase
+
+from objectapp.models import Gbobject
+from objectapp.managers import DRAFT
+from objectapp.managers import PUBLISHED
+from objectapp.signals import disable_for_loaddata
+from objectapp.signals import ping_directories_handler
+from objectapp.signals import ping_external_urls_handler
+
+
+class SignalsTestCase(TestCase):
+ """Test cases for signals"""
+
+ def test_disable_for_loaddata(self):
+ self.top = 0
+
+ @disable_for_loaddata
+ def make_top():
+ self.top += 1
+
+ def call():
+ return make_top()
+
+ call()
+ self.assertEquals(self.top, 1)
+ # Okay the command is executed
+
+ def test_ping_directories_handler(self):
+ # Set up a stub around DirectoryPinger
+ self.top = 0
+
+ def fake_pinger(*ka, **kw):
+ self.top += 1
+
+ import objectapp.ping
+ from objectapp import settings
+ self.original_pinger = objectapp.ping.DirectoryPinger
+ objectapp.ping.DirectoryPinger = fake_pinger
+
+ params = {'title': 'My gbobject',
+ 'content': 'My content',
+ 'status': PUBLISHED,
+ 'slug': 'my-gbobject'}
+ gbobject = Gbobject.objects.create(**params)
+ self.assertEquals(gbobject.is_visible, True)
+ settings.PING_DIRECTORIES = ()
+ ping_directories_handler('sender', **{'instance': gbobject})
+ self.assertEquals(self.top, 0)
+ settings.PING_DIRECTORIES = ('toto',)
+ settings.SAVE_PING_DIRECTORIES = True
+ ping_directories_handler('sender', **{'instance': gbobject})
+ self.assertEquals(self.top, 1)
+ gbobject.status = DRAFT
+ ping_directories_handler('sender', **{'instance': gbobject})
+ self.assertEquals(self.top, 1)
+
+ # Remove stub
+ objectapp.ping.DirectoryPinger = self.original_pinger
+
+ def test_ping_external_urls_handler(self):
+ # Set up a stub around ExternalUrlsPinger
+ self.top = 0
+
+ def fake_pinger(*ka, **kw):
+ self.top += 1
+
+ import objectapp.ping
+ from objectapp import settings
+ self.original_pinger = objectapp.ping.ExternalUrlsPinger
+ objectapp.ping.ExternalUrlsPinger = fake_pinger
+
+ params = {'title': 'My gbobject',
+ 'content': 'My content',
+ 'status': PUBLISHED,
+ 'slug': 'my-gbobject'}
+ gbobject = Gbobject.objects.create(**params)
+ self.assertEquals(gbobject.is_visible, True)
+ settings.SAVE_PING_EXTERNAL_URLS = False
+ ping_external_urls_handler('sender', **{'instance': gbobject})
+ self.assertEquals(self.top, 0)
+ settings.SAVE_PING_EXTERNAL_URLS = True
+ ping_external_urls_handler('sender', **{'instance': gbobject})
+ self.assertEquals(self.top, 1)
+ gbobject.status = 0
+ ping_external_urls_handler('sender', **{'instance': gbobject})
+ self.assertEquals(self.top, 1)
+
+ # Remove stub
+ objectapp.ping.ExternalUrlsPinger = self.original_pinger
diff --git a/objectapp/tests/sitemaps.py b/objectapp/tests/sitemaps.py
new file mode 100644
index 0000000..e91a07c
--- /dev/null
+++ b/objectapp/tests/sitemaps.py
@@ -0,0 +1,145 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test cases for Objectapp's sitemaps"""
+from django.test import TestCase
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+
+from tagging.models import Tag
+
+from objectapp.models import Gbobject
+from objectapp.models import Author
+from objectapp.models import Objecttype
+from objectapp.managers import PUBLISHED
+from objectapp.sitemaps import GbobjectSitemap
+from objectapp.sitemaps import ObjecttypeSitemap
+from objectapp.sitemaps import AuthorSitemap
+from objectapp.sitemaps import TagSitemap
+
+
+class ObjectappSitemapsTestCase(TestCase):
+ """Test cases for Sitemaps classes provided"""
+ urls = 'objectapp.tests.urls'
+
+ def setUp(self):
+ self.site = Site.objects.get_current()
+ self.author = User.objects.create(username='admin',
+ email='admin@example.com')
+ self.Objecttype = Objecttype.objects.create(title='Tests', slug='tests')
+ params = {'title': 'My gbobject 1', 'content': 'My content 1',
+ 'tags': 'objectapp, test', 'slug': 'my-gbobject-1',
+ 'status': PUBLISHED}
+ self.gbobject_1 = Gbobject.objects.create(**params)
+ self.gbobject_1.authors.add(self.author)
+ self.gbobject_1.objecttypes.add(self.Objecttype)
+ self.gbobject_1.sites.add(self.site)
+
+ params = {'title': 'My gbobject 2', 'content': 'My content 2',
+ 'tags': 'objectapp', 'slug': 'my-gbobject-2',
+ 'status': PUBLISHED}
+ self.gbobject_2 = Gbobject.objects.create(**params)
+ self.gbobject_2.authors.add(self.author)
+ self.gbobject_2.objecttypes.add(self.Objecttype)
+ self.gbobject_2.sites.add(self.site)
+
+ def test_gbobject_sitemap(self):
+ sitemap = GbobjectSitemap()
+ self.assertEquals(len(sitemap.items()), 2)
+ self.assertEquals(sitemap.lastmod(self.gbobject_1),
+ self.gbobject_1.last_update)
+
+ def test_Objecttype_sitemap(self):
+ sitemap = ObjecttypeSitemap()
+ self.assertEquals(len(sitemap.items()), 1)
+ self.assertEquals(sitemap.lastmod(self.Objecttype),
+ self.gbobject_2.creation_date)
+ self.assertEquals(sitemap.lastmod(Objecttype.objects.create(
+ title='New', slug='new')), None)
+ self.assertEquals(sitemap.priority(self.Objecttype), '1.0')
+
+ def test_author_sitemap(self):
+ sitemap = AuthorSitemap()
+ authors = sitemap.items()
+ self.assertEquals(len(authors), 1)
+ self.assertEquals(sitemap.lastmod(authors[0]),
+ self.gbobject_2.creation_date)
+ self.assertEquals(sitemap.lastmod(Author.objects.create(
+ username='New', email='new@example.com')), None)
+ self.assertEquals(sitemap.location(self.author), '/authors/admin/')
+
+ def test_tag_sitemap(self):
+ sitemap = TagSitemap()
+ objectapp_tag = Tag.objects.get(name='objectapp')
+ self.assertEquals(len(sitemap.items()), 2)
+ self.assertEquals(sitemap.lastmod(objectapp_tag),
+ self.gbobject_2.creation_date)
+ self.assertEquals(sitemap.priority(objectapp_tag), '1.0')
+ self.assertEquals(sitemap.location(objectapp_tag), '/tags/objectapp/')
+
+ def test_Objecttype_sitemap_zero_division_error(self):
+ Gbobject.objects.all().delete()
+ Objecttype_sitemap = ObjecttypeSitemap()
+ Objecttype_sitemap.items()
+ self.assertEquals(Objecttype_sitemap.priority(self.Objecttype), '0.5')
diff --git a/objectapp/tests/spam_checker.py b/objectapp/tests/spam_checker.py
new file mode 100644
index 0000000..9465255
--- /dev/null
+++ b/objectapp/tests/spam_checker.py
@@ -0,0 +1,104 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test cases for Objectapp's spam_checker"""
+from __future__ import with_statement
+import warnings
+
+from django.test import TestCase
+
+from objectapp.spam_checker import get_spam_checker
+from objectapp.spam_checker.backends.all_is_spam import backend
+
+
+class SpamCheckerTestCase(TestCase):
+ """Test cases for objectapp.spam_checker"""
+
+ def test_get_spam_checker(self):
+ try:
+ with warnings.catch_warnings(record=True) as w:
+ self.assertEquals(get_spam_checker('mymodule.myclass'), None)
+ self.assertTrue(issubclass(w[-1].Objecttype, RuntimeWarning))
+ self.assertEquals(
+ str(w[-1].message),
+ 'mymodule.myclass backend cannot be imported')
+ except AttributeError:
+ # Fail under Python2.5, because of'warnings.catch_warnings'
+ pass
+
+ try:
+ with warnings.catch_warnings(record=True) as w:
+ self.assertEquals(
+ get_spam_checker('objectapp.tests.custom_spam_checker'), None)
+ self.assertTrue(issubclass(w[-1].Objecttype, RuntimeWarning))
+ self.assertEquals(
+ str(w[-1].message),
+ 'This backend only exists for testing')
+ except AttributeError:
+ # Fail under Python2.5, because of'warnings.catch_warnings'
+ pass
+
+ self.assertEquals(
+ get_spam_checker('objectapp.spam_checker.backends.all_is_spam'),
+ backend)
diff --git a/objectapp/tests/templates/objectapp/_gbobject_detail.html b/objectapp/tests/templates/objectapp/_gbobject_detail.html
new file mode 100644
index 0000000..0ffd2e6
--- /dev/null
+++ b/objectapp/tests/templates/objectapp/_gbobject_detail.html
@@ -0,0 +1,3 @@
+<h1>{{ object.title }}</h1>
+
+{{ object.content|linebreaks }}
diff --git a/objectapp/tests/templates/objectapp/base.html b/objectapp/tests/templates/objectapp/base.html
new file mode 100644
index 0000000..c938356
--- /dev/null
+++ b/objectapp/tests/templates/objectapp/base.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Objectapp's Blog - {% block title %}{% endblock %}</title>
+ </head>
+ <body>
+ {% block content %}{% endblock %}
+ </body>
+</html>
+
diff --git a/objectapp/tests/templates/objectapp/gbobject_detail.html b/objectapp/tests/templates/objectapp/gbobject_detail.html
new file mode 100644
index 0000000..02b9aba
--- /dev/null
+++ b/objectapp/tests/templates/objectapp/gbobject_detail.html
@@ -0,0 +1,7 @@
+{% extends "objectapp/base.html" %}
+
+{% block title %}{{ object.title }}{% endblock %}
+
+{% block content %}
+ {{ object.html_content|safe }}
+{% endblock %}
diff --git a/objectapp/tests/templates/objectapp/gbobject_list.html b/objectapp/tests/templates/objectapp/gbobject_list.html
new file mode 100644
index 0000000..6b5b974
--- /dev/null
+++ b/objectapp/tests/templates/objectapp/gbobject_list.html
@@ -0,0 +1,10 @@
+{% extends "objectapp/base.html" %}
+
+{% block content %}
+
+{% for object in object_list %}
+ {{ object.html_content|safe }}
+{% endfor %}
+
+{% endblock %}
+
diff --git a/objectapp/tests/templates/objectapp/gbobject_search.html b/objectapp/tests/templates/objectapp/gbobject_search.html
new file mode 100644
index 0000000..d85e241
--- /dev/null
+++ b/objectapp/tests/templates/objectapp/gbobject_search.html
@@ -0,0 +1 @@
+{% extends "objectapp/gbobject_list.html" %}
diff --git a/objectapp/tests/templatetags.py b/objectapp/tests/templatetags.py
new file mode 100644
index 0000000..ab1c5d7
--- /dev/null
+++ b/objectapp/tests/templatetags.py
@@ -0,0 +1,590 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test cases for Objectapp's templatetags"""
+from datetime import datetime
+
+from django.test import TestCase
+from django.template import Context
+from django.template import Template
+from django.template import TemplateSyntaxError
+from django.contrib import comments
+from django.core.paginator import Paginator
+from django.core.urlresolvers import reverse
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.contrib.comments.models import CommentFlag
+
+from tagging.models import Tag
+
+from objectapp.models import Gbobject
+from objectapp.models import Author
+from objectapp.models import Objecttype
+from objectapp.managers import DRAFT
+from objectapp.managers import PUBLISHED
+from objectapp.templatetags.objectapp_tags import get_authors
+from objectapp.templatetags.objectapp_tags import get_gravatar
+from objectapp.templatetags.objectapp_tags import get_tag_cloud
+from objectapp.templatetags.objectapp_tags import get_objecttypes
+from objectapp.templatetags.objectapp_tags import objectapp_pagination
+from objectapp.templatetags.objectapp_tags import get_recent_gbobjects
+from objectapp.templatetags.objectapp_tags import get_random_gbobjects
+from objectapp.templatetags.objectapp_tags import objectapp_breadcrumbs
+from objectapp.templatetags.objectapp_tags import get_popular_gbobjects
+from objectapp.templatetags.objectapp_tags import get_similar_gbobjects
+from objectapp.templatetags.objectapp_tags import get_recent_comments
+from objectapp.templatetags.objectapp_tags import get_recent_linkbacks
+from objectapp.templatetags.objectapp_tags import get_calendar_gbobjects
+from objectapp.templatetags.objectapp_tags import get_archives_gbobjects
+from objectapp.templatetags.objectapp_tags import get_featured_gbobjects
+from objectapp.templatetags.objectapp_tags import get_archives_gbobjects_tree
+
+
+class TemplateTagsTestCase(TestCase):
+ """Test cases for Template tags"""
+
+ def setUp(self):
+ params = {'title': 'My gbobject',
+ 'content': 'My content',
+ 'tags': 'objectapp, test',
+ 'creation_date': datetime(2010, 1, 1),
+ 'slug': 'my-gbobject'}
+ self.gbobject = Gbobject.objects.create(**params)
+
+ def publish_gbobject(self):
+ self.gbobject.status = PUBLISHED
+ self.gbobject.featured = True
+ self.gbobject.sites.add(Site.objects.get_current())
+ self.gbobject.save()
+
+ def test_get_objecttypes(self):
+ context = get_objecttypes()
+ self.assertEquals(len(context['objecttypes']), 0)
+ self.assertEquals(context['template'], 'objectapp/tags/objecttypes.html')
+
+ Objecttype.objects.create(title='Objecttype 1', slug='Objecttype-1')
+ context = get_objecttypes('custom_template.html')
+ self.assertEquals(len(context['objecttypes']), 1)
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ def test_get_authors(self):
+ context = get_authors()
+ self.assertEquals(len(context['authors']), 0)
+ self.assertEquals(context['template'], 'objectapp/tags/authors.html')
+
+ user = User.objects.create_user(username='webmaster',
+ email='webmaster@example.com')
+ self.gbobject.authors.add(user)
+ self.publish_gbobject()
+ context = get_authors('custom_template.html')
+ self.assertEquals(len(context['authors']), 1)
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ def test_get_recent_gbobjects(self):
+ context = get_recent_gbobjects()
+ self.assertEquals(len(context['gbobjects']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/recent_gbobjects.html')
+
+ self.publish_gbobject()
+ context = get_recent_gbobjects(3, 'custom_template.html')
+ self.assertEquals(len(context['gbobjects']), 1)
+ self.assertEquals(context['template'], 'custom_template.html')
+ context = get_recent_gbobjects(0)
+ self.assertEquals(len(context['gbobjects']), 0)
+
+ def test_get_featured_gbobjects(self):
+ context = get_featured_gbobjects()
+ self.assertEquals(len(context['gbobjects']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/featured_gbobjects.html')
+
+ self.publish_gbobject()
+ context = get_featured_gbobjects(3, 'custom_template.html')
+ self.assertEquals(len(context['gbobjects']), 1)
+ self.assertEquals(context['template'], 'custom_template.html')
+ context = get_featured_gbobjects(0)
+ self.assertEquals(len(context['gbobjects']), 0)
+
+ def test_get_random_gbobjects(self):
+ context = get_random_gbobjects()
+ self.assertEquals(len(context['gbobjects']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/random_gbobjects.html')
+
+ self.publish_gbobject()
+ context = get_random_gbobjects(3, 'custom_template.html')
+ self.assertEquals(len(context['gbobjects']), 1)
+ self.assertEquals(context['template'], 'custom_template.html')
+ context = get_random_gbobjects(0)
+ self.assertEquals(len(context['gbobjects']), 0)
+
+ def test_get_popular_gbobjects(self):
+ context = get_popular_gbobjects()
+ self.assertEquals(len(context['gbobjects']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/popular_gbobjects.html')
+
+ self.publish_gbobject()
+ context = get_popular_gbobjects(3, 'custom_template.html')
+ self.assertEquals(len(context['gbobjects']), 0)
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content',
+ 'tags': 'objectapp, test',
+ 'status': PUBLISHED,
+ 'slug': 'my-second-gbobject'}
+ site = Site.objects.get_current()
+ second_gbobject = Gbobject.objects.create(**params)
+ second_gbobject.sites.add(site)
+
+ comments.get_model().objects.create(comment='My Comment 1', site=site,
+ content_object=self.gbobject)
+ comments.get_model().objects.create(comment='My Comment 2', site=site,
+ content_object=self.gbobject)
+ comments.get_model().objects.create(comment='My Comment 3', site=site,
+ content_object=second_gbobject)
+ context = get_popular_gbobjects(3)
+ self.assertEquals(context['gbobjects'], [self.gbobject, second_gbobject])
+ self.gbobject.status = DRAFT
+ self.gbobject.save()
+ context = get_popular_gbobjects(3)
+ self.assertEquals(context['gbobjects'], [second_gbobject])
+
+ def test_get_similar_gbobjects(self):
+ self.publish_gbobject()
+ source_context = Context({'object': self.gbobject})
+ context = get_similar_gbobjects(source_context)
+ self.assertEquals(len(context['gbobjects']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/similar_gbobjects.html')
+
+ params = {'title': 'My second gbobject',
+ 'content': 'This is the second gbobject of my tests.',
+ 'tags': 'objectapp, test',
+ 'status': PUBLISHED,
+ 'slug': 'my-second-gbobject'}
+ site = Site.objects.get_current()
+ second_gbobject = Gbobject.objects.create(**params)
+ second_gbobject.sites.add(site)
+
+ source_context = Context({'object': second_gbobject})
+ context = get_similar_gbobjects(source_context, 3,
+ 'custom_template.html',
+ flush=True)
+ self.assertEquals(len(context['gbobjects']), 1)
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ def test_get_archives_gbobjects(self):
+ context = get_archives_gbobjects()
+ self.assertEquals(len(context['archives']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/archives_gbobjects.html')
+
+ self.publish_gbobject()
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content',
+ 'tags': 'objectapp, test',
+ 'status': PUBLISHED,
+ 'creation_date': datetime(2009, 1, 1),
+ 'slug': 'my-second-gbobject'}
+ site = Site.objects.get_current()
+ second_gbobject = Gbobject.objects.create(**params)
+ second_gbobject.sites.add(site)
+
+ context = get_archives_gbobjects('custom_template.html')
+ self.assertEquals(len(context['archives']), 2)
+ self.assertEquals(context['archives'][0], datetime(2010, 1, 1))
+ self.assertEquals(context['archives'][1], datetime(2009, 1, 1))
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ def test_get_archives_tree(self):
+ context = get_archives_gbobjects_tree()
+ self.assertEquals(len(context['archives']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/archives_gbobjects_tree.html')
+
+ self.publish_gbobject()
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content',
+ 'tags': 'objectapp, test',
+ 'status': PUBLISHED,
+ 'creation_date': datetime(2009, 1, 10),
+ 'slug': 'my-second-gbobject'}
+ site = Site.objects.get_current()
+ second_gbobject = Gbobject.objects.create(**params)
+ second_gbobject.sites.add(site)
+
+ context = get_archives_gbobjects_tree('custom_template.html')
+ self.assertEquals(len(context['archives']), 2)
+ self.assertEquals(context['archives'][0], datetime(2009, 1, 10))
+ self.assertEquals(context['archives'][1], datetime(2010, 1, 1))
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ def test_get_calendar_gbobjects(self):
+ source_context = Context()
+ context = get_calendar_gbobjects(source_context)
+ self.assertEquals(context['previous_month'], None)
+ self.assertEquals(context['next_month'], None)
+ self.assertEquals(context['template'], 'objectapp/tags/calendar.html')
+
+ self.publish_gbobject()
+ context = get_calendar_gbobjects(source_context,
+ template='custom_template.html')
+ self.assertEquals(context['previous_month'], datetime(2010, 1, 1))
+ self.assertEquals(context['next_month'], None)
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ context = get_calendar_gbobjects(source_context, 2009, 1)
+ self.assertEquals(context['previous_month'], None)
+ self.assertEquals(context['next_month'], datetime(2010, 1, 1))
+
+ source_context = Context({'month': datetime(2009, 1, 1)})
+ context = get_calendar_gbobjects(source_context)
+ self.assertEquals(context['previous_month'], None)
+ self.assertEquals(context['next_month'], datetime(2010, 1, 1))
+
+ source_context = Context({'month': datetime(2010, 1, 1)})
+ context = get_calendar_gbobjects(source_context)
+ self.assertEquals(context['previous_month'], None)
+ self.assertEquals(context['next_month'], None)
+
+ params = {'title': 'My second gbobject',
+ 'content': 'My second content',
+ 'tags': 'objectapp, test',
+ 'status': PUBLISHED,
+ 'creation_date': datetime(2008, 1, 1),
+ 'slug': 'my-second-gbobject'}
+ site = Site.objects.get_current()
+ second_gbobject = Gbobject.objects.create(**params)
+ second_gbobject.sites.add(site)
+
+ source_context = Context()
+ context = get_calendar_gbobjects(source_context, 2009, 1)
+ self.assertEquals(context['previous_month'], datetime(2008, 1, 1))
+ self.assertEquals(context['next_month'], datetime(2010, 1, 1))
+ context = get_calendar_gbobjects(source_context)
+ self.assertEquals(context['previous_month'], datetime(2010, 1, 1))
+ self.assertEquals(context['next_month'], None)
+
+ def test_get_recent_comments(self):
+ site = Site.objects.get_current()
+ context = get_recent_comments()
+ self.assertEquals(len(context['comments']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/recent_comments.html')
+
+ comment_1 = comments.get_model().objects.create(
+ comment='My Comment 1', site=site,
+ content_object=self.gbobject)
+ context = get_recent_comments(3, 'custom_template.html')
+ self.assertEquals(len(context['comments']), 0)
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ self.publish_gbobject()
+ context = get_recent_comments()
+ self.assertEquals(len(context['comments']), 1)
+
+ author = User.objects.create_user(username='webmaster',
+ email='webmaster@example.com')
+ comment_2 = comments.get_model().objects.create(
+ comment='My Comment 2', site=site,
+ content_object=self.gbobject)
+ comment_2.flags.create(user=author,
+ flag=CommentFlag.MODERATOR_APPROVAL)
+ context = get_recent_comments()
+ self.assertEquals(list(context['comments']), [comment_2, comment_1])
+
+ def test_get_recent_linkbacks(self):
+ user = User.objects.create_user(username='webmaster',
+ email='webmaster@example.com')
+ site = Site.objects.get_current()
+ context = get_recent_linkbacks()
+ self.assertEquals(len(context['linkbacks']), 0)
+ self.assertEquals(context['template'],
+ 'objectapp/tags/recent_linkbacks.html')
+
+ linkback_1 = comments.get_model().objects.create(
+ comment='My Linkback 1', site=site,
+ content_object=self.gbobject)
+ linkback_1.flags.create(user=user, flag='pingback')
+ context = get_recent_linkbacks(3, 'custom_template.html')
+ self.assertEquals(len(context['linkbacks']), 0)
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ self.publish_gbobject()
+ context = get_recent_linkbacks()
+ self.assertEquals(len(context['linkbacks']), 1)
+
+ linkback_2 = comments.get_model().objects.create(
+ comment='My Linkback 2', site=site,
+ content_object=self.gbobject)
+ linkback_2.flags.create(user=user, flag='trackback')
+ context = get_recent_linkbacks()
+ self.assertEquals(list(context['linkbacks']), [linkback_2, linkback_1])
+
+ def test_objectapp_pagination(self):
+ class FakeRequest(object):
+ def __init__(self, get_dict):
+ self.GET = get_dict
+
+ source_context = Context({'request': FakeRequest(
+ {'page': '1', 'key': 'val'})})
+ paginator = Paginator(range(200), 10)
+
+ context = objectapp_pagination(source_context, paginator.page(1))
+ self.assertEquals(context['page'].number, 1)
+ self.assertEquals(context['begin'], [1, 2, 3])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [18, 19, 20])
+ self.assertEquals(context['GET_string'], '&key=val')
+ self.assertEquals(context['template'], 'objectapp/tags/pagination.html')
+
+ source_context = Context({'request': FakeRequest({})})
+ context = objectapp_pagination(source_context, paginator.page(2))
+ self.assertEquals(context['page'].number, 2)
+ self.assertEquals(context['begin'], [1, 2, 3, 4])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [18, 19, 20])
+ self.assertEquals(context['GET_string'], '')
+
+ context = objectapp_pagination(source_context, paginator.page(3))
+ self.assertEquals(context['begin'], [1, 2, 3, 4, 5])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [18, 19, 20])
+
+ context = objectapp_pagination(source_context, paginator.page(6))
+ self.assertEquals(context['begin'], [1, 2, 3, 4, 5, 6, 7, 8])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [18, 19, 20])
+
+ context = objectapp_pagination(source_context, paginator.page(11))
+ self.assertEquals(context['begin'], [1, 2, 3])
+ self.assertEquals(context['middle'], [9, 10, 11, 12, 13])
+ self.assertEquals(context['end'], [18, 19, 20])
+
+ context = objectapp_pagination(source_context, paginator.page(15))
+ self.assertEquals(context['begin'], [1, 2, 3])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [13, 14, 15, 16, 17, 18, 19, 20])
+
+ context = objectapp_pagination(source_context, paginator.page(18))
+ self.assertEquals(context['begin'], [1, 2, 3])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [16, 17, 18, 19, 20])
+
+ context = objectapp_pagination(source_context, paginator.page(19))
+ self.assertEquals(context['begin'], [1, 2, 3])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [17, 18, 19, 20])
+
+ context = objectapp_pagination(source_context, paginator.page(20))
+ self.assertEquals(context['begin'], [1, 2, 3])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [18, 19, 20])
+
+ context = objectapp_pagination(source_context, paginator.page(10),
+ begin_pages=1, end_pages=3,
+ before_pages=4, after_pages=3,
+ template='custom_template.html')
+ self.assertEquals(context['begin'], [1])
+ self.assertEquals(context['middle'], [6, 7, 8, 9, 10, 11, 12, 13])
+ self.assertEquals(context['end'], [18, 19, 20])
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ paginator = Paginator(range(50), 10)
+ context = objectapp_pagination(source_context, paginator.page(1))
+ self.assertEquals(context['begin'], [1, 2, 3, 4, 5])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [])
+
+ paginator = Paginator(range(60), 10)
+ context = objectapp_pagination(source_context, paginator.page(1))
+ self.assertEquals(context['begin'], [1, 2, 3, 4, 5, 6])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [])
+
+ paginator = Paginator(range(70), 10)
+ context = objectapp_pagination(source_context, paginator.page(1))
+ self.assertEquals(context['begin'], [1, 2, 3])
+ self.assertEquals(context['middle'], [])
+ self.assertEquals(context['end'], [5, 6, 7])
+
+ def test_objectapp_breadcrumbs(self):
+ class FakeRequest(object):
+ def __init__(self, path):
+ self.path = path
+
+ source_context = Context({'request': FakeRequest('/')})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 1)
+ self.assertEquals(context['breadcrumbs'][0].name, 'Blog')
+ self.assertEquals(context['breadcrumbs'][0].url,
+ reverse('objectapp_gbobject_archive_index'))
+ self.assertEquals(context['separator'], '/')
+ self.assertEquals(context['template'], 'objectapp/tags/breadcrumbs.html')
+
+ context = objectapp_breadcrumbs(source_context,
+ '>', 'Weblog', 'custom_template.html')
+ self.assertEquals(len(context['breadcrumbs']), 1)
+ self.assertEquals(context['breadcrumbs'][0].name, 'Weblog')
+ self.assertEquals(context['separator'], '>')
+ self.assertEquals(context['template'], 'custom_template.html')
+
+ source_context = Context(
+ {'request': FakeRequest(self.gbobject.get_absolute_url()),
+ 'object': self.gbobject})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 5)
+
+ cat_1 = Objecttype.objects.create(title='Objecttype 1', slug='Objecttype-1')
+ source_context = Context(
+ {'request': FakeRequest(cat_1.get_absolute_url()),
+ 'object': cat_1})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 3)
+ cat_2 = Objecttype.objects.create(title='Objecttype 2', slug='Objecttype-2',
+ parent=cat_1)
+ source_context = Context(
+ {'request': FakeRequest(cat_2.get_absolute_url()),
+ 'object': cat_2})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 4)
+
+ tag = Tag.objects.get(name='test')
+ source_context = Context(
+ {'request': FakeRequest(reverse('objectapp_tag_detail',
+ args=['test'])),
+ 'object': tag})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 3)
+
+ User.objects.create_user(username='webmaster',
+ email='webmaster@example.com')
+ author = Author.objects.get(username='webmaster')
+ source_context = Context(
+ {'request': FakeRequest(author.get_absolute_url()),
+ 'object': author})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 3)
+
+ source_context = Context(
+ {'request': FakeRequest(reverse(
+ 'objectapp_gbobject_archive_year', args=[2011]))})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 2)
+
+ source_context = Context({'request': FakeRequest(reverse(
+ 'objectapp_gbobject_archive_month', args=[2011, '03']))})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 3)
+
+ source_context = Context({'request': FakeRequest(reverse(
+ 'objectapp_gbobject_archive_day', args=[2011, '03', 15]))})
+ context = objectapp_breadcrumbs(source_context)
+ self.assertEquals(len(context['breadcrumbs']), 4)
+ # More tests can be done here, for testing path and objects in context
+
+ def test_get_gravatar(self):
+ self.assertEquals(
+ get_gravatar('webmaster@example.com'),
+ 'http://www.gravatar.com/avatar/86d4fd4a22de452'
+ 'a9228298731a0b592.jpg?s=80&amp;r=g')
+ self.assertEquals(
+ get_gravatar(' WEBMASTER@example.com ', 15, 'x', '404'),
+ 'http://www.gravatar.com/avatar/86d4fd4a22de452'
+ 'a9228298731a0b592.jpg?s=15&amp;r=x&amp;d=404')
+
+ def test_get_tags(self):
+ Tag.objects.create(name='tag')
+ t = Template("""
+ {% load objectapp_tags %}
+ {% get_tags as gbobject_tags %}
+ {{ gbobject_tags|join:", " }}
+ """)
+ html = t.render(Context())
+ self.assertEquals(html.strip(), '')
+ self.publish_gbobject()
+ html = t.render(Context())
+ self.assertEquals(html.strip(), 'test, objectapp')
+
+ template_error_as = """
+ {% load objectapp_tags %}
+ {% get_tags a_s gbobject_tags %}"""
+ self.assertRaises(TemplateSyntaxError, Template, template_error_as)
+
+ template_error_args = """
+ {% load objectapp_tags %}
+ {% get_tags as gbobject tags %}"""
+ self.assertRaises(TemplateSyntaxError, Template, template_error_args)
+
+ def test_get_tag_cloud(self):
+ context = get_tag_cloud()
+ self.assertEquals(len(context['tags']), 0)
+ self.assertEquals(context['template'], 'objectapp/tags/tag_cloud.html')
+ self.publish_gbobject()
+ context = get_tag_cloud(6, 'custom_template.html')
+ self.assertEquals(len(context['tags']), 2)
+ self.assertEquals(context['template'], 'custom_template.html')
diff --git a/objectapp/tests/url_shortener.py b/objectapp/tests/url_shortener.py
new file mode 100644
index 0000000..4a3719c
--- /dev/null
+++ b/objectapp/tests/url_shortener.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test cases for Objectapp's url_shortener"""
+from __future__ import with_statement
+import warnings
+
+from django.test import TestCase
+
+from objectapp.url_shortener import get_url_shortener
+from objectapp import url_shortener as us_settings
+from objectapp.url_shortener.backends.default import backend as default_backend
+
+
+class URLShortenerTestCase(TestCase):
+ """Test cases for objectapp.url_shortener"""
+
+ def setUp(self):
+ self.original_backend = us_settings.URL_SHORTENER_BACKEND
+
+ def tearDown(self):
+ us_settings.URL_SHORTENER_BACKEND = self.original_backend
+
+ def test_get_url_shortener(self):
+ us_settings.URL_SHORTENER_BACKEND = 'mymodule.myclass'
+ try:
+ with warnings.catch_warnings(record=True) as w:
+ self.assertEquals(get_url_shortener(), default_backend)
+ self.assertTrue(issubclass(w[-1].Objecttype, RuntimeWarning))
+ self.assertEquals(
+ str(w[-1].message),
+ 'mymodule.myclass backend cannot be imported')
+ except AttributeError:
+ # Fail under Python2.5, because of'warnings.catch_warnings'
+ pass
+
+ us_settings.URL_SHORTENER_BACKEND = 'objectapp.tests.custom_url_shortener'
+ try:
+ with warnings.catch_warnings(record=True) as w:
+ self.assertEquals(get_url_shortener(), default_backend)
+ self.assertTrue(issubclass(w[-1].Objecttype, RuntimeWarning))
+ self.assertEquals(
+ str(w[-1].message),
+ 'This backend only exists for testing')
+ except AttributeError:
+ # Fail under Python2.5, because of'warnings.catch_warnings'
+ pass
+
+ us_settings.URL_SHORTENER_BACKEND = 'objectapp.url_shortener'\
+ '.backends.default'
+ self.assertEquals(get_url_shortener(), default_backend)
diff --git a/objectapp/tests/urls.py b/objectapp/tests/urls.py
new file mode 100644
index 0000000..a9b8400
--- /dev/null
+++ b/objectapp/tests/urls.py
@@ -0,0 +1,85 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test urls for the objectapp project"""
+from django.contrib import admin
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import include
+from django.conf.urls.defaults import patterns
+
+from objectapp.urls import urlpatterns
+
+admin.autodiscover()
+
+handler500 = 'django.views.defaults.server_error'
+handler404 = 'django.views.defaults.page_not_found'
+
+urlpatterns += patterns(
+ '',
+ url(r'^channel-test/$', 'objectapp.views.channels.gbobject_channel',
+ {'query': 'test'}),
+ url(r'^comments/', include('django.contrib.comments.urls')),
+ url(r'^xmlrpc/$', 'django_xmlrpc.views.handle_xmlrpc'),
+ url(r'^admin/', include(admin.site.urls)),
+ )
diff --git a/objectapp/tests/utils.py b/objectapp/tests/utils.py
new file mode 100644
index 0000000..16b4e8c
--- /dev/null
+++ b/objectapp/tests/utils.py
@@ -0,0 +1,90 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Utils for Objectapp's tests"""
+import StringIO
+from xmlrpclib import Transport
+
+from django.test.client import Client
+
+
+class TestTransport(Transport):
+ """Handles connections to XML-RPC server
+ through Django test client."""
+
+ def __init__(self, *args, **kwargs):
+ Transport.__init__(self, *args, **kwargs)
+ self.client = Client()
+
+ def request(self, host, handler, request_body, verbose=0):
+ self.verbose = verbose
+ response = self.client.post(handler,
+ request_body,
+ content_type="text/xml")
+ res = StringIO.StringIO(response.content)
+ setattr(res, 'getheader', lambda *args: '') # For Python >= 2.7
+ res.seek(0)
+ if not hasattr(res, 'getheader'):
+ setattr(res, 'getheader', lambda *args: "")
+ return self.parse_response(res)
diff --git a/objectapp/tests/views.py b/objectapp/tests/views.py
new file mode 100644
index 0000000..5f4907a
--- /dev/null
+++ b/objectapp/tests/views.py
@@ -0,0 +1,363 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Test cases for Objectapp's views"""
+from datetime import datetime
+
+from django.conf import settings
+from django.test import TestCase
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.template import TemplateDoesNotExist
+from django.utils.translation import ugettext_lazy as _
+
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.managers import PUBLISHED
+from objectapp.settings import PAGINATION
+
+
+class ViewsBaseCase(TestCase):
+ """
+ Setup and utility function base case.
+ """
+
+ def setUp(self):
+ self.old_CONTEXT_PROCESSORS = settings.TEMPLATE_CONTEXT_PROCESSORS
+ self.old_TEMPLATE_LOADERS = settings.TEMPLATE_LOADERS
+ settings.TEMPLATE_LOADERS = (
+ ('django.template.loaders.cached.Loader', (
+ 'django.template.loaders.app_directories.Loader',
+ 'django.template.loaders.eggs.Loader',
+ )
+ ),
+ )
+ settings.TEMPLATE_CONTEXT_PROCESSORS = (
+ 'django.core.context_processors.request',
+ )
+
+ self.site = Site.objects.get_current()
+ self.author = User.objects.create_superuser(
+ username='admin', email='admin@example.com', password='password')
+ self.Objecttype = Objecttype.objects.create(title='Tests', slug='tests')
+ params = {'title': 'Test 1',
+ 'content': 'First test gbobject published',
+ 'slug': 'test-1',
+ 'tags': 'tests',
+ 'creation_date': datetime(2010, 1, 1),
+ 'status': PUBLISHED}
+ gbobject = Gbobject.objects.create(**params)
+ gbobject.sites.add(self.site)
+ gbobject.objecttypes.add(self.Objecttype)
+ gbobject.authors.add(self.author)
+
+ params = {'title': 'Test 2',
+ 'content': 'Second test gbobject published',
+ 'slug': 'test-2',
+ 'tags': 'tests',
+ 'creation_date': datetime(2010, 6, 1),
+ 'status': PUBLISHED}
+ gbobject = Gbobject.objects.create(**params)
+ gbobject.sites.add(self.site)
+ gbobject.objecttypes.add(self.Objecttype)
+ gbobject.authors.add(self.author)
+
+ def tearDown(self):
+ settings.TEMPLATE_CONTEXT_PROCESSORS = self.old_CONTEXT_PROCESSORS
+ settings.TEMPLATE_LOADERS = self.old_TEMPLATE_LOADERS
+
+ def create_published_gbobject(self):
+ params = {'title': 'My test gbobject',
+ 'content': 'My test content',
+ 'slug': 'my-test-gbobject',
+ 'tags': 'tests',
+ 'creation_date': datetime(2010, 1, 1),
+ 'status': PUBLISHED}
+ gbobject = Gbobject.objects.create(**params)
+ gbobject.sites.add(self.site)
+ gbobject.objecttypes.add(self.Objecttype)
+ gbobject.authors.add(self.author)
+ return gbobject
+
+ def check_publishing_context(self, url, first_expected,
+ second_expected=None):
+ """Test the numbers of gbobjects in context of an url,"""
+ response = self.client.get(url)
+ self.assertEquals(len(response.context['object_list']), first_expected)
+ if second_expected:
+ self.create_published_gbobject()
+ response = self.client.get(url)
+ self.assertEquals(
+ len(response.context['object_list']), second_expected)
+ return response
+
+
+class ObjectappViewsTestCase(ViewsBaseCase):
+ """
+ Test cases for generic views used in the application,
+ for reproducing and correcting issue :
+ http://github.com/Fantomas42/django-blog-objectapp/issues#issue/3
+ """
+ urls = 'objectapp.tests.urls'
+
+ def test_objectapp_gbobject_archive_index(self):
+ self.check_publishing_context('/', 2, 3)
+
+ def test_objectapp_gbobject_archive_year(self):
+ self.check_publishing_context('/2010/', 2, 3)
+
+ def test_objectapp_gbobject_archive_month(self):
+ self.check_publishing_context('/2010/01/', 1, 2)
+
+ def test_objectapp_gbobject_archive_day(self):
+ self.check_publishing_context('/2010/01/01/', 1, 2)
+
+ def test_objectapp_gbobject_shortlink(self):
+ response = self.client.get('/1/', follow=True)
+ self.assertEquals(response.redirect_chain,
+ [('http://testserver/2010/01/01/test-1/', 301)])
+
+ def test_objectapp_gbobject_detail(self):
+ gbobject = self.create_published_gbobject()
+ gbobject.sites.clear()
+ # Check a 404 error, but the 404.html may no exist
+ try:
+ self.assertRaises(TemplateDoesNotExist, self.client.get,
+ '/2010/01/01/my-test-gbobject/')
+ except AssertionError:
+ response = self.client.get('/2010/01/01/my-test-gbobject/')
+ self.assertEquals(response.status_code, 404)
+
+ gbobject.template = 'objectapp/_gbobject_detail.html'
+ gbobject.save()
+ gbobject.sites.add(Site.objects.get_current())
+ response = self.client.get('/2010/01/01/my-test-gbobject/')
+ self.assertEquals(response.status_code, 200)
+ self.assertTemplateUsed(response, 'objectapp/_gbobject_detail.html')
+
+ def test_objectapp_gbobject_detail_login(self):
+ gbobject = self.create_published_gbobject()
+ gbobject.login_required = True
+ gbobject.save()
+ response = self.client.get('/2010/01/01/my-test-gbobject/')
+ self.assertTemplateUsed(response, 'objectapp/login.html')
+
+ def test_objectapp_gbobject_detail_password(self):
+ gbobject = self.create_published_gbobject()
+ gbobject.password = 'password'
+ gbobject.save()
+ response = self.client.get('/2010/01/01/my-test-gbobject/')
+ self.assertTemplateUsed(response, 'objectapp/password.html')
+ self.assertEquals(response.context['error'], False)
+ response = self.client.post('/2010/01/01/my-test-gbobject/',
+ {'password': 'bad_password'})
+ self.assertTemplateUsed(response, 'objectapp/password.html')
+ self.assertEquals(response.context['error'], True)
+ response = self.client.post('/2010/01/01/my-test-gbobject/',
+ {'password': 'password'})
+ self.assertEquals(response.status_code, 302)
+
+ def test_objectapp_gbobject_channel(self):
+ self.check_publishing_context('/channel-test/', 2, 3)
+
+ def test_objectapp_Objecttype_list(self):
+ self.check_publishing_context('/objecttypes/', 1)
+ gbobject = Gbobject.objects.all()[0]
+ gbobject.objecttypes.add(Objecttype.objects.create(title='New Objecttype',
+ slug='new-Objecttype'))
+ self.check_publishing_context('/objecttypes/', 2)
+
+ def test_objectapp_Objecttype_detail(self):
+ response = self.check_publishing_context('/objecttypes/tests/', 2, 3)
+ self.assertTemplateUsed(response, 'objectapp/Objecttype/gbobject_list.html')
+ self.assertEquals(response.context['Objecttype'].slug, 'tests')
+
+ def test_objectapp_Objecttype_detail_paginated(self):
+ """Test case reproducing issue #42 on Objecttype
+ detail view paginated"""
+ for i in range(PAGINATION):
+ params = {'title': 'My gbobject %i' % i,
+ 'content': 'My content %i' % i,
+ 'slug': 'my-gbobject-%i' % i,
+ 'creation_date': datetime(2010, 1, 1),
+ 'status': PUBLISHED}
+ gbobject = Gbobject.objects.create(**params)
+ gbobject.sites.add(self.site)
+ gbobject.objecttypes.add(self.Objecttype)
+ gbobject.authors.add(self.author)
+ response = self.client.get('/objecttypes/tests/')
+ self.assertEquals(len(response.context['object_list']), PAGINATION)
+ response = self.client.get('/objecttypes/tests/?page=2')
+ self.assertEquals(len(response.context['object_list']), 2)
+ response = self.client.get('/objecttypes/tests/page/2/')
+ self.assertEquals(len(response.context['object_list']), 2)
+ self.assertEquals(response.context['Objecttype'].slug, 'tests')
+
+ def test_objectapp_author_list(self):
+ self.check_publishing_context('/authors/', 1)
+ gbobject = Gbobject.objects.all()[0]
+ gbobject.authors.add(User.objects.create(username='new-user',
+ email='new_user@example.com'))
+ self.check_publishing_context('/authors/', 2)
+
+ def test_objectapp_author_detail(self):
+ response = self.check_publishing_context('/authors/admin/', 2, 3)
+ self.assertTemplateUsed(response, 'objectapp/author/gbobject_list.html')
+ self.assertEquals(response.context['author'].username, 'admin')
+
+ def test_objectapp_tag_list(self):
+ self.check_publishing_context('/tags/', 1)
+ gbobject = Gbobject.objects.all()[0]
+ gbobject.tags = 'tests, tag'
+ gbobject.save()
+ self.check_publishing_context('/tags/', 2)
+
+ def test_objectapp_tag_detail(self):
+ response = self.check_publishing_context('/tags/tests/', 2, 3)
+ self.assertTemplateUsed(response, 'objectapp/tag/gbobject_list.html')
+ self.assertEquals(response.context['tag'].name, 'tests')
+
+ def test_objectapp_gbobject_search(self):
+ self.check_publishing_context('/search/?pattern=test', 2, 3)
+ response = self.client.get('/search/?pattern=ab')
+ self.assertEquals(len(response.context['object_list']), 0)
+ self.assertEquals(response.context['error'],
+ _('The pattern is too short'))
+ response = self.client.get('/search/')
+ self.assertEquals(len(response.context['object_list']), 0)
+ self.assertEquals(response.context['error'],
+ _('No pattern to search found'))
+
+ def test_objectapp_sitemap(self):
+ response = self.client.get('/sitemap/')
+ self.assertEquals(len(response.context['gbobjects']), 2)
+ self.assertEquals(len(response.context['objecttypes']), 1)
+ gbobject = self.create_published_gbobject()
+ gbobject.objecttypes.add(Objecttype.objects.create(title='New Objecttype',
+ slug='new-Objecttype'))
+ response = self.client.get('/sitemap/')
+ self.assertEquals(len(response.context['gbobjects']), 3)
+ self.assertEquals(len(response.context['objecttypes']), 2)
+
+ def test_objectapp_trackback(self):
+ # Check a 404 error, but the 404.html may no exist
+ try:
+ self.assertRaises(TemplateDoesNotExist, self.client.post,
+ '/trackback/404/')
+ except AssertionError:
+ response = self.client.post('/trackback/404/')
+ self.assertEquals(response.status_code, 404)
+ self.assertEquals(
+ self.client.post('/trackback/1/').status_code, 301)
+ self.assertEquals(
+ self.client.get('/trackback/1/').status_code, 301)
+ gbobject = Gbobject.objects.get(slug='test-1')
+ gbobject.pingback_enabled = False
+ gbobject.save()
+ self.assertEquals(
+ self.client.post('/trackback/1/',
+ {'url': 'http://example.com'}).content,
+ '<?xml version="1.0" encoding="utf-8"?>\n<response>\n \n '
+ '<error>1</error>\n <message>Trackback is not enabled for '
+ 'Test 1</message>\n \n</response>\n')
+ gbobject.pingback_enabled = True
+ gbobject.save()
+ self.assertEquals(
+ self.client.post('/trackback/1/',
+ {'url': 'http://example.com'}).content,
+ '<?xml version="1.0" encoding="utf-8"?>\n<response>\n \n '
+ '<error>0</error>\n \n</response>\n')
+ self.assertEquals(
+ self.client.post('/trackback/1/',
+ {'url': 'http://example.com'}).content,
+ '<?xml version="1.0" encoding="utf-8"?>\n<response>\n \n '
+ '<error>1</error>\n <message>Trackback is already registered'
+ '</message>\n \n</response>\n')
+
+
+class ObjectappCustomDetailViews(ViewsBaseCase):
+ """
+ Tests with an alternate urls.py that modifies how author_detail,
+ tags_detail and objecttypes_detail views to be called with a custom
+ template_name keyword argument and an extra_context.
+ """
+ urls = 'objectapp.tests.custom_views_detail_urls'
+
+ def test_custom_Objecttype_detail(self):
+ response = self.check_publishing_context('/objecttypes/tests/', 2, 3)
+ self.assertTemplateUsed(response, 'objectapp/gbobject_list.html')
+ self.assertEquals(response.context['Objecttype'].slug, 'tests')
+ self.assertEquals(response.context['extra'], 'context')
+
+ def test_custom_author_detail(self):
+ response = self.check_publishing_context('/authors/admin/', 2, 3)
+ self.assertTemplateUsed(response, 'objectapp/gbobject_list.html')
+ self.assertEquals(response.context['author'].username, 'admin')
+ self.assertEquals(response.context['extra'], 'context')
+
+ def test_custom_tag_detail(self):
+ response = self.check_publishing_context('/tags/tests/', 2, 3)
+ self.assertTemplateUsed(response, 'objectapp/gbobject_list.html')
+ self.assertEquals(response.context['tag'].name, 'tests')
+ self.assertEquals(response.context['extra'], 'context')
diff --git a/objectapp/testsettings.py b/objectapp/testsettings.py
new file mode 100644
index 0000000..a69af6e
--- /dev/null
+++ b/objectapp/testsettings.py
@@ -0,0 +1,103 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Settings for testing objectapp"""
+import os
+from objectapp.xmlrpc import OBJECTAPP_XMLRPC_METHODS
+
+DATABASES = {'default': {'NAME': 'objectapp_tests.db',
+ 'ENGINE': 'django.db.backends.sqlite3'}}
+
+SITE_ID = 1
+
+STATIC_URL = '/static/'
+
+ROOT_URLCONF = 'objectapp.tests.urls'
+
+TEMPLATE_CONTEXT_PROCESSORS = [
+ 'django.core.context_processors.request',
+ 'objectapp.context_processors.version']
+
+TEMPLATE_LOADERS = [
+ ['django.template.loaders.cached.Loader', [
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader']
+ ]
+ ]
+
+TEMPLATE_DIRS = [os.path.join(os.path.dirname(__file__),
+ 'tests', 'templates')]
+
+INSTALLED_APPS = ['django.contrib.contenttypes',
+ 'django.contrib.comments',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django_xmlrpc',
+ 'mptt', 'tagging', 'objectapp']
+
+OBJECTAPP_PAGINATION = 3
+
+XMLRPC_METHODS = OBJECTAPP_XMLRPC_METHODS
diff --git a/objectapp/url_shortener/__init__.py b/objectapp/url_shortener/__init__.py
new file mode 100644
index 0000000..6b5f98d
--- /dev/null
+++ b/objectapp/url_shortener/__init__.py
@@ -0,0 +1,88 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Url shortener for Objectapp"""
+import warnings
+
+from django.utils.importlib import import_module
+from django.core.exceptions import ImproperlyConfigured
+
+from objectapp.settings import URL_SHORTENER_BACKEND
+from objectapp.url_shortener.backends.default import backend as default_backend
+
+
+def get_url_shortener():
+ """Return the selected url shortener backend"""
+ try:
+ backend_module = import_module(URL_SHORTENER_BACKEND)
+ backend = getattr(backend_module, 'backend')
+ except (ImportError, AttributeError):
+ warnings.warn('%s backend cannot be imported' % URL_SHORTENER_BACKEND,
+ RuntimeWarning)
+ backend = default_backend
+ except ImproperlyConfigured, e:
+ warnings.warn(str(e), RuntimeWarning)
+ backend = default_backend
+
+ return backend
diff --git a/objectapp/url_shortener/backends/__init__.py b/objectapp/url_shortener/backends/__init__.py
new file mode 100644
index 0000000..83a6d7e
--- /dev/null
+++ b/objectapp/url_shortener/backends/__init__.py
@@ -0,0 +1 @@
+"""Shortlink backends for Objectapp"""
diff --git a/objectapp/url_shortener/backends/bitly.py b/objectapp/url_shortener/backends/bitly.py
new file mode 100644
index 0000000..fda2a84
--- /dev/null
+++ b/objectapp/url_shortener/backends/bitly.py
@@ -0,0 +1,84 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Bit.ly url shortener backend for Objectapp"""
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+
+try:
+ from django_bitly.models import Bittle
+except ImportError:
+ raise ImproperlyConfigured('django_bitly is not available')
+
+if not getattr(settings, 'BITLY_LOGIN', ''):
+ raise ImproperlyConfigured('You have to set a BITLY_LOGIN setting')
+
+if not getattr(settings, 'BITLY_API_KEY', ''):
+ raise ImproperlyConfigured('You have to set a BITLY_API_KEY setting')
+
+
+def backend(gbobject):
+ """Bit.ly url shortener backend for Objectapp"""
+ bittle = Bittle.objects.bitlify(gbobject)
+ return bittle.shortUrl
diff --git a/objectapp/url_shortener/backends/default.py b/objectapp/url_shortener/backends/default.py
new file mode 100644
index 0000000..544dd0b
--- /dev/null
+++ b/objectapp/url_shortener/backends/default.py
@@ -0,0 +1,75 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Default url shortener backend for Objectapp"""
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import reverse
+
+from objectapp.settings import PROTOCOL
+
+
+def backend(gbobject):
+ """Default url shortener backend for Objectapp"""
+ return '%s://%s%s' % (PROTOCOL, Site.objects.get_current().domain,
+ reverse('objectapp_gbobject_shortlink', args=[gbobject.pk]))
diff --git a/objectapp/urls/__init__.py b/objectapp/urls/__init__.py
new file mode 100644
index 0000000..2ba09ff
--- /dev/null
+++ b/objectapp/urls/__init__.py
@@ -0,0 +1,84 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Defaults urls for the Objectapp project"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import include
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns(
+ '',
+ url(r'^tags/', include('objectapp.urls.tags',)),
+ url(r'^feeds/', include('objectapp.urls.feeds')),
+ url(r'^authors/', include('objectapp.urls.authors')),
+ url(r'^objecttypes/', include('objectapp.urls.objecttypes')),
+ url(r'^search/', include('objectapp.urls.search')),
+ url(r'^sitemap/', include('objectapp.urls.sitemap')),
+ url(r'^trackback/', include('objectapp.urls.trackback')),
+ url(r'^discussions/', include('objectapp.urls.discussions')),
+ url(r'^add/', include('objectapp.urls.add')),
+ url(r'^', include('objectapp.urls.quick_gbobject')),
+ url(r'^', include('objectapp.urls.capabilities')),
+ url(r'^', include('objectapp.urls.gbobjects')),
+ )
diff --git a/objectapp/urls/add.py b/objectapp/urls/add.py
new file mode 100644
index 0000000..77de348
--- /dev/null
+++ b/objectapp/urls/add.py
@@ -0,0 +1,78 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for Objectapp forms"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('objectapp.views.add',
+ url(r'^gbobject/$', 'addgbobject',
+ name='objectapp_add_gbobject'),
+ url(r'^process/$', 'addprocess',
+ name='objectapp_add_gbobject'),
+ url(r'^system/$', 'addsystem',
+ name='objectapp_add_system'),
+
+ )
+
diff --git a/objectapp/urls/authors.py b/objectapp/urls/authors.py
new file mode 100644
index 0000000..6e23729
--- /dev/null
+++ b/objectapp/urls/authors.py
@@ -0,0 +1,78 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp authors"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+
+urlpatterns = patterns('objectapp.views.authors',
+ url(r'^$', 'author_list',
+ name='objectapp_author_list'),
+ url(r'^(?P<username>[.+-@\w]+)/$', 'author_detail',
+ name='objectapp_author_detail'),
+ url(r'^(?P<username>[.+-@\w]+)/page/(?P<page>\d+)/$',
+ 'author_detail',
+ name='objectapp_author_detail_paginated'),
+ )
diff --git a/objectapp/urls/capabilities.py b/objectapp/urls/capabilities.py
new file mode 100644
index 0000000..e271762
--- /dev/null
+++ b/objectapp/urls/capabilities.py
@@ -0,0 +1,98 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the objectapp capabilities"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+from django.contrib.sites.models import Site
+
+from objectapp.settings import PROTOCOL
+from objectapp.settings import COPYRIGHT
+from objectapp.settings import FEEDS_FORMAT
+
+extra_context = {'protocol': PROTOCOL,
+ 'site': Site.objects.get_current()}
+
+extra_context_opensearch = extra_context.copy()
+extra_context_opensearch.update({'copyright': COPYRIGHT,
+ 'feeds_format': FEEDS_FORMAT})
+
+urlpatterns = patterns('django.views.generic.simple',
+ url(r'^rsd.xml$', 'direct_to_template',
+ {'template': 'objectapp/rsd.xml',
+ 'mimetype': 'application/rsd+xml',
+ 'extra_context': extra_context},
+ name='objectapp_rsd'),
+ url(r'^wlwmanifest.xml$', 'direct_to_template',
+ {'template': 'objectapp/wlwmanifest.xml',
+ 'mimetype': 'application/wlwmanifest+xml',
+ 'extra_context': extra_context},
+ name='objectapp_wlwmanifest'),
+ url(r'^opensearch.xml$', 'direct_to_template',
+ {'template': 'objectapp/opensearch.xml',
+ 'mimetype':
+ 'application/opensearchdescription+xml',
+ 'extra_context': extra_context_opensearch},
+ name='objectapp_opensearch'),
+ )
diff --git a/objectapp/urls/discussions.py b/objectapp/urls/discussions.py
new file mode 100644
index 0000000..759c7b0
--- /dev/null
+++ b/objectapp/urls/discussions.py
@@ -0,0 +1,73 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp discussions"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('django.views.generic.simple',
+ url(r'^success/$', 'direct_to_template',
+ {'template': 'comments/objectapp/gbobject/posted.html'},
+ name='objectapp_discussion_success'),
+ )
diff --git a/objectapp/urls/feeds.py b/objectapp/urls/feeds.py
new file mode 100644
index 0000000..5e6f47d
--- /dev/null
+++ b/objectapp/urls/feeds.py
@@ -0,0 +1,113 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp feeds"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+from objectapp.feeds import LatestGbobjects
+from objectapp.feeds import GbobjectDiscussions
+from objectapp.feeds import GbobjectComments
+from objectapp.feeds import GbobjectTrackbacks
+from objectapp.feeds import GbobjectPingbacks
+from objectapp.feeds import SearchGbobjects
+from objectapp.feeds import TagGbobjects
+from objectapp.feeds import ObjecttypeGbobjects
+from objectapp.feeds import AuthorGbobjects
+
+
+urlpatterns = patterns(
+ '',
+ url(r'^latest/$',
+ LatestGbobjects(),
+ name='objectapp_gbobject_latest_feed'),
+ url(r'^search/$',
+ SearchGbobjects(),
+ name='objectapp_gbobject_search_feed'),
+ url(r'^tags/(?P<slug>[- \w]+)/$',
+ TagGbobjects(),
+ name='objectapp_tag_feed'),
+ url(r'^authors/(?P<username>[.+-@\w]+)/$',
+ AuthorGbobjects(),
+ name='objectapp_author_feed'),
+ url(r'^objecttypes/(?P<path>[-\/\w]+)/$',
+ ObjecttypeGbobjects(),
+ name='objectapp_Objecttype_feed'),
+ url(r'^discussions/(?P<year>\d{4})/(?P<month>\d{2})/' \
+ '(?P<day>\d{2})/(?P<slug>[-\w]+)/$',
+ GbobjectDiscussions(),
+ name='objectapp_gbobject_discussion_feed'),
+ url(r'^comments/(?P<year>\d{4})/(?P<month>\d{2})/' \
+ '(?P<day>\d{2})/(?P<slug>[-\w]+)/$',
+ GbobjectComments(),
+ name='objectapp_gbobject_comment_feed'),
+ url(r'^pingbacks/(?P<year>\d{4})/(?P<month>\d{2})/' \
+ '(?P<day>\d{2})/(?P<slug>[-\w]+)/$',
+ GbobjectPingbacks(),
+ name='objectapp_gbobject_pingback_feed'),
+ url(r'^trackbacks/(?P<year>\d{4})/(?P<month>\d{2})/' \
+ '(?P<day>\d{2})/(?P<slug>[-\w]+)/$',
+ GbobjectTrackbacks(),
+ name='objectapp_gbobject_trackback_feed'),
+ )
diff --git a/objectapp/urls/gbobjects.py b/objectapp/urls/gbobjects.py
new file mode 100644
index 0000000..565a20b
--- /dev/null
+++ b/objectapp/urls/gbobjects.py
@@ -0,0 +1,114 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp gbobjects"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+from objectapp.models import Gbobject
+from objectapp.settings import PAGINATION
+from objectapp.settings import ALLOW_EMPTY
+from objectapp.settings import ALLOW_FUTURE
+
+gbobject_conf_index = {'paginate_by': PAGINATION,
+ 'template_name': 'objectapp/gbobject_archive.html'}
+
+gbobject_conf = {'date_field': 'creation_date',
+ 'allow_empty': ALLOW_EMPTY,
+ 'allow_future': ALLOW_FUTURE,
+ 'month_format': '%m'}
+
+gbobject_conf_year = gbobject_conf.copy()
+gbobject_conf_year['make_object_list'] = True
+del gbobject_conf_year['month_format']
+
+gbobject_conf_detail = gbobject_conf.copy()
+del gbobject_conf_detail['allow_empty']
+gbobject_conf_detail['queryset'] = Gbobject.published.on_site()
+
+
+urlpatterns = patterns(
+ 'objectapp.views.gbobjects',
+ url(r'^$',
+ 'gbobject_index', gbobject_conf_index,
+ name='objectapp_gbobject_archive_index'),
+ url(r'^page/(?P<page>\d+)/$',
+ 'gbobject_index', gbobject_conf_index,
+ name='objectapp_gbobject_archive_index_paginated'),
+ url(r'^(?P<year>\d{4})/$',
+ 'gbobject_year', gbobject_conf_year,
+ name='objectapp_gbobject_archive_year'),
+ url(r'^(?P<year>\d{4})/(?P<month>\d{2})/$',
+ 'gbobject_month', gbobject_conf,
+ name='objectapp_gbobject_archive_month'),
+ url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$',
+ 'gbobject_day', gbobject_conf,
+ name='objectapp_gbobject_archive_day'),
+ url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[-\w]+)/$',
+ 'gbobject_detail', gbobject_conf_detail,
+ name='objectapp_gbobject_detail'),
+ url(r'^(?P<object_id>\d+)/$',
+ 'gbobject_shortlink',
+ name='objectapp_gbobject_shortlink'),
+ )
diff --git a/objectapp/urls/objecttypes.py b/objectapp/urls/objecttypes.py
new file mode 100644
index 0000000..212a46a
--- /dev/null
+++ b/objectapp/urls/objecttypes.py
@@ -0,0 +1,84 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp objecttypes"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+from objectapp.models import Objecttype
+
+Objecttype_conf = {'queryset': Objecttype.tree.all()}
+
+urlpatterns = patterns('django.views.generic.list_detail',
+ url(r'^$', 'object_list',
+ Objecttype_conf, 'objectapp_Objecttype_list'),
+ )
+
+urlpatterns += patterns('objectapp.views.objecttypes',
+ url(r'^(?P<path>[-\/\w]+)/page/(?P<page>\d+)/$',
+ 'Objecttype_detail',
+ name='objectapp_Objecttype_detail_paginated'),
+ url(r'^(?P<path>[-\/\w]+)/$', 'Objecttype_detail',
+ name='objectapp_Objecttype_detail'),
+ )
diff --git a/objectapp/urls/quick_gbobject.py b/objectapp/urls/quick_gbobject.py
new file mode 100644
index 0000000..8ff5956
--- /dev/null
+++ b/objectapp/urls/quick_gbobject.py
@@ -0,0 +1,72 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Url for the Objectapp quick gbobject view"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('objectapp.views.quick_gbobject',
+ url(r'^quick_gbobject/$', 'view_quick_gbobject',
+ name='objectapp_gbobject_quick_post')
+ )
diff --git a/objectapp/urls/search.py b/objectapp/urls/search.py
new file mode 100644
index 0000000..afe2bde
--- /dev/null
+++ b/objectapp/urls/search.py
@@ -0,0 +1,71 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp search"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('objectapp.views.search',
+ url(r'^$', 'gbobject_search', name='objectapp_gbobject_search'),
+ )
diff --git a/objectapp/urls/sitemap.py b/objectapp/urls/sitemap.py
new file mode 100644
index 0000000..019d833
--- /dev/null
+++ b/objectapp/urls/sitemap.py
@@ -0,0 +1,73 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp sitemap"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('objectapp.views.sitemap',
+ url(r'^$', 'sitemap',
+ {'template': 'objectapp/sitemap.html'},
+ name='objectapp_sitemap'),
+ )
diff --git a/objectapp/urls/tags.py b/objectapp/urls/tags.py
new file mode 100644
index 0000000..340a09e
--- /dev/null
+++ b/objectapp/urls/tags.py
@@ -0,0 +1,76 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp tags"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('objectapp.views.tags',
+ url(r'^$', 'tag_list',
+ name='objectapp_tag_list'),
+ url(r'^(?P<tag>[^/]+(?u))/$', 'tag_detail',
+ name='objectapp_tag_detail'),
+ url(r'^(?P<tag>[^/]+(?u))/page/(?P<page>\d+)/$',
+ 'tag_detail', name='objectapp_tag_detail_paginated'),
+ )
diff --git a/objectapp/urls/trackback.py b/objectapp/urls/trackback.py
new file mode 100644
index 0000000..b78f1b5
--- /dev/null
+++ b/objectapp/urls/trackback.py
@@ -0,0 +1,72 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Urls for the Objectapp trackback"""
+from django.conf.urls.defaults import url
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('objectapp.views.trackback',
+ url(r'^(?P<object_id>\d+)/$', 'gbobject_trackback',
+ name='objectapp_gbobject_trackback'),
+ )
diff --git a/objectapp/views/__init__.py b/objectapp/views/__init__.py
new file mode 100644
index 0000000..a16657d
--- /dev/null
+++ b/objectapp/views/__init__.py
@@ -0,0 +1 @@
+"""Views for Objectapp"""
diff --git a/objectapp/views/add.py b/objectapp/views/add.py
new file mode 100644
index 0000000..362c3d3
--- /dev/null
+++ b/objectapp/views/add.py
@@ -0,0 +1,126 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from django.http import HttpResponse
+from django.http import HttpResponseRedirect
+from django.template import RequestContext
+from django.shortcuts import render_to_response
+from datetime import datetime
+from objectapp.forms import *
+
+
+def addgbobject(request):
+ if request.method == 'POST':
+ formset = GbobjectForm(request.POST)
+ if formset.is_valid():
+ formset.save()
+ return HttpResponseRedirect("/objects/")
+
+
+
+ else:
+
+ formset = GbobjectForm()
+
+
+ variables = RequestContext(request,{'formset':formset})
+ template = "objectappforms/gbobjectform.html"
+ return render_to_response(template, variables)
+
+
+def addprocess(request):
+ if request.method == 'POST':
+ formset = ProcessForm(request.POST)
+ if formset.is_valid():
+ formset.save()
+ return HttpResponseRedirect("/objects/")
+
+
+
+ else:
+
+ formset = ProcessForm()
+
+
+ variables = RequestContext(request,{'formset':formset})
+ template = "objectappforms/processform.html"
+ return render_to_response(template, variables)
+
+def addsystem(request):
+ if request.method == 'POST':
+ formset = ProcessForm(request.POST)
+ if formset.is_valid():
+ formset.save()
+ return HttpResponseRedirect("/objects/")
+
+
+
+ else:
+
+ formset = SystemForm()
+
+
+ variables = RequestContext(request,{'formset':formset})
+ template = "objectappforms/systemform.html"
+ return render_to_response(template, variables)
diff --git a/objectapp/views/authors.py b/objectapp/views/authors.py
new file mode 100644
index 0000000..a6b7745
--- /dev/null
+++ b/objectapp/views/authors.py
@@ -0,0 +1,92 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Views for Objectapp authors"""
+from django.shortcuts import get_object_or_404
+from django.views.generic.list_detail import object_list
+
+from objectapp.models import Author
+from objectapp.settings import PAGINATION
+from objectapp.views.decorators import update_queryset
+from objectapp.views.decorators import template_name_for_gbobject_queryset_filtered
+
+
+author_list = update_queryset(object_list, Author.published.all)
+
+
+def author_detail(request, username, page=None, **kwargs):
+ """Display the gbobjects of an author"""
+ extra_context = kwargs.pop('extra_context', {})
+
+ author = get_object_or_404(Author, username=username)
+ if not kwargs.get('template_name'):
+ kwargs['template_name'] = template_name_for_gbobject_queryset_filtered(
+ 'author', author.username)
+
+ extra_context.update({'author': author})
+ kwargs['extra_context'] = extra_context
+
+ return object_list(request, queryset=author.gbobjects_published(),
+ paginate_by=PAGINATION, page=page,
+ **kwargs)
diff --git a/objectapp/views/channels.py b/objectapp/views/channels.py
new file mode 100644
index 0000000..bc842a0
--- /dev/null
+++ b/objectapp/views/channels.py
@@ -0,0 +1,58 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Views for Objectapp channels"""
+from django.views.generic.list_detail import object_list
+
+from objectapp.models import Gbobject
+
+
+def gbobject_channel(request, query, *ka, **kw):
+ """Display a custom selection of gbobjects"""
+ queryset = Gbobject.published.search(query)
+ return object_list(request, queryset=queryset,
+ *ka, **kw)
diff --git a/objectapp/views/decorators.py b/objectapp/views/decorators.py
new file mode 100644
index 0000000..cdc7203
--- /dev/null
+++ b/objectapp/views/decorators.py
@@ -0,0 +1,151 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Decorators for objectapp.views"""
+from functools import wraps
+
+from django.template import RequestContext
+from django.contrib.auth.views import login
+from django.shortcuts import redirect
+from django.shortcuts import get_object_or_404
+from django.shortcuts import render_to_response
+from django.template.loader import get_template
+from django.template import TemplateDoesNotExist
+from django.views.decorators.csrf import csrf_protect
+from django.views.decorators.cache import never_cache
+
+
+def update_queryset(view, queryset,
+ queryset_parameter='queryset'):
+ """Decorator around views based on a queryset
+ passed in parameter, who will force the update
+ of the queryset before executing the view.
+ Related to issue http://code.djangoproject.com/ticket/8378"""
+
+ @wraps(view)
+ def wrapper(*args, **kwargs):
+ """Regenerate the queryset before passing it to the view."""
+ kwargs[queryset_parameter] = queryset()
+ return view(*args, **kwargs)
+
+ return wrapper
+
+
+@csrf_protect
+@never_cache
+def password(request, gbobject):
+ """Displays the password form and handle validation
+ by setting the valid password in a cookie."""
+ error = False
+ if request.method == 'POST':
+ if request.POST.get('password') == gbobject.password:
+ request.session[
+ 'objectapp_gbobject_%s_password' % gbobject.pk] = gbobject.password
+ return redirect(gbobject)
+ error = True
+ return render_to_response('objectapp/password.html', {'error': error},
+ context_instance=RequestContext(request))
+
+
+def protect_gbobject(view):
+ """Decorator performing a security check if needed
+ around the generic.date_based.gbobject_detail view
+ and specify the template used to render the gbobject"""
+
+ @wraps(view)
+ def wrapper(*ka, **kw):
+ """Do security check and retrieve the template"""
+ request = ka[0]
+ gbobject = get_object_or_404(kw['queryset'], slug=kw['slug'],
+ creation_date__year=kw['year'],
+ creation_date__month=kw['month'],
+ creation_date__day=kw['day'])
+
+ if gbobject.login_required and not request.user.is_authenticated():
+ return login(request, 'objectapp/login.html')
+ if gbobject.password and gbobject.password != \
+ request.session.get('objectapp_gbobject_%s_password' % gbobject.pk):
+ return password(request, gbobject)
+ kw['template_name'] = gbobject.template
+ return view(*ka, **kw)
+
+ return wrapper
+
+
+def template_name_for_gbobject_queryset_filtered(model_type, model_name):
+ """Return a custom template name for views
+ returning a queryset of Gbobject filtered by another model."""
+ template_name_list = (
+ 'objectapp/%s/%s/gbobject_list.html' % (model_type, model_name),
+ 'objectapp/%s/%s_gbobject_list.html' % (model_type, model_name),
+ 'objectapp/%s/gbobject_list.html' % model_type,
+ 'objectapp/gbobject_list.html')
+
+ for template_name in template_name_list:
+ try:
+ get_template(template_name)
+ return template_name
+ except TemplateDoesNotExist:
+ continue
diff --git a/objectapp/views/gbobjects.py b/objectapp/views/gbobjects.py
new file mode 100644
index 0000000..9662c97
--- /dev/null
+++ b/objectapp/views/gbobjects.py
@@ -0,0 +1,99 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+#
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Views for Objectapp gbobjects"""
+from django.shortcuts import redirect
+from django.shortcuts import get_object_or_404
+from django.views.generic.list_detail import object_list
+from django.views.generic.date_based import archive_year
+from django.views.generic.date_based import archive_month
+from django.views.generic.date_based import archive_day
+from django.views.generic.date_based import object_detail
+
+from objectapp.models import Gbobject
+from objectapp.views.decorators import protect_gbobject
+from objectapp.views.decorators import update_queryset
+
+
+gbobject_index = update_queryset(object_list, Gbobject.published.all)
+
+gbobject_year = update_queryset(archive_year, Gbobject.published.all)
+
+gbobject_month = update_queryset(archive_month, Gbobject.published.all)
+
+gbobject_day = update_queryset(archive_day, Gbobject.published.all)
+
+gbobject_detail = protect_gbobject(object_detail)
+
+
+def gbobject_shortlink(request, object_id):
+ """
+ Redirect to the 'get_absolute_url' of an Gbobject,
+ accordingly to 'object_id' argument
+ """
+ gbobject = get_object_or_404(Gbobject, pk=object_id)
+ return redirect(gbobject, permanent=True)
diff --git a/objectapp/views/objecttypes.py b/objectapp/views/objecttypes.py
new file mode 100644
index 0000000..f64704f
--- /dev/null
+++ b/objectapp/views/objecttypes.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Views for Objectapp objecttypes"""
+from django.shortcuts import get_object_or_404
+from django.views.generic.list_detail import object_list
+
+from objectapp.models import Objecttype
+from objectapp.settings import PAGINATION
+from objectapp.views.decorators import template_name_for_gbobject_queryset_filtered
+
+
+def get_Objecttype_or_404(path):
+ """Retrieve a Objecttype by a path"""
+ path_bits = [p for p in path.split('/') if p]
+ return get_object_or_404(Objecttype, slug=path_bits[-1])
+
+
+def Objecttype_detail(request, path, page=None, **kwargs):
+ """Display the gbobjects of a Objecttype"""
+ extra_context = kwargs.pop('extra_context', {})
+
+ Objecttype = get_Objecttype_or_404(path)
+ if not kwargs.get('template_name'):
+ kwargs['template_name'] = template_name_for_gbobject_queryset_filtered(
+ 'Objecttype', Objecttype.slug)
+
+ extra_context.update({'Objecttype': Objecttype})
+ kwargs['extra_context'] = extra_context
+
+ return object_list(request, queryset=Objecttype.gbobjects_published(),
+ paginate_by=PAGINATION, page=page,
+ **kwargs)
diff --git a/objectapp/views/quick_gbobject.py b/objectapp/views/quick_gbobject.py
new file mode 100644
index 0000000..0fca2ee
--- /dev/null
+++ b/objectapp/views/quick_gbobject.py
@@ -0,0 +1,118 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Views for Objectapp quick gbobject"""
+from urllib import urlencode
+
+from django import forms
+from django.utils.html import linebreaks
+from django.shortcuts import redirect
+from django.core.urlresolvers import reverse
+from django.contrib.sites.models import Site
+from django.template.defaultfilters import slugify
+from django.utils.encoding import smart_str
+from django.contrib.auth.decorators import permission_required
+
+from objectapp.models import Gbobject
+from objectapp.managers import DRAFT
+from objectapp.managers import PUBLISHED
+
+
+class QuickGbobjectForm(forms.Form):
+ """Form for posting an gbobject quickly"""
+
+ title = forms.CharField(required=True, max_length=255)
+ content = forms.CharField(required=True)
+ tags = forms.CharField(required=False, max_length=255)
+
+
+@permission_required('objectapp.add_gbobject')
+def view_quick_gbobject(request):
+ """View for quickly post an Gbobject"""
+ if request.POST:
+ form = QuickGbobjectForm(request.POST)
+ if form.is_valid():
+ gbobject_dict = form.cleaned_data
+ status = PUBLISHED
+ if 'save_draft' in request.POST:
+ status = DRAFT
+ gbobject_dict['content'] = linebreaks(gbobject_dict['content'])
+ gbobject_dict['slug'] = slugify(gbobject_dict['title'])
+ gbobject_dict['status'] = status
+ gbobject = Gbobject.objects.create(**gbobject_dict)
+ gbobject.sites.add(Site.objects.get_current())
+ gbobject.authors.add(request.user)
+ return redirect(gbobject)
+
+ data = {'title': smart_str(request.POST.get('title', '')),
+ 'content': smart_str(linebreaks(request.POST.get(
+ 'content', ''))),
+ 'tags': smart_str(request.POST.get('tags', '')),
+ 'slug': slugify(request.POST.get('title', '')),
+ 'authors': request.user.pk,
+ 'sites': Site.objects.get_current().pk}
+ return redirect('%s?%s' % (reverse('admin:objectapp_gbobject_add'),
+ urlencode(data)))
+
+ return redirect('admin:objectapp_gbobject_add')
diff --git a/objectapp/views/search.py b/objectapp/views/search.py
new file mode 100644
index 0000000..bb9fe9d
--- /dev/null
+++ b/objectapp/views/search.py
@@ -0,0 +1,91 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Views for Objectapp gbobjects search"""
+from django.utils.translation import ugettext as _
+from django.views.generic.list_detail import object_list
+
+from objectapp.models import Gbobject
+from objectapp.settings import PAGINATION
+
+
+def gbobject_search(request):
+ """Search gbobjects matching with a pattern"""
+ error = None
+ pattern = None
+ gbobjects = Gbobject.published.none()
+
+ if request.GET:
+ pattern = request.GET.get('pattern', '')
+ if len(pattern) < 3:
+ error = _('The pattern is too short')
+ else:
+ gbobjects = Gbobject.published.search(pattern)
+ else:
+ error = _('No pattern to search found')
+
+ return object_list(request, queryset=gbobjects,
+ paginate_by=PAGINATION,
+ template_name='objectapp/gbobject_search.html',
+ extra_context={'error': error,
+ 'pattern': pattern})
diff --git a/objectapp/views/sitemap.py b/objectapp/views/sitemap.py
new file mode 100644
index 0000000..2c9ebbc
--- /dev/null
+++ b/objectapp/views/sitemap.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Views for Objectapp sitemap"""
+from django.views.generic.simple import direct_to_template
+
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+
+
+def sitemap(*ka, **kw):
+ """Wrapper around the direct to template generic view to
+ force the update of the extra context"""
+ kw['extra_context'] = {'gbobjects': Gbobject.published.all(),
+ 'objecttypes': Objecttype.tree.all()}
+ return direct_to_template(*ka, **kw)
diff --git a/objectapp/views/tags.py b/objectapp/views/tags.py
new file mode 100644
index 0000000..33ada35
--- /dev/null
+++ b/objectapp/views/tags.py
@@ -0,0 +1,97 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Views for Objectapp tags"""
+from django.template import RequestContext
+from django.shortcuts import render_to_response
+from django.template.defaultfilters import slugify
+
+from tagging.models import Tag
+from tagging.views import tagged_object_list
+
+from objectapp.models import Gbobject
+from objectapp.settings import PAGINATION
+
+from objectapp.views.decorators import template_name_for_gbobject_queryset_filtered
+
+
+def tag_list(request, template_name='objectapp/tag_list.html'):
+ """Return the list of published tags with counts,
+ try to simulate an object_list view"""
+ tag_list = Tag.objects.usage_for_queryset(
+ Gbobject.published.all(), counts=True)
+ return render_to_response(template_name, {'object_list': tag_list},
+ context_instance=RequestContext(request))
+
+
+def tag_detail(request, tag, page=None, **kwargs):
+ """Display the gbobjects of a tag"""
+ if not kwargs.get('template_name'):
+ kwargs['template_name'] = template_name_for_gbobject_queryset_filtered(
+ 'tag', slugify(tag))
+
+ return tagged_object_list(request, tag=tag,
+ queryset_or_model=Gbobject.published.all(),
+ paginate_by=PAGINATION, page=page,
+ **kwargs)
diff --git a/objectapp/views/trackback.py b/objectapp/views/trackback.py
new file mode 100644
index 0000000..932f575
--- /dev/null
+++ b/objectapp/views/trackback.py
@@ -0,0 +1,109 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Views for Objectapp trackback"""
+from django.shortcuts import redirect
+from django.shortcuts import get_object_or_404
+from django.contrib.sites.models import Site
+from django.contrib import comments
+from django.views.decorators.csrf import csrf_exempt
+from django.contrib.contenttypes.models import ContentType
+from django.views.generic.simple import direct_to_template
+
+from objectapp.models import Gbobject
+
+
+@csrf_exempt
+def gbobject_trackback(request, object_id):
+ """Set a TrackBack for an Gbobject"""
+ gbobject = get_object_or_404(Gbobject.published, pk=object_id)
+
+ if request.POST.get('url'):
+ error = ''
+ url = request.POST['url']
+ site = Site.objects.get_current()
+
+ if not gbobject.pingback_enabled:
+ error = u'Trackback is not enabled for %s' % gbobject.title
+
+ title = request.POST.get('title') or url
+ excerpt = request.POST.get('excerpt') or title
+ blog_name = request.POST.get('blog_name') or title
+
+ if not error:
+ comment, created = comments.get_model().objects.get_or_create(
+ content_type=ContentType.objects.get_for_model(Gbobject),
+ object_pk=gbobject.pk, site=site, user_url=url,
+ user_name=blog_name, defaults={'comment': excerpt})
+ if created:
+ user = gbobject.authors.all()[0]
+ comment.flags.create(user=user, flag='trackback')
+ else:
+ error = u'Trackback is already registered'
+
+ return direct_to_template(request, 'objectapp/gbobject_trackback.xml',
+ mimetype='text/xml',
+ extra_context={'error': error})
+
+ return redirect(gbobject, permanent=True)
diff --git a/objectapp/xmlrpc/__init__.py b/objectapp/xmlrpc/__init__.py
new file mode 100644
index 0000000..29d9e2b
--- /dev/null
+++ b/objectapp/xmlrpc/__init__.py
@@ -0,0 +1,81 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""XML-RPC methods for Objectapp"""
+
+
+OBJECTAPP_XMLRPC_PINGBACK = [
+ ('objectapp.xmlrpc.pingback.pingback_ping',
+ 'pingback.ping'),
+ ('objectapp.xmlrpc.pingback.pingback_extensions_get_pingbacks',
+ 'pingback.extensions.getPingbacks')]
+
+OBJECTAPP_XMLRPC_METAWEBLOG = [
+ ('objectapp.xmlrpc.metaweblog.get_users_blogs',
+ 'blogger.getUsersBlogs'),
+ ('objectapp.xmlrpc.metaweblog.get_user_info',
+ 'blogger.getUserInfo'),
+ ('objectapp.xmlrpc.metaweblog.delete_post',
+ 'blogger.deletePost'),
+ ('objectapp.xmlrpc.metaweblog.get_authors',
+ 'wp.getAuthors'),
+ ('objectapp.xmlrpc.metaweblog.get_objecttypes',
+ 'metaWeblog.getObjecttypes'),
+ ('objectapp.xmlrpc.metaweblog.new_Objecttype',
+ 'wp.newObjecttype'),
+ ('objectapp.xmlrpc.metaweblog.get_recent_posts',
+ 'metaWeblog.getRecentPosts'),
+ ('objectapp.xmlrpc.metaweblog.get_post',
+ 'metaWeblog.getPost'),
+ ('objectapp.xmlrpc.metaweblog.new_post',
+ 'metaWeblog.newPost'),
+ ('objectapp.xmlrpc.metaweblog.edit_post',
+ 'metaWeblog.editPost'),
+ ('objectapp.xmlrpc.metaweblog.new_media_object',
+ 'metaWeblog.newMediaObject')]
+
+OBJECTAPP_XMLRPC_METHODS = OBJECTAPP_XMLRPC_PINGBACK + OBJECTAPP_XMLRPC_METAWEBLOG
diff --git a/objectapp/xmlrpc/metaweblog.py b/objectapp/xmlrpc/metaweblog.py
new file mode 100644
index 0000000..18b2b25
--- /dev/null
+++ b/objectapp/xmlrpc/metaweblog.py
@@ -0,0 +1,344 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""XML-RPC methods of Objectapp metaWeblog API"""
+import os
+from datetime import datetime
+from xmlrpclib import Fault
+from xmlrpclib import DateTime
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import reverse
+from django.utils.translation import gettext as _
+from django.utils.html import strip_tags
+from django.utils.text import truncate_words
+from django.core.files.base import ContentFile
+from django.core.files.storage import default_storage
+from django.template.defaultfilters import slugify
+
+from objectapp.models import Gbobject
+from objectapp.models import Objecttype
+from objectapp.settings import PROTOCOL
+from objectapp.settings import UPLOAD_TO
+from objectapp.managers import DRAFT, PUBLISHED
+from django_xmlrpc.decorators import xmlrpc_func
+
+# http://docs.nucleuscms.org/blog/12#errorcodes
+LOGIN_ERROR = 801
+PERMISSION_DENIED = 803
+
+
+def authenticate(username, password, permission=None):
+ """Authenticate staff_user with permission"""
+ try:
+ user = User.objects.get(username__exact=username)
+ except User.DoesNotExist:
+ raise Fault(LOGIN_ERROR, _('Username is incorrect.'))
+ if not user.check_password(password):
+ raise Fault(LOGIN_ERROR, _('Password is invalid.'))
+ if not user.is_staff or not user.is_active:
+ raise Fault(PERMISSION_DENIED, _('User account unavailable.'))
+ if permission:
+ if not user.has_perm(permission):
+ raise Fault(PERMISSION_DENIED, _('User cannot %s.') % permission)
+ return user
+
+
+def blog_structure(site):
+ """A blog structure"""
+ return {'url': '%s://%s%s' % (
+ PROTOCOL, site.domain, reverse('objectapp_gbobject_archive_index')),
+ 'blogid': settings.SITE_ID,
+ 'blogName': site.name}
+
+
+def user_structure(user, site):
+ """An user structure"""
+ return {'userid': user.pk,
+ 'email': user.email,
+ 'nickname': user.username,
+ 'lastname': user.last_name,
+ 'firstname': user.first_name,
+ 'url': '%s://%s%s' % (
+ PROTOCOL, site.domain,
+ reverse('objectapp_author_detail', args=[user.username]))}
+
+
+def author_structure(user):
+ """An author structure"""
+ return {'user_id': user.pk,
+ 'user_login': user.username,
+ 'display_name': user.username,
+ 'user_email': user.email}
+
+
+def Objecttype_structure(Objecttype, site):
+ """A Objecttype structure"""
+ return {'description': Objecttype.title,
+ 'htmlUrl': '%s://%s%s' % (
+ PROTOCOL, site.domain,
+ Objecttype.get_absolute_url()),
+ 'rssUrl': '%s://%s%s' % (
+ PROTOCOL, site.domain,
+ reverse('objectapp_Objecttype_feed', args=[Objecttype.tree_path])),
+ # Useful Wordpress Extensions
+ 'ObjecttypeId': Objecttype.pk,
+ 'parentId': Objecttype.parent and Objecttype.parent.pk or 0,
+ 'ObjecttypeDescription': Objecttype.description,
+ 'ObjecttypeName': Objecttype.title}
+
+
+def post_structure(gbobject, site):
+ """A post structure with extensions"""
+ author = gbobject.authors.all()[0]
+ return {'title': gbobject.title,
+ 'description': unicode(gbobject.html_content),
+ 'link': '%s://%s%s' % (PROTOCOL, site.domain,
+ gbobject.get_absolute_url()),
+ # Basic Extensions
+ 'permaLink': '%s://%s%s' % (PROTOCOL, site.domain,
+ gbobject.get_absolute_url()),
+ 'objecttypes': [cat.title for cat in gbobject.objecttypes.all()],
+ 'dateCreated': DateTime(gbobject.creation_date.isoformat()),
+ 'postid': gbobject.pk,
+ 'userid': author.username,
+ # Useful Movable Type Extensions
+ 'mt_excerpt': gbobject.excerpt,
+ 'mt_allow_comments': int(gbobject.comment_enabled),
+ 'mt_allow_pings': int(gbobject.pingback_enabled),
+ 'mt_keywords': gbobject.tags,
+ # Useful Wordpress Extensions
+ 'wp_author': author.username,
+ 'wp_author_id': author.pk,
+ 'wp_author_display_name': author.username,
+ 'wp_password': gbobject.password,
+ 'wp_slug': gbobject.slug,
+ 'sticky': gbobject.featured}
+
+
+@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
+def get_users_blogs(apikey, username, password):
+ """blogger.getUsersBlogs(api_key, username, password)
+ => blog structure[]"""
+ authenticate(username, password)
+ site = Site.objects.get_current()
+ return [blog_structure(site)]
+
+
+@xmlrpc_func(returns='struct', args=['string', 'string', 'string'])
+def get_user_info(apikey, username, password):
+ """blogger.getUserInfo(api_key, username, password)
+ => user structure"""
+ user = authenticate(username, password)
+ site = Site.objects.get_current()
+ return user_structure(user, site)
+
+
+@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
+def get_authors(apikey, username, password):
+ """wp.getAuthors(api_key, username, password)
+ => author structure[]"""
+ authenticate(username, password)
+ return [author_structure(author)
+ for author in User.objects.filter(is_staff=True)]
+
+
+@xmlrpc_func(returns='boolean', args=['string', 'string',
+ 'string', 'string', 'string'])
+def delete_post(apikey, post_id, username, password, publish):
+ """blogger.deletePost(api_key, post_id, username, password, 'publish')
+ => boolean"""
+ user = authenticate(username, password, 'objectapp.delete_gbobject')
+ gbobject = Gbobject.objects.get(id=post_id, authors=user)
+ gbobject.delete()
+ return True
+
+
+@xmlrpc_func(returns='struct', args=['string', 'string', 'string'])
+def get_post(post_id, username, password):
+ """metaWeblog.getPost(post_id, username, password)
+ => post structure"""
+ user = authenticate(username, password)
+ site = Site.objects.get_current()
+ return post_structure(Gbobject.objects.get(id=post_id, authors=user), site)
+
+
+@xmlrpc_func(returns='struct[]',
+ args=['string', 'string', 'string', 'integer'])
+def get_recent_posts(blog_id, username, password, number):
+ """metaWeblog.getRecentPosts(blog_id, username, password, number)
+ => post structure[]"""
+ user = authenticate(username, password)
+ site = Site.objects.get_current()
+ return [post_structure(gbobject, site) \
+ for gbobject in Gbobject.objects.filter(authors=user)[:number]]
+
+
+@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
+def get_objecttypes(blog_id, username, password):
+ """metaWeblog.getObjecttypes(blog_id, username, password)
+ => Objecttype structure[]"""
+ authenticate(username, password)
+ site = Site.objects.get_current()
+ return [Objecttype_structure(Objecttype, site) \
+ for Objecttype in Objecttype.objects.all()]
+
+
+@xmlrpc_func(returns='string', args=['string', 'string', 'string', 'struct'])
+def new_Objecttype(blog_id, username, password, Objecttype_struct):
+ """wp.newObjecttype(blog_id, username, password, Objecttype)
+ => Objecttype_id"""
+ authenticate(username, password, 'objectapp.add_Objecttype')
+ Objecttype_dict = {'title': Objecttype_struct['name'],
+ 'description': Objecttype_struct['description'],
+ 'slug': Objecttype_struct['slug']}
+ if int(Objecttype_struct['parent_id']):
+ Objecttype_dict['parent'] = Objecttype.objects.get(
+ pk=Objecttype_struct['parent_id'])
+ Objecttype = Objecttype.objects.create(**Objecttype_dict)
+
+ return Objecttype.pk
+
+
+@xmlrpc_func(returns='string', args=['string', 'string', 'string',
+ 'struct', 'boolean'])
+def new_post(blog_id, username, password, post, publish):
+ """metaWeblog.newPost(blog_id, username, password, post, publish)
+ => post_id"""
+ user = authenticate(username, password, 'objectapp.add_gbobject')
+ if post.get('dateCreated'):
+ creation_date = datetime.strptime(
+ post['dateCreated'].value.replace('Z', '').replace('-', ''),
+ '%Y%m%dT%H:%M:%S')
+ else:
+ creation_date = datetime.now()
+
+ gbobject_dict = {'title': post['title'],
+ 'content': post['description'],
+ 'excerpt': post.get('mt_excerpt', truncate_words(
+ strip_tags(post['description']), 50)),
+ 'creation_date': creation_date,
+ 'last_update': creation_date,
+ 'comment_enabled': post.get('mt_allow_comments', 1) == 1,
+ 'pingback_enabled': post.get('mt_allow_pings', 1) == 1,
+ 'featured': post.get('sticky', 0) == 1,
+ 'tags': 'mt_keywords' in post and post['mt_keywords'] or '',
+ 'slug': 'wp_slug' in post and post['wp_slug'] or slugify(
+ post['title']),
+ 'password': post.get('wp_password', ''),
+ 'status': publish and PUBLISHED or DRAFT}
+ gbobject = Gbobject.objects.create(**gbobject_dict)
+
+ author = user
+ if 'wp_author_id' in post and user.has_perm('objectapp.can_change_author'):
+ if int(post['wp_author_id']) != user.pk:
+ author = User.objects.get(pk=post['wp_author_id'])
+ gbobject.authors.add(author)
+
+ gbobject.sites.add(Site.objects.get_current())
+ if 'objecttypes' in post:
+ gbobject.objecttypes.add(*[Objecttype.objects.get_or_create(
+ title=cat, slug=slugify(cat))[0]
+ for cat in post['objecttypes']])
+
+ return gbobject.pk
+
+
+@xmlrpc_func(returns='boolean', args=['string', 'string', 'string',
+ 'struct', 'boolean'])
+def edit_post(post_id, username, password, post, publish):
+ """metaWeblog.editPost(post_id, username, password, post, publish)
+ => boolean"""
+ user = authenticate(username, password, 'objectapp.change_gbobject')
+ gbobject = Gbobject.objects.get(id=post_id, authors=user)
+ if post.get('dateCreated'):
+ creation_date = datetime.strptime(
+ post['dateCreated'].value.replace('Z', '').replace('-', ''),
+ '%Y%m%dT%H:%M:%S')
+ else:
+ creation_date = gbobject.creation_date
+
+ gbobject.title = post['title']
+ gbobject.content = post['description']
+ gbobject.excerpt = post.get('mt_excerpt', truncate_words(
+ strip_tags(post['description']), 50))
+ gbobject.creation_date = creation_date
+ gbobject.last_update = datetime.now()
+ gbobject.comment_enabled = post.get('mt_allow_comments', 1) == 1
+ gbobject.pingback_enabled = post.get('mt_allow_pings', 1) == 1
+ gbobject.featured = post.get('sticky', 0) == 1
+ gbobject.tags = 'mt_keywords' in post and post['mt_keywords'] or ''
+ gbobject.slug = 'wp_slug' in post and post['wp_slug'] or slugify(
+ post['title'])
+ gbobject.status = publish and PUBLISHED or DRAFT
+ gbobject.password = post.get('wp_password', '')
+ gbobject.save()
+
+ if 'wp_author_id' in post and user.has_perm('objectapp.can_change_author'):
+ if int(post['wp_author_id']) != user.pk:
+ author = User.objects.get(pk=post['wp_author_id'])
+ gbobject.authors.clear()
+ gbobject.authors.add(author)
+
+ if 'objecttypes' in post:
+ gbobject.objecttypes.clear()
+ gbobject.objecttypes.add(*[Objecttype.objects.get_or_create(
+ title=cat, slug=slugify(cat))[0]
+ for cat in post['objecttypes']])
+ return True
+
+
+@xmlrpc_func(returns='struct', args=['string', 'string', 'string', 'struct'])
+def new_media_object(blog_id, username, password, media):
+ """metaWeblog.newMediaObject(blog_id, username, password, media)
+ => media structure"""
+ authenticate(username, password)
+ path = default_storage.save(os.path.join(UPLOAD_TO, media['name']),
+ ContentFile(media['bits'].data))
+ return {'url': default_storage.url(path)}
diff --git a/objectapp/xmlrpc/pingback.py b/objectapp/xmlrpc/pingback.py
new file mode 100644
index 0000000..2c8d51b
--- /dev/null
+++ b/objectapp/xmlrpc/pingback.py
@@ -0,0 +1,187 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""XML-RPC methods of Objectapp Pingback"""
+from urllib2 import urlopen
+from urllib2 import URLError
+from urllib2 import HTTPError
+from urlparse import urlsplit
+
+from django.contrib import comments
+from django.utils.html import strip_tags
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import resolve
+from django.core.urlresolvers import Resolver404
+from django.utils.translation import ugettext as _
+from django.contrib.contenttypes.models import ContentType
+
+from objectapp.models import Gbobject
+from objectapp.settings import PINGBACK_CONTENT_LENGTH
+from BeautifulSoup import BeautifulSoup
+from django_xmlrpc.decorators import xmlrpc_func
+
+UNDEFINED_ERROR = 0
+SOURCE_DOES_NOT_EXIST = 16
+SOURCE_DOES_NOT_LINK = 17
+TARGET_DOES_NOT_EXIST = 32
+TARGET_IS_NOT_PINGABLE = 33
+PINGBACK_ALREADY_REGISTERED = 48
+
+
+def generate_pingback_content(soup, target, max_length, trunc_char='...'):
+ """Generate a description text for the pingback"""
+ link = soup.find('a', href=target)
+
+ content = strip_tags(unicode(link.findParent()))
+ index = content.index(link.string)
+
+ if len(content) > max_length:
+ middle = max_length / 2
+ start = index - middle
+ end = index + middle
+
+ if start <= 0:
+ end -= start
+ extract = content[0:end]
+ else:
+ extract = '%s%s' % (trunc_char, content[start:end])
+
+ if end < len(content):
+ extract += trunc_char
+ return extract
+
+ return content
+
+
+@xmlrpc_func(returns='string', args=['string', 'string'])
+def pingback_ping(source, target):
+ """pingback.ping(sourceURI, targetURI) => 'Pingback message'
+
+ Notifies the server that a link has been added to sourceURI,
+ pointing to targetURI.
+
+ See: http://hixie.ch/specs/pingback/pingback-1.0"""
+ try:
+ if source == target:
+ return UNDEFINED_ERROR
+
+ site = Site.objects.get_current()
+ try:
+ document = ''.join(urlopen(source).readlines())
+ except (HTTPError, URLError):
+ return SOURCE_DOES_NOT_EXIST
+
+ if not target in document:
+ return SOURCE_DOES_NOT_LINK
+
+ scheme, netloc, path, query, fragment = urlsplit(target)
+ if netloc != site.domain:
+ return TARGET_DOES_NOT_EXIST
+
+ try:
+ view, args, kwargs = resolve(path)
+ except Resolver404:
+ return TARGET_DOES_NOT_EXIST
+
+ try:
+ gbobject = Gbobject.published.get(
+ slug=kwargs['slug'],
+ creation_date__year=kwargs['year'],
+ creation_date__month=kwargs['month'],
+ creation_date__day=kwargs['day'])
+ if not gbobject.pingback_enabled:
+ return TARGET_IS_NOT_PINGABLE
+ except (KeyError, Gbobject.DoesNotExist):
+ return TARGET_IS_NOT_PINGABLE
+
+ soup = BeautifulSoup(document)
+ title = soup.find('title')
+ title = title and strip_tags(title) or _('No title')
+ description = generate_pingback_content(soup, target,
+ PINGBACK_CONTENT_LENGTH)
+
+ comment, created = comments.get_model().objects.get_or_create(
+ content_type=ContentType.objects.get_for_model(Gbobject),
+ object_pk=gbobject.pk, user_url=source, site=site,
+ defaults={'comment': description, 'user_name': title})
+ if created:
+ user = gbobject.authors.all()[0]
+ comment.flags.create(user=user, flag='pingback')
+ return 'Pingback from %s to %s registered.' % (source, target)
+ return PINGBACK_ALREADY_REGISTERED
+ except:
+ return UNDEFINED_ERROR
+
+
+@xmlrpc_func(returns='string[]', args=['string'])
+def pingback_extensions_get_pingbacks(target):
+ """pingback.extensions.getPingbacks(url) => '[url, url, ...]'
+
+ Returns an array of URLs that link to the specified url.
+
+ See: http://www.aquarionics.com/misc/archives/blogite/0198.html"""
+ site = Site.objects.get_current()
+
+ scheme, netloc, path, query, fragment = urlsplit(target)
+ if netloc != site.domain:
+ return TARGET_DOES_NOT_EXIST
+
+ try:
+ view, args, kwargs = resolve(path)
+ except Resolver404:
+ return TARGET_DOES_NOT_EXIST
+
+ try:
+ gbobject = Gbobject.published.get(
+ slug=kwargs['slug'],
+ creation_date__year=kwargs['year'],
+ creation_date__month=kwargs['month'],
+ creation_date__day=kwargs['day'])
+ except (KeyError, Gbobject.DoesNotExist):
+ return TARGET_IS_NOT_PINGABLE
+
+ return [pingback.user_url for pingback in gbobject.pingbacks]