Browse Source

Normalised line endings

Rafał Pitoń 12 years ago
parent
commit
9b21ab3a17
279 changed files with 27282 additions and 27282 deletions
  1. 94 94
      misago/markdown/factory.py
  2. 49 49
      misago/markdown/parsers.py
  3. 39 39
      misago/messages.py
  4. 14 14
      misago/middleware/acl.py
  5. 18 18
      misago/middleware/banning.py
  6. 32 32
      misago/middleware/bruteforce.py
  7. 12 12
      misago/middleware/cookiejar.py
  8. 14 14
      misago/middleware/crawlers.py
  9. 23 23
      misago/middleware/csrf.py
  10. 17 17
      misago/middleware/firewalls.py
  11. 7 7
      misago/middleware/heartbeat.py
  12. 16 16
      misago/middleware/mailsqueue.py
  13. 5 5
      misago/middleware/messages.py
  14. 12 12
      misago/middleware/privatethreads.py
  15. 28 28
      misago/middleware/session.py
  16. 16 16
      misago/middleware/stopwatch.py
  17. 22 22
      misago/middleware/theme.py
  18. 4 4
      misago/middleware/thread.py
  19. 34 34
      misago/middleware/user.py
  20. 950 950
      misago/migrations/0001_initial.py
  21. 395 395
      misago/migrations/0002_auto__del_field_session_hidden.py
  22. 414 414
      misago/migrations/0003_auto__add_field_checkpoint_old_forum__add_field_checkpoint_old_forum_n.py
  23. 399 399
      misago/migrations/0004_auto__add_field_checkpoint_deleted.py
  24. 400 400
      misago/migrations/0005_auto__add_field_forum_pruned_archive.py
  25. 404 404
      misago/migrations/0006_auto.py
  26. 393 393
      misago/migrations/0007_removethemeadjustments.py
  27. 396 396
      misago/migrations/0008_auto__add_field_thread_report_for.py
  28. 403 403
      misago/migrations/0009_auto__chg_field_thread_report_for__add_field_monitoritem_type.py
  29. 395 395
      misago/migrations/0010_auto__del_field_checkpoint_post__del_field_monitoritem_value__add_fiel.py
  30. 393 393
      misago/migrations/0011_auto__del_field_thread_merges__del_field_post_merge.py
  31. 395 395
      misago/migrations/0012_auto__add_field_post_current_date.py
  32. 391 391
      misago/migrations/0013_set_posts_current_date.py
  33. 390 390
      misago/migrations/0014_auto__del_field_post_edit_date.py
  34. 388 388
      misago/migrations/0015_remove_users_reported.py
  35. 394 394
      misago/migrations/0016_auto__add_field_post_delete_date.py
  36. 392 392
      misago/migrations/0017_populate_post_delete_date.py
  37. 394 394
      misago/migrations/0018_auto__add_field_watchedthread_starter__del_field_setting_value__add_fi.py
  38. 396 396
      misago/migrations/0019_auto__add_field_post_reports.py
  39. 24 24
      misago/models/__init__.py
  40. 77 77
      misago/models/alertmodel.py
  41. 91 91
      misago/models/banmodel.py
  42. 61 61
      misago/models/changemodel.py
  43. 60 60
      misago/models/checkpointmodel.py
  44. 6 6
      misago/models/fixturemodel.py
  45. 342 342
      misago/models/forummodel.py
  46. 28 28
      misago/models/forumreadmodel.py
  47. 37 37
      misago/models/forumrolemodel.py
  48. 73 73
      misago/models/karmamodel.py
  49. 26 26
      misago/models/monitoritemmodel.py
  50. 35 35
      misago/models/newslettermodel.py
  51. 186 186
      misago/models/postmodel.py
  52. 52 52
      misago/models/pruningpolicymodel.py
  53. 97 97
      misago/models/rankmodel.py
  54. 45 45
      misago/models/rolemodel.py
  55. 17 17
      misago/models/sessionmodel.py
  56. 139 139
      misago/models/settingmodel.py
  57. 14 14
      misago/models/settingsgroupmodel.py
  58. 38 38
      misago/models/signinattemptmodel.py
  59. 303 303
      misago/models/threadmodel.py
  60. 22 22
      misago/models/threadreadmodel.py
  61. 9 9
      misago/models/tokenmodel.py
  62. 581 581
      misago/models/usermodel.py
  63. 8 8
      misago/models/usernamechangemodel.py
  64. 35 35
      misago/models/watchedthreadmodel.py
  65. 103 103
      misago/monitor.py
  66. 62 62
      misago/onlines.py
  67. 110 110
      misago/readstrackers.py
  68. 88 88
      misago/search.py
  69. 40 40
      misago/search_indexes.py
  70. 261 261
      misago/sessions.py
  71. 261 261
      misago/settings_base.py
  72. 16 16
      misago/shortcuts.py
  73. 11 11
      misago/signals.py
  74. 6 6
      misago/stopwatch.py
  75. 8 8
      misago/template/loader.py
  76. 62 62
      misago/template/middlewares.py
  77. 42 42
      misago/template/theme.py
  78. 28 28
      misago/templatetags/datetime.py
  79. 33 33
      misago/templatetags/django2jinja.py
  80. 40 40
      misago/templatetags/md.py
  81. 25 25
      misago/templatetags/utils.py
  82. 70 70
      misago/tests/user_manager_create_user.py
  83. 11 11
      misago/thread.py
  84. 56 56
      misago/urls.py
  85. 46 46
      misago/utils/avatars.py
  86. 142 142
      misago/utils/datesformats.py
  87. 126 126
      misago/utils/fixtures.py
  88. 64 64
      misago/utils/pagination.py
  89. 8 8
      misago/utils/plugins.py
  90. 32 32
      misago/utils/strings.py
  91. 87 87
      misago/utils/timezones.py
  92. 19 19
      misago/utils/translation.py
  93. 23 23
      misago/utils/urls.py
  94. 16 16
      misago/utils/views.py
  95. 83 83
      misago/validators.py
  96. 14 14
      requirements.txt
  97. 91 91
      static/admin/css/admin.less
  98. 62 62
      static/admin/css/admin/alerts.less
  99. 65 65
      static/admin/css/admin/avatars.less
  100. 102 102
      static/admin/css/admin/buttons.less
  101. 105 105
      static/admin/css/admin/forms.less
  102. 28 28
      static/admin/css/admin/graphs.less
  103. 171 171
      static/admin/css/admin/navbar.less
  104. 74 74
      static/admin/css/admin/navs.less
  105. 32 32
      static/admin/css/admin/popovers.less
  106. 94 94
      static/admin/css/admin/scaffolding.less
  107. 183 183
      static/admin/css/admin/tables.less
  108. 54 54
      static/admin/css/admin/users-lists.less
  109. 15 15
      static/admin/css/admin/wells.less
  110. 64 64
      static/admin/js/admin.js
  111. 98 98
      static/cranefly/css/cranefly.less
  112. 41 41
      static/cranefly/css/cranefly/alerts.less
  113. 32 32
      static/cranefly/css/cranefly/breadcrumbs.less
  114. 164 164
      static/cranefly/css/cranefly/buttons.less
  115. 260 260
      static/cranefly/css/cranefly/category.less
  116. 132 132
      static/cranefly/css/cranefly/changelog.less
  117. 70 70
      static/cranefly/css/cranefly/editor.less
  118. 55 55
      static/cranefly/css/cranefly/error.less
  119. 124 124
      static/cranefly/css/cranefly/forms.less
  120. 566 566
      static/cranefly/css/cranefly/forum.less
  121. 165 165
      static/cranefly/css/cranefly/forummap.less
  122. 170 170
      static/cranefly/css/cranefly/header.less
  123. 444 444
      static/cranefly/css/cranefly/index.less
  124. 44 44
      static/cranefly/css/cranefly/karmas.less
  125. 156 156
      static/cranefly/css/cranefly/markdown.less
  126. 43 43
      static/cranefly/css/cranefly/messages.less
  127. 495 495
      static/cranefly/css/cranefly/navbar.less
  128. 46 46
      static/cranefly/css/cranefly/newsfeed.less
  129. 46 46
      static/cranefly/css/cranefly/pagination.less
  130. 162 162
      static/cranefly/css/cranefly/profiles.less
  131. 78 78
      static/cranefly/css/cranefly/report.less
  132. 53 53
      static/cranefly/css/cranefly/reports.less
  133. 69 69
      static/cranefly/css/cranefly/scaffolding.less
  134. 105 105
      static/cranefly/css/cranefly/search.less
  135. 21 21
      static/cranefly/css/cranefly/signin.less
  136. 621 621
      static/cranefly/css/cranefly/thread.less
  137. 118 118
      static/cranefly/css/cranefly/usercp.less
  138. 30 30
      static/cranefly/css/cranefly/watchedthreads.less
  139. 50 50
      static/cranefly/css/ranks.less
  140. 232 232
      static/cranefly/js/cranefly.js
  141. 171 171
      static/cranefly/js/editor.js
  142. 74 74
      templates/500.html
  143. 44 44
      templates/_email/base.html
  144. 9 9
      templates/_email/base.txt
  145. 8 8
      templates/_email/private_thread_invite.html
  146. 9 9
      templates/_email/private_thread_invite.txt
  147. 8 8
      templates/_email/private_thread_reply_notification.html
  148. 9 9
      templates/_email/private_thread_reply_notification.txt
  149. 8 8
      templates/_email/report_reply_notification.html
  150. 9 9
      templates/_email/report_reply_notification.txt
  151. 8 8
      templates/_email/thread_reply_notification.html
  152. 9 9
      templates/_email/thread_reply_notification.txt
  153. 6 6
      templates/_email/users/activation/admin.html
  154. 6 6
      templates/_email/users/activation/admin.txt
  155. 10 10
      templates/_email/users/activation/admin_done.html
  156. 7 7
      templates/_email/users/activation/admin_done.txt
  157. 11 11
      templates/_email/users/activation/invalidated.html
  158. 9 9
      templates/_email/users/activation/invalidated.txt
  159. 15 15
      templates/_email/users/activation/none.html
  160. 14 14
      templates/_email/users/activation/none.txt
  161. 11 11
      templates/_email/users/activation/resend.html
  162. 9 9
      templates/_email/users/activation/resend.txt
  163. 11 11
      templates/_email/users/activation/user.html
  164. 7 7
      templates/_email/users/activation/user.txt
  165. 12 12
      templates/_email/users/new_credentials.html
  166. 9 9
      templates/_email/users/new_credentials.txt
  167. 6 6
      templates/_email/users/newsletter.html
  168. 6 6
      templates/_email/users/newsletter.txt
  169. 14 14
      templates/_email/users/password/confirm.html
  170. 11 11
      templates/_email/users/password/confirm.txt
  171. 15 15
      templates/_email/users/password/new.html
  172. 11 11
      templates/_email/users/password/new.txt
  173. 13 13
      templates/_email/users/password/new_admin.html
  174. 9 9
      templates/_email/users/password/new_admin.txt
  175. 213 213
      templates/_forms.html
  176. 43 43
      templates/admin/admin/acl_form.html
  177. 53 53
      templates/admin/admin/form.html
  178. 30 30
      templates/admin/admin/layout.html
  179. 148 148
      templates/admin/admin/list.html
  180. 14 14
      templates/admin/bans/list.html
  181. 16 16
      templates/admin/base.html
  182. 7 7
      templates/admin/clients/list.html
  183. 22 22
      templates/admin/error403.html
  184. 22 22
      templates/admin/error404.html
  185. 5 5
      templates/admin/forums/delete.html
  186. 17 17
      templates/admin/forums/list.html
  187. 64 64
      templates/admin/index.html
  188. 45 45
      templates/admin/layout.html
  189. 17 17
      templates/admin/layout_compact.html
  190. 25 25
      templates/admin/macros.html
  191. 8 8
      templates/admin/newsletters/list.html
  192. 23 23
      templates/admin/online/list.html
  193. 21 21
      templates/admin/processing.html
  194. 14 14
      templates/admin/prune/apply.html
  195. 7 7
      templates/admin/prune/list.html
  196. 16 16
      templates/admin/ranks/list.html
  197. 28 28
      templates/admin/roles/forums.html
  198. 7 7
      templates/admin/roles/list.html
  199. 7 7
      templates/admin/roles_forums/list.html
  200. 15 15
      templates/admin/settings/search_results.html
  201. 41 41
      templates/admin/settings/settings.html
  202. 26 26
      templates/admin/signin.html
  203. 16 16
      templates/admin/stats/form.html
  204. 31 31
      templates/admin/stats/graph.html
  205. 13 13
      templates/admin/stats/layout.html
  206. 8 8
      templates/admin/stats/not_available.html
  207. 76 76
      templates/admin/stats/plot.html
  208. 25 25
      templates/admin/team/list.html
  209. 11 11
      templates/admin/todo.html
  210. 35 35
      templates/admin/users/list.html
  211. 71 71
      templates/cranefly/alerts.html
  212. 21 21
      templates/cranefly/base.html
  213. 114 114
      templates/cranefly/category.html
  214. 45 45
      templates/cranefly/editor.html
  215. 22 22
      templates/cranefly/error403.html
  216. 39 39
      templates/cranefly/error403_banned.html
  217. 22 22
      templates/cranefly/error404.html
  218. 74 74
      templates/cranefly/forum_map.html
  219. 26 26
      templates/cranefly/forum_tos.html
  220. 188 188
      templates/cranefly/index.html
  221. 170 170
      templates/cranefly/layout.html
  222. 81 81
      templates/cranefly/macros.html
  223. 103 103
      templates/cranefly/new_threads.html
  224. 54 54
      templates/cranefly/newsfeed.html
  225. 103 103
      templates/cranefly/popular_threads.html
  226. 80 80
      templates/cranefly/private_threads/changelog.html
  227. 95 95
      templates/cranefly/private_threads/changelog_diff.html
  228. 34 34
      templates/cranefly/private_threads/details.html
  229. 169 169
      templates/cranefly/private_threads/list.html
  230. 181 181
      templates/cranefly/private_threads/posting.html
  231. 582 582
      templates/cranefly/private_threads/thread.html
  232. 243 243
      templates/cranefly/profiles/details.html
  233. 58 58
      templates/cranefly/profiles/followers.html
  234. 58 58
      templates/cranefly/profiles/follows.html
  235. 113 113
      templates/cranefly/profiles/list.html
  236. 70 70
      templates/cranefly/profiles/posts.html
  237. 108 108
      templates/cranefly/profiles/profile.html
  238. 66 66
      templates/cranefly/profiles/threads.html
  239. 62 62
      templates/cranefly/register.html
  240. 80 80
      templates/cranefly/reports/changelog.html
  241. 95 95
      templates/cranefly/reports/changelog_diff.html
  242. 34 34
      templates/cranefly/reports/details.html
  243. 183 183
      templates/cranefly/reports/list.html
  244. 177 177
      templates/cranefly/reports/posting.html
  245. 460 460
      templates/cranefly/reports/thread.html
  246. 35 35
      templates/cranefly/resend_activation.html
  247. 35 35
      templates/cranefly/reset_password.html
  248. 5 5
      templates/cranefly/search/error.html
  249. 16 16
      templates/cranefly/search/home.html
  250. 16 16
      templates/cranefly/search/layout.html
  251. 56 56
      templates/cranefly/search/results.html
  252. 65 65
      templates/cranefly/signin.html
  253. 80 80
      templates/cranefly/threads/changelog.html
  254. 95 95
      templates/cranefly/threads/changelog_diff.html
  255. 34 34
      templates/cranefly/threads/details.html
  256. 116 116
      templates/cranefly/threads/karmas.html
  257. 289 289
      templates/cranefly/threads/list.html
  258. 62 62
      templates/cranefly/threads/merge.html
  259. 58 58
      templates/cranefly/threads/move_posts.html
  260. 59 59
      templates/cranefly/threads/move_thread.html
  261. 57 57
      templates/cranefly/threads/move_threads.html
  262. 188 188
      templates/cranefly/threads/posting.html
  263. 58 58
      templates/cranefly/threads/split.html
  264. 547 547
      templates/cranefly/threads/thread.html
  265. 47 47
      templates/cranefly/usercp/avatar.html
  266. 21 21
      templates/cranefly/usercp/avatar_banned.html
  267. 90 90
      templates/cranefly/usercp/avatar_crop.html
  268. 36 36
      templates/cranefly/usercp/avatar_gallery.html
  269. 40 40
      templates/cranefly/usercp/avatar_upload.html
  270. 32 32
      templates/cranefly/usercp/credentials.html
  271. 20 20
      templates/cranefly/usercp/layout.html
  272. 46 46
      templates/cranefly/usercp/options.html
  273. 41 41
      templates/cranefly/usercp/signature.html
  274. 21 21
      templates/cranefly/usercp/signature_banned.html
  275. 45 45
      templates/cranefly/usercp/username.html
  276. 208 208
      templates/cranefly/watched.html
  277. 21 21
      templates/debug_toolbar/panels/acl.html
  278. 137 137
      templates/forms.html
  279. 1 1
      templates/search/indexes/misago/post_text.txt

+ 94 - 94
misago/markdown/factory.py

@@ -1,95 +1,95 @@
-import re
-import markdown
-from django.conf import settings
-from django.utils.importlib import import_module
-from django.utils.translation import ugettext_lazy as _
-from misago.utils.strings import random_string
-from misago.markdown.extensions.cleanlinks import CleanLinksExtension
-from misago.markdown.extensions.emoji import EmojiExtension
-from misago.markdown.parsers import RemoveHTMLParser
-
-def clear_markdown(text):
-    parser = RemoveHTMLParser()
-    parser.feed(text)
-    return parser.clean_text
-
-
-def remove_unsupported(md):
-    # References are evil, we dont support them
-    del md.preprocessors['reference']
-    del md.inlinePatterns['reference']
-    del md.inlinePatterns['image_reference']
-    del md.inlinePatterns['short_reference']
-
-
-def signature_markdown(acl, text):
-    md = markdown.Markdown(
-                           safe_mode='escape',
-                           output_format=settings.OUTPUT_FORMAT,
-                           extensions=['nl2br'])
-
-    remove_unsupported(md)
-    cleanlinks = CleanLinksExtension()
-    cleanlinks.extendMarkdown(md)
-
-    if not acl.usercp.allow_signature_links():
-        del md.inlinePatterns['link']
-        del md.inlinePatterns['autolink']
-    if not acl.usercp.allow_signature_images():
-        del md.inlinePatterns['image_link']
-    else:
-        emojis = EmojiExtension()
-        emojis.extendMarkdown(md)
-
-    del md.parser.blockprocessors['hashheader']
-    del md.parser.blockprocessors['setextheader']
-    del md.parser.blockprocessors['code']
-    del md.parser.blockprocessors['quote']
-    del md.parser.blockprocessors['hr']
-    del md.parser.blockprocessors['olist']
-    del md.parser.blockprocessors['ulist']
-    
-    return md.convert(text)
-
-
-def post_markdown(text):
-    md = markdown.Markdown(safe_mode='escape',
-                           output_format=settings.OUTPUT_FORMAT,
-                           extensions=['nl2br', 'fenced_code'])
-
-    remove_unsupported(md)
-    md.mi_token = random_string(16)
-    for extension in settings.MARKDOWN_EXTENSIONS:
-        module = '.'.join(extension.split('.')[:-1])
-        extension = extension.split('.')[-1]
-        module = import_module(module)
-        attr = getattr(module, extension)
-        ext = attr()
-        ext.extendMarkdown(md)
-    text = md.convert(text)
-    md, text = tidy_markdown(md, text)
-    return md, text
-
-
-def tidy_markdown(md, text):
-    text = text.replace('<p><h3><quotetitle>', '<article><header><quotetitle>')
-    text = text.replace('</quotetitle></h3></p>', '</quotetitle></header></article>')
-    text = text.replace('</quotetitle></h3><br>\r\n', '</quotetitle></header></article>\r\n<p>')
-    text = text.replace('\r\n<p></p>', '')
-    return md, text
-
-
-def finalize_markdown(text):
-    def trans_quotetitle(match):
-        return _("Posted by %(user)s") % {'user': match.group('content')}
-    text = re.sub(r'<quotetitle>(?P<content>.+)</quotetitle>', trans_quotetitle, text)
-    text = re.sub(r'<quotesingletitle>', _("Quote"), text)
-    text = re.sub(r'<imgalt>', _("Posted image"), text)
-    return text
-
-
-def emojis():
-    if 'misago.markdown.extensions.emoji.EmojiExtension' in settings.MARKDOWN_EXTENSIONS:
-        from misago.markdown.extensions.emoji import EMOJIS
-        return EMOJIS
+import re
+import markdown
+from django.conf import settings
+from django.utils.importlib import import_module
+from django.utils.translation import ugettext_lazy as _
+from misago.utils.strings import random_string
+from misago.markdown.extensions.cleanlinks import CleanLinksExtension
+from misago.markdown.extensions.emoji import EmojiExtension
+from misago.markdown.parsers import RemoveHTMLParser
+
+def clear_markdown(text):
+    parser = RemoveHTMLParser()
+    parser.feed(text)
+    return parser.clean_text
+
+
+def remove_unsupported(md):
+    # References are evil, we dont support them
+    del md.preprocessors['reference']
+    del md.inlinePatterns['reference']
+    del md.inlinePatterns['image_reference']
+    del md.inlinePatterns['short_reference']
+
+
+def signature_markdown(acl, text):
+    md = markdown.Markdown(
+                           safe_mode='escape',
+                           output_format=settings.OUTPUT_FORMAT,
+                           extensions=['nl2br'])
+
+    remove_unsupported(md)
+    cleanlinks = CleanLinksExtension()
+    cleanlinks.extendMarkdown(md)
+
+    if not acl.usercp.allow_signature_links():
+        del md.inlinePatterns['link']
+        del md.inlinePatterns['autolink']
+    if not acl.usercp.allow_signature_images():
+        del md.inlinePatterns['image_link']
+    else:
+        emojis = EmojiExtension()
+        emojis.extendMarkdown(md)
+
+    del md.parser.blockprocessors['hashheader']
+    del md.parser.blockprocessors['setextheader']
+    del md.parser.blockprocessors['code']
+    del md.parser.blockprocessors['quote']
+    del md.parser.blockprocessors['hr']
+    del md.parser.blockprocessors['olist']
+    del md.parser.blockprocessors['ulist']
+    
+    return md.convert(text)
+
+
+def post_markdown(text):
+    md = markdown.Markdown(safe_mode='escape',
+                           output_format=settings.OUTPUT_FORMAT,
+                           extensions=['nl2br', 'fenced_code'])
+
+    remove_unsupported(md)
+    md.mi_token = random_string(16)
+    for extension in settings.MARKDOWN_EXTENSIONS:
+        module = '.'.join(extension.split('.')[:-1])
+        extension = extension.split('.')[-1]
+        module = import_module(module)
+        attr = getattr(module, extension)
+        ext = attr()
+        ext.extendMarkdown(md)
+    text = md.convert(text)
+    md, text = tidy_markdown(md, text)
+    return md, text
+
+
+def tidy_markdown(md, text):
+    text = text.replace('<p><h3><quotetitle>', '<article><header><quotetitle>')
+    text = text.replace('</quotetitle></h3></p>', '</quotetitle></header></article>')
+    text = text.replace('</quotetitle></h3><br>\r\n', '</quotetitle></header></article>\r\n<p>')
+    text = text.replace('\r\n<p></p>', '')
+    return md, text
+
+
+def finalize_markdown(text):
+    def trans_quotetitle(match):
+        return _("Posted by %(user)s") % {'user': match.group('content')}
+    text = re.sub(r'<quotetitle>(?P<content>.+)</quotetitle>', trans_quotetitle, text)
+    text = re.sub(r'<quotesingletitle>', _("Quote"), text)
+    text = re.sub(r'<imgalt>', _("Posted image"), text)
+    return text
+
+
+def emojis():
+    if 'misago.markdown.extensions.emoji.EmojiExtension' in settings.MARKDOWN_EXTENSIONS:
+        from misago.markdown.extensions.emoji import EMOJIS
+        return EMOJIS
     return []
     return []

+ 49 - 49
misago/markdown/parsers.py

@@ -1,50 +1,50 @@
-from HTMLParser import HTMLParser
-from urlparse import urlparse
-from misago.utils.strings import random_string
-
-class RemoveHTMLParser(HTMLParser):
-    def __init__(self):
-        HTMLParser.__init__(self)
-        self.clean_text = ''
-        self.lookback = []
-
-    def handle_entityref(self, name):
-        if name == 'gt':
-            self.clean_text += '>'
-        if name == 'lt':
-            self.clean_text += '<'
-
-    def handle_starttag(self, tag, attrs):
-        if tag == 'img':
-            self.handle_startendtag(tag, attrs)
-        else:
-            self.lookback.append(tag)
-
-    def handle_endtag(self, tag):
-        try:
-            if self.lookback[-1] == tag:
-                self.lookback.pop()
-        except IndexError:
-            pass
-
-    def handle_startendtag(self, tag, attrs):
-        try:
-            if tag == 'img':
-                for attr in attrs:
-                    if attr[0] == 'alt':
-                        self.clean_text += attr[1]
-                        break
-        except KeyError:
-            pass
-        
-    def handle_data(self, data):
-        # String does not repeat itself
-        if self.clean_text[-len(data):] != data:
-            # String is not "QUOTE"
-            try:
-                if self.lookback[-1] in ('strong', 'em'):
-                    self.clean_text += data
-                elif not (data == 'Quote' and self.lookback[-1] == 'h3' and self.lookback[-2] == 'blockquote'):
-                    self.clean_text += data
-            except IndexError:
+from HTMLParser import HTMLParser
+from urlparse import urlparse
+from misago.utils.strings import random_string
+
+class RemoveHTMLParser(HTMLParser):
+    def __init__(self):
+        HTMLParser.__init__(self)
+        self.clean_text = ''
+        self.lookback = []
+
+    def handle_entityref(self, name):
+        if name == 'gt':
+            self.clean_text += '>'
+        if name == 'lt':
+            self.clean_text += '<'
+
+    def handle_starttag(self, tag, attrs):
+        if tag == 'img':
+            self.handle_startendtag(tag, attrs)
+        else:
+            self.lookback.append(tag)
+
+    def handle_endtag(self, tag):
+        try:
+            if self.lookback[-1] == tag:
+                self.lookback.pop()
+        except IndexError:
+            pass
+
+    def handle_startendtag(self, tag, attrs):
+        try:
+            if tag == 'img':
+                for attr in attrs:
+                    if attr[0] == 'alt':
+                        self.clean_text += attr[1]
+                        break
+        except KeyError:
+            pass
+        
+    def handle_data(self, data):
+        # String does not repeat itself
+        if self.clean_text[-len(data):] != data:
+            # String is not "QUOTE"
+            try:
+                if self.lookback[-1] in ('strong', 'em'):
+                    self.clean_text += data
+                elif not (data == 'Quote' and self.lookback[-1] == 'h3' and self.lookback[-2] == 'blockquote'):
+                    self.clean_text += data
+            except IndexError:
                 self.clean_text += data
                 self.clean_text += data

+ 39 - 39
misago/messages.py

@@ -1,39 +1,39 @@
-class Messages(object):
-    def __init__(self, session):
-        self.session = session
-        self.messages = session.get('messages_list', [])
-        self.session['messages_list'] = []
-
-    def set_message(self, message, type='info', owner=None):
-        message.type = type
-        message.owner = owner
-        self.messages.append(message)
-
-    def set_flash(self, message, type='info', owner=None):
-        self.set_message(message, type, owner)
-        self.session['messages_list'].append(message)
-
-    def get_message(self, owner=None):
-        for index, message in enumerate(self.messages):
-            if message.owner == owner:
-                del self.messages[index]
-                return message
-        return None
-
-    def get_messages(self, owner=None):
-        orphans = []
-        messages = []
-        for message in self.messages:
-            if message.owner == owner:
-                messages.append(message)
-            else:
-                orphans.append(message)
-        self.messages = orphans
-        return messages
-
-
-class Message(object):
-    def __init__(self, message=None, type='info', owner=None):
-        self.type = type
-        self.message = message
-        self.owner = owner
+class Messages(object):
+    def __init__(self, session):
+        self.session = session
+        self.messages = session.get('messages_list', [])
+        self.session['messages_list'] = []
+
+    def set_message(self, message, type='info', owner=None):
+        message.type = type
+        message.owner = owner
+        self.messages.append(message)
+
+    def set_flash(self, message, type='info', owner=None):
+        self.set_message(message, type, owner)
+        self.session['messages_list'].append(message)
+
+    def get_message(self, owner=None):
+        for index, message in enumerate(self.messages):
+            if message.owner == owner:
+                del self.messages[index]
+                return message
+        return None
+
+    def get_messages(self, owner=None):
+        orphans = []
+        messages = []
+        for message in self.messages:
+            if message.owner == owner:
+                messages.append(message)
+            else:
+                orphans.append(message)
+        self.messages = orphans
+        return messages
+
+
+class Message(object):
+    def __init__(self, message=None, type='info', owner=None):
+        self.type = type
+        self.message = message
+        self.owner = owner

+ 14 - 14
misago/middleware/acl.py

@@ -1,14 +1,14 @@
-from misago.acl.builder import acl
-
-class ACLMiddleware(object):
-    def process_request(self, request):
-        request.acl = acl(request, request.user)
-        
-        if (request.user.is_authenticated() and
-            (request.acl.team or request.user.is_god()) != request.user.is_team):
-            request.user.is_team = (request.acl.team or request.user.is_god())
-            request.user.save(force_update=True)
-            
-        if request.session.team != request.user.is_team:
-            request.session.team = request.user.is_team
-            request.session.save()
+from misago.acl.builder import acl
+
+class ACLMiddleware(object):
+    def process_request(self, request):
+        request.acl = acl(request, request.user)
+        
+        if (request.user.is_authenticated() and
+            (request.acl.team or request.user.is_god()) != request.user.is_team):
+            request.user.is_team = (request.acl.team or request.user.is_god())
+            request.user.save(force_update=True)
+            
+        if request.session.team != request.user.is_team:
+            request.session.team = request.user.is_team
+            request.session.save()

+ 18 - 18
misago/middleware/banning.py

@@ -1,18 +1,18 @@
-from misago.models import BanCache, Guest
-
-class BanningMiddleware(object):
-    def process_request(self, request):
-        if request.heartbeat or request.user.is_crawler():
-            return None
-            
-        try:
-            request.ban = request.session['ban']
-        except KeyError:
-            request.ban = BanCache()
-            request.session['ban'] = request.ban
-
-        if not request.firewall.admin:
-            request.ban.check_for_updates(request)
-            # Make sure banned session is downgraded to guest level
-            if request.ban.is_banned():
-                request.session.sign_out(request)
+from misago.models import BanCache, Guest
+
+class BanningMiddleware(object):
+    def process_request(self, request):
+        if request.heartbeat or request.user.is_crawler():
+            return None
+            
+        try:
+            request.ban = request.session['ban']
+        except KeyError:
+            request.ban = BanCache()
+            request.session['ban'] = request.ban
+
+        if not request.firewall.admin:
+            request.ban.check_for_updates(request)
+            # Make sure banned session is downgraded to guest level
+            if request.ban.is_banned():
+                request.session.sign_out(request)

+ 32 - 32
misago/middleware/bruteforce.py

@@ -1,32 +1,32 @@
-from datetime import timedelta
-from django.utils import timezone
-from misago.conf import settings
-from misago.models import SignInAttempt
-
-class JamCache(object):
-    def __init__(self):
-        self.jammed = False
-        self.expires = timezone.now()
-    
-    def check_for_updates(self, request):
-        if self.expires < timezone.now():
-            self.jammed = SignInAttempt.objects.is_jammed(request.session.get_ip(request))
-            self.expires = timezone.now() + timedelta(minutes=settings.jams_lifetime)
-            return True
-        return False
-
-    def is_jammed(self):
-        return self.jammed
-
-
-class JamMiddleware(object):
-    def process_request(self, request):
-        if request.user.is_crawler():
-            return None
-        try:
-            request.jam = request.session['jam']
-        except KeyError:
-            request.jam = JamCache()
-            request.session['jam'] = request.jam
-        if not request.firewall.admin:
-            request.jam.check_for_updates(request)
+from datetime import timedelta
+from django.utils import timezone
+from misago.conf import settings
+from misago.models import SignInAttempt
+
+class JamCache(object):
+    def __init__(self):
+        self.jammed = False
+        self.expires = timezone.now()
+    
+    def check_for_updates(self, request):
+        if self.expires < timezone.now():
+            self.jammed = SignInAttempt.objects.is_jammed(request.session.get_ip(request))
+            self.expires = timezone.now() + timedelta(minutes=settings.jams_lifetime)
+            return True
+        return False
+
+    def is_jammed(self):
+        return self.jammed
+
+
+class JamMiddleware(object):
+    def process_request(self, request):
+        if request.user.is_crawler():
+            return None
+        try:
+            request.jam = request.session['jam']
+        except KeyError:
+            request.jam = JamCache()
+            request.session['jam'] = request.jam
+        if not request.firewall.admin:
+            request.jam.check_for_updates(request)

+ 12 - 12
misago/middleware/cookiejar.py

@@ -1,12 +1,12 @@
-from misago.cookiejar import CookieJar
-
-class CookieJarMiddleware(object):
-    def process_request(self, request):
-        request.cookiejar = CookieJar()
-
-    def process_response(self, request, response):
-        try:
-            request.cookiejar.flush(response)
-        except AttributeError:
-            pass
-        return response
+from misago.cookiejar import CookieJar
+
+class CookieJarMiddleware(object):
+    def process_request(self, request):
+        request.cookiejar = CookieJar()
+
+    def process_response(self, request, response):
+        try:
+            request.cookiejar.flush(response)
+        except AttributeError:
+            pass
+        return response

+ 14 - 14
misago/middleware/crawlers.py

@@ -1,15 +1,15 @@
-from misago.crawlers import Crawler
-from misago import models
-
-class DetectCrawlerMiddleware(object):
-    def process_request(self, request):
-        # If its correct request (We have client IP), see if it exists in Crawlers DB
-        if request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR'):
-            found_crawler = Crawler(
-                                    request.META.get('HTTP_USER_AGENT', ''),
-                                    request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR')
-                                    )
-            
-            # If crawler exists in database, use it as this request user
-            if found_crawler.crawler:
+from misago.crawlers import Crawler
+from misago import models
+
+class DetectCrawlerMiddleware(object):
+    def process_request(self, request):
+        # If its correct request (We have client IP), see if it exists in Crawlers DB
+        if request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR'):
+            found_crawler = Crawler(
+                                    request.META.get('HTTP_USER_AGENT', ''),
+                                    request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR')
+                                    )
+            
+            # If crawler exists in database, use it as this request user
+            if found_crawler.crawler:
                 request.user = models.Crawler(found_crawler.username)
                 request.user = models.Crawler(found_crawler.username)

+ 23 - 23
misago/middleware/csrf.py

@@ -1,23 +1,23 @@
-from misago.utils.strings import random_string
-
-class CSRFProtection(object):
-    def __init__(self, csrf_token):
-        self.csrf_id = '_csrf_token'
-        self.csrf_token = csrf_token
-        
-    def request_secure(self, request):
-        return request.method == 'POST' and request.POST.get(self.csrf_id) == self.csrf_token
-
-
-class CSRFMiddleware(object):
-    def process_request(self, request):
-        if request.user.is_crawler():
-            return None
-
-        if 'csrf_token' in request.session:
-            csrf_token = request.session['csrf_token']
-        else:
-            csrf_token = random_string(16);
-            request.session['csrf_token'] = csrf_token
-        
-        request.csrf = CSRFProtection(csrf_token)
+from misago.utils.strings import random_string
+
+class CSRFProtection(object):
+    def __init__(self, csrf_token):
+        self.csrf_id = '_csrf_token'
+        self.csrf_token = csrf_token
+        
+    def request_secure(self, request):
+        return request.method == 'POST' and request.POST.get(self.csrf_id) == self.csrf_token
+
+
+class CSRFMiddleware(object):
+    def process_request(self, request):
+        if request.user.is_crawler():
+            return None
+
+        if 'csrf_token' in request.session:
+            csrf_token = request.session['csrf_token']
+        else:
+            csrf_token = random_string(16);
+            request.session['csrf_token'] = csrf_token
+        
+        request.csrf = CSRFProtection(csrf_token)

+ 17 - 17
misago/middleware/firewalls.py

@@ -1,17 +1,17 @@
-from django.conf import settings
-from misago.firewalls import *
-from misago.template.theme import activate_theme
-
-class FirewallMiddleware(object):
-    firewall_admin = FirewallAdmin()
-    firewall_forum = FirewallForum()
-
-    def process_request(self, request):
-        if settings.ADMIN_PATH and self.firewall_admin.behind_firewall(request.path_info):
-            request.firewall = self.firewall_admin
-            activate_theme('admin')
-        else:
-            request.firewall = self.firewall_forum
-
-    def process_view(self, request, callback, callback_args, callback_kwargs):
-        return request.firewall.process_view(request, callback, callback_args, callback_kwargs)
+from django.conf import settings
+from misago.firewalls import *
+from misago.template.theme import activate_theme
+
+class FirewallMiddleware(object):
+    firewall_admin = FirewallAdmin()
+    firewall_forum = FirewallForum()
+
+    def process_request(self, request):
+        if settings.ADMIN_PATH and self.firewall_admin.behind_firewall(request.path_info):
+            request.firewall = self.firewall_admin
+            activate_theme('admin')
+        else:
+            request.firewall = self.firewall_forum
+
+    def process_view(self, request, callback, callback_args, callback_kwargs):
+        return request.firewall.process_view(request, callback, callback_args, callback_kwargs)

+ 7 - 7
misago/middleware/heartbeat.py

@@ -1,8 +1,8 @@
-from django.conf import settings
-from django.http import HttpResponse
-
-class HeartbeatMiddleware(object):
-    def process_request(self, request):
-        request.heartbeat = settings.HEARTBEAT_PATH and settings.HEARTBEAT_PATH == request.path[1:]
-        if request.heartbeat:
+from django.conf import settings
+from django.http import HttpResponse
+
+class HeartbeatMiddleware(object):
+    def process_request(self, request):
+        request.heartbeat = settings.HEARTBEAT_PATH and settings.HEARTBEAT_PATH == request.path[1:]
+        if request.heartbeat:
             return HttpResponse('BATTLECRUISER OPERATIONAL')
             return HttpResponse('BATTLECRUISER OPERATIONAL')

+ 16 - 16
misago/middleware/mailsqueue.py

@@ -1,17 +1,17 @@
-from django.conf import settings
-from django.core import mail
-
-class MailsQueueMiddleware(object):
-    def process_request(self, request):
-        request.mails_queue = []
-
-    def process_response(self, request, response):
-        try:
-            if request.mails_queue:
-                connection = mail.get_connection(fail_silently=settings.DEBUG)
-                connection.open()
-                connection.send_messages(request.mails_queue)
-                connection.close()
-        except AttributeError:
-            pass
+from django.conf import settings
+from django.core import mail
+
+class MailsQueueMiddleware(object):
+    def process_request(self, request):
+        request.mails_queue = []
+
+    def process_response(self, request, response):
+        try:
+            if request.mails_queue:
+                connection = mail.get_connection(fail_silently=settings.DEBUG)
+                connection.open()
+                connection.send_messages(request.mails_queue)
+                connection.close()
+        except AttributeError:
+            pass
         return response
         return response

+ 5 - 5
misago/middleware/messages.py

@@ -1,5 +1,5 @@
-from misago.messages import Messages
-
-class MessagesMiddleware(object):
-    def process_request(self, request):
-        request.messages = Messages(request.session)
+from misago.messages import Messages
+
+class MessagesMiddleware(object):
+    def process_request(self, request):
+        request.messages = Messages(request.session)

+ 12 - 12
misago/middleware/privatethreads.py

@@ -1,13 +1,13 @@
-from misago.models import Forum, Thread
-from misago.readstrackers import ThreadsTracker
-
-class PrivateThreadsMiddleware(object):
-    def process_request(self, request):
-        if (request.user.is_authenticated() and
-                request.acl.private_threads.can_participate() and
-                request.user.sync_pds):
-            forum = Forum.objects.special_model('private_threads')
-            tracker = ThreadsTracker(request, forum)
-            unread_pds = tracker.unread_count(forum.thread_set.filter(participants__id=request.user.pk))
-            request.user.sync_unread_pds(unread_pds)
+from misago.models import Forum, Thread
+from misago.readstrackers import ThreadsTracker
+
+class PrivateThreadsMiddleware(object):
+    def process_request(self, request):
+        if (request.user.is_authenticated() and
+                request.acl.private_threads.can_participate() and
+                request.user.sync_pds):
+            forum = Forum.objects.special_model('private_threads')
+            tracker = ThreadsTracker(request, forum)
+            unread_pds = tracker.unread_count(forum.thread_set.filter(participants__id=request.user.pk))
+            request.user.sync_unread_pds(unread_pds)
             request.user.save(force_update=True)
             request.user.save(force_update=True)

+ 28 - 28
misago/middleware/session.py

@@ -1,28 +1,28 @@
-from django.utils import timezone
-from misago.sessions import CrawlerSession, HumanSession
-
-class SessionMiddleware(object):
-    def process_request(self, request):
-        try:
-            if request.user.is_crawler():
-                # Crawler Session
-                request.session = CrawlerSession(request)
-        except AttributeError:
-            # Human Session
-            request.session = HumanSession(request)
-            request.user = request.session.get_user()
-
-    def process_response(self, request, response):
-        try:
-            # Sync last visit date
-            if request.user.is_authenticated():
-                visit_sync = request.session.get('visit_sync')
-                if not visit_sync or (timezone.now() - visit_sync).seconds >= 900:
-                    request.session['visit_sync'] = timezone.now()
-                    request.user.last_date = timezone.now()
-                    request.user.save(force_update=True)
-            request.session.match()
-            request.session.save()
-        except AttributeError:
-            pass
-        return response
+from django.utils import timezone
+from misago.sessions import CrawlerSession, HumanSession
+
+class SessionMiddleware(object):
+    def process_request(self, request):
+        try:
+            if request.user.is_crawler():
+                # Crawler Session
+                request.session = CrawlerSession(request)
+        except AttributeError:
+            # Human Session
+            request.session = HumanSession(request)
+            request.user = request.session.get_user()
+
+    def process_response(self, request, response):
+        try:
+            # Sync last visit date
+            if request.user.is_authenticated():
+                visit_sync = request.session.get('visit_sync')
+                if not visit_sync or (timezone.now() - visit_sync).seconds >= 900:
+                    request.session['visit_sync'] = timezone.now()
+                    request.user.last_date = timezone.now()
+                    request.user.save(force_update=True)
+            request.session.match()
+            request.session.save()
+        except AttributeError:
+            pass
+        return response

+ 16 - 16
misago/middleware/stopwatch.py

@@ -1,16 +1,16 @@
-from django.conf import settings
-from misago.stopwatch import Stopwatch
-
-class StopwatchMiddleware(object):
-    def process_request(self, request):
-        request.stopwatch = Stopwatch()
-
-    def process_response(self, request, response):
-        try:
-            if settings.STOPWATCH_LOG:
-                stat_file = open(settings.STOPWATCH_LOG, 'a')
-                stat_file.write("%s %s s\n" % (request.path_info, request.stopwatch.time()))
-                stat_file.close()
-        except AttributeError:
-            pass
-        return response
+from django.conf import settings
+from misago.stopwatch import Stopwatch
+
+class StopwatchMiddleware(object):
+    def process_request(self, request):
+        request.stopwatch = Stopwatch()
+
+    def process_response(self, request, response):
+        try:
+            if settings.STOPWATCH_LOG:
+                stat_file = open(settings.STOPWATCH_LOG, 'a')
+                stat_file.write("%s %s s\n" % (request.path_info, request.stopwatch.time()))
+                stat_file.close()
+        except AttributeError:
+            pass
+        return response

+ 22 - 22
misago/middleware/theme.py

@@ -1,22 +1,22 @@
-from urlparse import urlparse
-from django.conf import settings
-from django.core.cache import cache
-from misago.template.theme import activate_theme
-
-class ThemeMiddleware(object):
-    def process_request(self, request):
-        if not settings.INSTALLED_THEMES:
-            raise ValueError('There are no themes installed!')
-
-        activate_theme(settings.INSTALLED_THEMES[0])
-
-        if settings.MOBILE_SUBDOMAIN and settings.MOBILE_TEMPLATES:
-            if settings.MOBILE_SUBDOMAIN == '*':
-                activate_theme(settings.MOBILE_TEMPLATES)
-            else:
-                mobile_domain = '%s.%s/' % (settings.MOBILE_SUBDOMAIN, urlparse(settings.BOARD_ADDRESS).netloc)
-                current_domain = '%s.%s/' % (settings.MOBILE_SUBDOMAIN, urlparse(request.META.get('HTTP_HOST')).netloc)
-                
-                if current_domain == mobile_domain:
-                    activate_theme(settings.MOBILE_TEMPLATES)
-                    
+from urlparse import urlparse
+from django.conf import settings
+from django.core.cache import cache
+from misago.template.theme import activate_theme
+
+class ThemeMiddleware(object):
+    def process_request(self, request):
+        if not settings.INSTALLED_THEMES:
+            raise ValueError('There are no themes installed!')
+
+        activate_theme(settings.INSTALLED_THEMES[0])
+
+        if settings.MOBILE_SUBDOMAIN and settings.MOBILE_TEMPLATES:
+            if settings.MOBILE_SUBDOMAIN == '*':
+                activate_theme(settings.MOBILE_TEMPLATES)
+            else:
+                mobile_domain = '%s.%s/' % (settings.MOBILE_SUBDOMAIN, urlparse(settings.BOARD_ADDRESS).netloc)
+                current_domain = '%s.%s/' % (settings.MOBILE_SUBDOMAIN, urlparse(request.META.get('HTTP_HOST')).netloc)
+                
+                if current_domain == mobile_domain:
+                    activate_theme(settings.MOBILE_TEMPLATES)
+                    

+ 4 - 4
misago/middleware/thread.py

@@ -1,5 +1,5 @@
-from misago.thread import clear
-
-class ThreadMiddleware(object):
-    def process_request(self, request):
+from misago.thread import clear
+
+class ThreadMiddleware(object):
+    def process_request(self, request):
         clear()
         clear()

+ 34 - 34
misago/middleware/user.py

@@ -1,34 +1,34 @@
-from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
-from misago.conf import settings
-from misago.messages import Message
-from misago.monitor import monitor, UpdatingMonitor
-from misago.onlines import MembersOnline
-
-def set_timezone(new_tz):
-    if settings.USE_TZ:
-        try:
-            import pytz
-            timezone.activate(pytz.timezone(new_tz))
-        except ImportError:
-            pass
-
-
-class UserMiddleware(object):
-    def process_request(self, request):
-        if request.user.is_authenticated():
-            request.session.rank = request.user.rank_id
-            set_timezone(request.user.timezone)
-            if request.session.remember_me:
-                request.messages.set_message(Message(_("Welcome back, %(username)s! We've signed you in automatically for your convenience.") % {'username': request.user.username}), 'info')
-        else:
-            set_timezone(settings.default_timezone)
-            request.session.rank = None
-        request.onlines = MembersOnline(settings.online_counting, settings.online_counting_frequency)
-
-    def process_response(self, request, response):
-        try:
-            request.onlines.sync()
-        except AttributeError:
-            pass
-        return response
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
+from misago.conf import settings
+from misago.messages import Message
+from misago.monitor import monitor, UpdatingMonitor
+from misago.onlines import MembersOnline
+
+def set_timezone(new_tz):
+    if settings.USE_TZ:
+        try:
+            import pytz
+            timezone.activate(pytz.timezone(new_tz))
+        except ImportError:
+            pass
+
+
+class UserMiddleware(object):
+    def process_request(self, request):
+        if request.user.is_authenticated():
+            request.session.rank = request.user.rank_id
+            set_timezone(request.user.timezone)
+            if request.session.remember_me:
+                request.messages.set_message(Message(_("Welcome back, %(username)s! We've signed you in automatically for your convenience.") % {'username': request.user.username}), 'info')
+        else:
+            set_timezone(settings.default_timezone)
+            request.session.rank = None
+        request.onlines = MembersOnline(settings.online_counting, settings.online_counting_frequency)
+
+    def process_response(self, request, response):
+        try:
+            request.onlines.sync()
+        except AttributeError:
+            pass
+        return response

+ 950 - 950
misago/migrations/0001_initial.py

@@ -1,951 +1,951 @@
-# -*- coding: 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 'Alert'
-        db.create_table(u'misago_alert', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'])),
-            ('date', self.gf('django.db.models.fields.DateTimeField')()),
-            ('message', self.gf('django.db.models.fields.TextField')()),
-            ('variables', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['Alert'])
-
-        # Adding model 'Ban'
-        db.create_table(u'misago_ban', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('test', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('ban', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('reason_user', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('reason_admin', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('expires', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['Ban'])
-
-        # Adding model 'Change'
-        db.create_table(u'misago_change', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
-            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
-            ('post', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Post'])),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
-            ('user_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('user_slug', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('date', self.gf('django.db.models.fields.DateTimeField')()),
-            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
-            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('reason', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('thread_name_new', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('thread_name_old', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('post_content', self.gf('django.db.models.fields.TextField')()),
-            ('size', self.gf('django.db.models.fields.IntegerField')(default=0)),
-            ('change', self.gf('django.db.models.fields.IntegerField')(default=0)),
-        ))
-        db.send_create_signal('misago', ['Change'])
-
-        # Adding model 'Checkpoint'
-        db.create_table(u'misago_checkpoint', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
-            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
-            ('post', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Post'])),
-            ('action', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
-            ('user_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('user_slug', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('target_user', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
-            ('target_user_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('target_user_slug', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('date', self.gf('django.db.models.fields.DateTimeField')()),
-            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
-            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
-        ))
-        db.send_create_signal('misago', ['Checkpoint'])
-
-        # Adding model 'Fixture'
-        db.create_table(u'misago_fixture', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-        ))
-        db.send_create_signal('misago', ['Fixture'])
-
-        # Adding model 'Forum'
-        db.create_table(u'misago_forum', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('parent', self.gf('mptt.fields.TreeForeignKey')(blank=True, related_name='children', null=True, to=orm['misago.Forum'])),
-            ('type', self.gf('django.db.models.fields.CharField')(max_length=12)),
-            ('special', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255)),
-            ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('description_preparsed', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('threads', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('threads_delta', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('posts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('posts_delta', self.gf('django.db.models.fields.IntegerField')(default=0)),
-            ('redirects', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('redirects_delta', self.gf('django.db.models.fields.IntegerField')(default=0)),
-            ('last_thread', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.Thread'])),
-            ('last_thread_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('last_thread_slug', self.gf('django.db.models.fields.SlugField')(max_length=255, null=True, blank=True)),
-            ('last_thread_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-            ('last_poster', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
-            ('last_poster_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('last_poster_slug', self.gf('django.db.models.fields.SlugField')(max_length=255, null=True, blank=True)),
-            ('last_poster_style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('prune_start', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('prune_last', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('redirect', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('attrs', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('show_details', self.gf('django.db.models.fields.BooleanField')(default=True)),
-            ('style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('closed', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
-            ('rght', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
-            ('tree_id', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
-            ('level', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
-        ))
-        db.send_create_signal('misago', ['Forum'])
-
-        # Adding model 'ForumRead'
-        db.create_table(u'misago_forumread', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'])),
-            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
-            ('updated', self.gf('django.db.models.fields.DateTimeField')()),
-            ('cleared', self.gf('django.db.models.fields.DateTimeField')()),
-        ))
-        db.send_create_signal('misago', ['ForumRead'])
-
-        # Adding model 'ForumRole'
-        db.create_table(u'misago_forumrole', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('_permissions', self.gf('django.db.models.fields.TextField')(null=True, db_column='permissions', blank=True)),
-        ))
-        db.send_create_signal('misago', ['ForumRole'])
-
-        # Adding model 'Karma'
-        db.create_table(u'misago_karma', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
-            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
-            ('post', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Post'])),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
-            ('user_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('user_slug', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('date', self.gf('django.db.models.fields.DateTimeField')()),
-            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
-            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('score', self.gf('django.db.models.fields.IntegerField')(default=0)),
-        ))
-        db.send_create_signal('misago', ['Karma'])
-
-        # Adding model 'MonitorItem'
-        db.create_table(u'misago_monitoritem', (
-            ('id', self.gf('django.db.models.fields.CharField')(max_length=255, primary_key=True)),
-            ('value', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('updated', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['MonitorItem'])
-
-        # Adding model 'Newsletter'
-        db.create_table(u'misago_newsletter', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('token', self.gf('django.db.models.fields.CharField')(max_length=32)),
-            ('step_size', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('progress', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('content_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('content_plain', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('ignore_subscriptions', self.gf('django.db.models.fields.BooleanField')(default=False)),
-        ))
-        db.send_create_signal('misago', ['Newsletter'])
-
-        # Adding M2M table for field ranks on 'Newsletter'
-        db.create_table(u'misago_newsletter_ranks', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('newsletter', models.ForeignKey(orm['misago.newsletter'], null=False)),
-            ('rank', models.ForeignKey(orm['misago.rank'], null=False))
-        ))
-        db.create_unique(u'misago_newsletter_ranks', ['newsletter_id', 'rank_id'])
-
-        # Adding model 'Post'
-        db.create_table(u'misago_post', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
-            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
-            ('merge', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
-            ('user_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
-            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('post', self.gf('django.db.models.fields.TextField')()),
-            ('post_preparsed', self.gf('django.db.models.fields.TextField')()),
-            ('upvotes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('downvotes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('checkpoints', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('date', self.gf('django.db.models.fields.DateTimeField')()),
-            ('edits', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('edit_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-            ('edit_reason', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('edit_user', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
-            ('edit_user_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('edit_user_slug', self.gf('django.db.models.fields.SlugField')(max_length=255, null=True, blank=True)),
-            ('reported', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('moderated', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('protected', self.gf('django.db.models.fields.BooleanField')(default=False)),
-        ))
-        db.send_create_signal('misago', ['Post'])
-
-        # Adding M2M table for field mentions on 'Post'
-        db.create_table(u'misago_post_mentions', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('post', models.ForeignKey(orm['misago.post'], null=False)),
-            ('user', models.ForeignKey(orm['misago.user'], null=False))
-        ))
-        db.create_unique(u'misago_post_mentions', ['post_id', 'user_id'])
-
-        # Adding model 'PruningPolicy'
-        db.create_table(u'misago_pruningpolicy', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('email', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('posts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('registered', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('last_visit', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-        ))
-        db.send_create_signal('misago', ['PruningPolicy'])
-
-        # Adding model 'Rank'
-        db.create_table(u'misago_rank', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('slug', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('special', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('as_tab', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('on_index', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
-            ('criteria', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['Rank'])
-
-        # Adding model 'Role'
-        db.create_table(u'misago_role', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('_special', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, db_column='special', blank=True)),
-            ('protected', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('_permissions', self.gf('django.db.models.fields.TextField')(null=True, db_column='permissions', blank=True)),
-        ))
-        db.send_create_signal('misago', ['Role'])
-
-        # Adding model 'Session'
-        db.create_table(u'misago_session', (
-            ('id', self.gf('django.db.models.fields.CharField')(max_length=42, primary_key=True)),
-            ('data', self.gf('django.db.models.fields.TextField')(db_column='session_data')),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='sessions', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
-            ('crawler', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
-            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('start', self.gf('django.db.models.fields.DateTimeField')()),
-            ('last', self.gf('django.db.models.fields.DateTimeField')()),
-            ('team', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('rank', self.gf('django.db.models.fields.related.ForeignKey')(related_name='sessions', null=True, on_delete=models.SET_NULL, to=orm['misago.Rank'])),
-            ('admin', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('matched', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('hidden', self.gf('django.db.models.fields.BooleanField')(default=False)),
-        ))
-        db.send_create_signal('misago', ['Session'])
-
-        # Adding model 'Setting'
-        db.create_table(u'misago_setting', (
-            ('setting', self.gf('django.db.models.fields.CharField')(max_length=255, primary_key=True)),
-            ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.SettingsGroup'], to_field='key')),
-            ('value', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('value_default', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('normalize_to', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('field', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('extra', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('position', self.gf('django.db.models.fields.IntegerField')(default=0)),
-            ('separator', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['Setting'])
-
-        # Adding model 'SettingsGroup'
-        db.create_table(u'misago_settingsgroup', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['SettingsGroup'])
-
-        # Adding model 'SignInAttempt'
-        db.create_table(u'misago_signinattempt', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
-            ('date', self.gf('django.db.models.fields.DateTimeField')()),
-        ))
-        db.send_create_signal('misago', ['SignInAttempt'])
-
-        # Adding model 'ThemeAdjustment'
-        db.create_table(u'misago_themeadjustment', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('theme', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
-            ('useragents', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['ThemeAdjustment'])
-
-        # Adding model 'Thread'
-        db.create_table(u'misago_thread', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
-            ('weight', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255)),
-            ('replies', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('replies_reported', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('replies_moderated', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('replies_deleted', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('merges', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('score', self.gf('django.db.models.fields.PositiveIntegerField')(default=30)),
-            ('upvotes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('downvotes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('start', self.gf('django.db.models.fields.DateTimeField')()),
-            ('start_post', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.Post'])),
-            ('start_poster', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
-            ('start_poster_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('start_poster_slug', self.gf('django.db.models.fields.SlugField')(max_length=255)),
-            ('start_poster_style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('last', self.gf('django.db.models.fields.DateTimeField')()),
-            ('last_post', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.Post'])),
-            ('last_poster', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
-            ('last_poster_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('last_poster_slug', self.gf('django.db.models.fields.SlugField')(max_length=255, null=True, blank=True)),
-            ('last_poster_style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('moderated', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('closed', self.gf('django.db.models.fields.BooleanField')(default=False)),
-        ))
-        db.send_create_signal('misago', ['Thread'])
-
-        # Adding M2M table for field participants on 'Thread'
-        db.create_table(u'misago_thread_participants', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('thread', models.ForeignKey(orm['misago.thread'], null=False)),
-            ('user', models.ForeignKey(orm['misago.user'], null=False))
-        ))
-        db.create_unique(u'misago_thread_participants', ['thread_id', 'user_id'])
-
-        # Adding model 'ThreadRead'
-        db.create_table(u'misago_threadread', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'])),
-            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
-            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
-            ('updated', self.gf('django.db.models.fields.DateTimeField')()),
-        ))
-        db.send_create_signal('misago', ['ThreadRead'])
-
-        # Adding model 'Token'
-        db.create_table(u'misago_token', (
-            ('id', self.gf('django.db.models.fields.CharField')(max_length=42, primary_key=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='signin_tokens', to=orm['misago.User'])),
-            ('created', self.gf('django.db.models.fields.DateTimeField')()),
-            ('accessed', self.gf('django.db.models.fields.DateTimeField')()),
-        ))
-        db.send_create_signal('misago', ['Token'])
-
-        # Adding model 'User'
-        db.create_table(u'misago_user', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('username', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('username_slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=255)),
-            ('email', self.gf('django.db.models.fields.EmailField')(max_length=255)),
-            ('email_hash', self.gf('django.db.models.fields.CharField')(unique=True, max_length=32)),
-            ('password', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('password_date', self.gf('django.db.models.fields.DateTimeField')()),
-            ('avatar_type', self.gf('django.db.models.fields.CharField')(max_length=10, null=True, blank=True)),
-            ('avatar_image', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('avatar_original', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('avatar_temp', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('signature', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('signature_preparsed', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('join_date', self.gf('django.db.models.fields.DateTimeField')()),
-            ('join_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
-            ('join_agent', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('last_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-            ('last_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True, blank=True)),
-            ('last_agent', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('hide_activity', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('subscribe_start', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('subscribe_reply', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('receive_newsletters', self.gf('django.db.models.fields.BooleanField')(default=True)),
-            ('threads', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('posts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('votes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('karma_given_p', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('karma_given_n', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('karma_p', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('karma_n', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('following', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('followers', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('score', self.gf('django.db.models.fields.IntegerField')(default=0)),
-            ('ranking', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('rank', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Rank'], null=True, on_delete=models.SET_NULL, blank=True)),
-            ('last_sync', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
-            ('last_post', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-            ('last_search', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-            ('alerts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('alerts_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
-            ('allow_pds', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('unread_pds', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
-            ('sync_pds', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('activation', self.gf('django.db.models.fields.IntegerField')(default=0)),
-            ('token', self.gf('django.db.models.fields.CharField')(max_length=12, null=True, blank=True)),
-            ('avatar_ban', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('avatar_ban_reason_user', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('avatar_ban_reason_admin', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('signature_ban', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('signature_ban_reason_user', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('signature_ban_reason_admin', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('timezone', self.gf('django.db.models.fields.CharField')(default='utc', max_length=255)),
-            ('is_team', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('acl_key', self.gf('django.db.models.fields.CharField')(max_length=12, null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['User'])
-
-        # Adding M2M table for field follows on 'User'
-        db.create_table(u'misago_user_follows', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('from_user', models.ForeignKey(orm['misago.user'], null=False)),
-            ('to_user', models.ForeignKey(orm['misago.user'], null=False))
-        ))
-        db.create_unique(u'misago_user_follows', ['from_user_id', 'to_user_id'])
-
-        # Adding M2M table for field ignores on 'User'
-        db.create_table(u'misago_user_ignores', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('from_user', models.ForeignKey(orm['misago.user'], null=False)),
-            ('to_user', models.ForeignKey(orm['misago.user'], null=False))
-        ))
-        db.create_unique(u'misago_user_ignores', ['from_user_id', 'to_user_id'])
-
-        # Adding M2M table for field roles on 'User'
-        db.create_table(u'misago_user_roles', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('user', models.ForeignKey(orm['misago.user'], null=False)),
-            ('role', models.ForeignKey(orm['misago.role'], null=False))
-        ))
-        db.create_unique(u'misago_user_roles', ['user_id', 'role_id'])
-
-        # Adding model 'UsernameChange'
-        db.create_table(u'misago_usernamechange', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='namechanges', to=orm['misago.User'])),
-            ('date', self.gf('django.db.models.fields.DateTimeField')()),
-            ('old_username', self.gf('django.db.models.fields.CharField')(max_length=255)),
-        ))
-        db.send_create_signal('misago', ['UsernameChange'])
-
-        # Adding model 'WatchedThread'
-        db.create_table(u'misago_watchedthread', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'])),
-            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
-            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
-            ('last_read', self.gf('django.db.models.fields.DateTimeField')()),
-            ('email', self.gf('django.db.models.fields.BooleanField')(default=False)),
-        ))
-        db.send_create_signal('misago', ['WatchedThread'])
-
-
-    def backwards(self, orm):
-        # Deleting model 'Alert'
-        db.delete_table(u'misago_alert')
-
-        # Deleting model 'Ban'
-        db.delete_table(u'misago_ban')
-
-        # Deleting model 'Change'
-        db.delete_table(u'misago_change')
-
-        # Deleting model 'Checkpoint'
-        db.delete_table(u'misago_checkpoint')
-
-        # Deleting model 'Fixture'
-        db.delete_table(u'misago_fixture')
-
-        # Deleting model 'Forum'
-        db.delete_table(u'misago_forum')
-
-        # Deleting model 'ForumRead'
-        db.delete_table(u'misago_forumread')
-
-        # Deleting model 'ForumRole'
-        db.delete_table(u'misago_forumrole')
-
-        # Deleting model 'Karma'
-        db.delete_table(u'misago_karma')
-
-        # Deleting model 'MonitorItem'
-        db.delete_table(u'misago_monitoritem')
-
-        # Deleting model 'Newsletter'
-        db.delete_table(u'misago_newsletter')
-
-        # Removing M2M table for field ranks on 'Newsletter'
-        db.delete_table('misago_newsletter_ranks')
-
-        # Deleting model 'Post'
-        db.delete_table(u'misago_post')
-
-        # Removing M2M table for field mentions on 'Post'
-        db.delete_table('misago_post_mentions')
-
-        # Deleting model 'PruningPolicy'
-        db.delete_table(u'misago_pruningpolicy')
-
-        # Deleting model 'Rank'
-        db.delete_table(u'misago_rank')
-
-        # Deleting model 'Role'
-        db.delete_table(u'misago_role')
-
-        # Deleting model 'Session'
-        db.delete_table(u'misago_session')
-
-        # Deleting model 'Setting'
-        db.delete_table(u'misago_setting')
-
-        # Deleting model 'SettingsGroup'
-        db.delete_table(u'misago_settingsgroup')
-
-        # Deleting model 'SignInAttempt'
-        db.delete_table(u'misago_signinattempt')
-
-        # Deleting model 'ThemeAdjustment'
-        db.delete_table(u'misago_themeadjustment')
-
-        # Deleting model 'Thread'
-        db.delete_table(u'misago_thread')
-
-        # Removing M2M table for field participants on 'Thread'
-        db.delete_table('misago_thread_participants')
-
-        # Deleting model 'ThreadRead'
-        db.delete_table(u'misago_threadread')
-
-        # Deleting model 'Token'
-        db.delete_table(u'misago_token')
-
-        # Deleting model 'User'
-        db.delete_table(u'misago_user')
-
-        # Removing M2M table for field follows on 'User'
-        db.delete_table('misago_user_follows')
-
-        # Removing M2M table for field ignores on 'User'
-        db.delete_table('misago_user_ignores')
-
-        # Removing M2M table for field roles on 'User'
-        db.delete_table('misago_user_roles')
-
-        # Deleting model 'UsernameChange'
-        db.delete_table(u'misago_usernamechange')
-
-        # Deleting model 'WatchedThread'
-        db.delete_table(u'misago_watchedthread')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.themeadjustment': {
-            'Meta': {'object_name': 'ThemeAdjustment'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'+'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 'Alert'
+        db.create_table(u'misago_alert', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'])),
+            ('date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('message', self.gf('django.db.models.fields.TextField')()),
+            ('variables', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['Alert'])
+
+        # Adding model 'Ban'
+        db.create_table(u'misago_ban', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('test', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('ban', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('reason_user', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('reason_admin', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('expires', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['Ban'])
+
+        # Adding model 'Change'
+        db.create_table(u'misago_change', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
+            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
+            ('post', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Post'])),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
+            ('user_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('user_slug', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
+            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('reason', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('thread_name_new', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('thread_name_old', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('post_content', self.gf('django.db.models.fields.TextField')()),
+            ('size', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('change', self.gf('django.db.models.fields.IntegerField')(default=0)),
+        ))
+        db.send_create_signal('misago', ['Change'])
+
+        # Adding model 'Checkpoint'
+        db.create_table(u'misago_checkpoint', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
+            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
+            ('post', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Post'])),
+            ('action', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
+            ('user_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('user_slug', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('target_user', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
+            ('target_user_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('target_user_slug', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
+            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
+        ))
+        db.send_create_signal('misago', ['Checkpoint'])
+
+        # Adding model 'Fixture'
+        db.create_table(u'misago_fixture', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+        ))
+        db.send_create_signal('misago', ['Fixture'])
+
+        # Adding model 'Forum'
+        db.create_table(u'misago_forum', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('parent', self.gf('mptt.fields.TreeForeignKey')(blank=True, related_name='children', null=True, to=orm['misago.Forum'])),
+            ('type', self.gf('django.db.models.fields.CharField')(max_length=12)),
+            ('special', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255)),
+            ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('description_preparsed', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('threads', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('threads_delta', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('posts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('posts_delta', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('redirects', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('redirects_delta', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('last_thread', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.Thread'])),
+            ('last_thread_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('last_thread_slug', self.gf('django.db.models.fields.SlugField')(max_length=255, null=True, blank=True)),
+            ('last_thread_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('last_poster', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
+            ('last_poster_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('last_poster_slug', self.gf('django.db.models.fields.SlugField')(max_length=255, null=True, blank=True)),
+            ('last_poster_style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('prune_start', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('prune_last', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('redirect', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('attrs', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('show_details', self.gf('django.db.models.fields.BooleanField')(default=True)),
+            ('style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('closed', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
+            ('rght', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
+            ('tree_id', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
+            ('level', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
+        ))
+        db.send_create_signal('misago', ['Forum'])
+
+        # Adding model 'ForumRead'
+        db.create_table(u'misago_forumread', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'])),
+            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
+            ('updated', self.gf('django.db.models.fields.DateTimeField')()),
+            ('cleared', self.gf('django.db.models.fields.DateTimeField')()),
+        ))
+        db.send_create_signal('misago', ['ForumRead'])
+
+        # Adding model 'ForumRole'
+        db.create_table(u'misago_forumrole', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('_permissions', self.gf('django.db.models.fields.TextField')(null=True, db_column='permissions', blank=True)),
+        ))
+        db.send_create_signal('misago', ['ForumRole'])
+
+        # Adding model 'Karma'
+        db.create_table(u'misago_karma', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
+            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
+            ('post', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Post'])),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
+            ('user_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('user_slug', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
+            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('score', self.gf('django.db.models.fields.IntegerField')(default=0)),
+        ))
+        db.send_create_signal('misago', ['Karma'])
+
+        # Adding model 'MonitorItem'
+        db.create_table(u'misago_monitoritem', (
+            ('id', self.gf('django.db.models.fields.CharField')(max_length=255, primary_key=True)),
+            ('value', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('updated', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['MonitorItem'])
+
+        # Adding model 'Newsletter'
+        db.create_table(u'misago_newsletter', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('token', self.gf('django.db.models.fields.CharField')(max_length=32)),
+            ('step_size', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('progress', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('content_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('content_plain', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('ignore_subscriptions', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal('misago', ['Newsletter'])
+
+        # Adding M2M table for field ranks on 'Newsletter'
+        db.create_table(u'misago_newsletter_ranks', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('newsletter', models.ForeignKey(orm['misago.newsletter'], null=False)),
+            ('rank', models.ForeignKey(orm['misago.rank'], null=False))
+        ))
+        db.create_unique(u'misago_newsletter_ranks', ['newsletter_id', 'rank_id'])
+
+        # Adding model 'Post'
+        db.create_table(u'misago_post', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
+            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
+            ('merge', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
+            ('user_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
+            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('post', self.gf('django.db.models.fields.TextField')()),
+            ('post_preparsed', self.gf('django.db.models.fields.TextField')()),
+            ('upvotes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('downvotes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('checkpoints', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('edits', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('edit_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('edit_reason', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('edit_user', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
+            ('edit_user_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('edit_user_slug', self.gf('django.db.models.fields.SlugField')(max_length=255, null=True, blank=True)),
+            ('reported', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('moderated', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('protected', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal('misago', ['Post'])
+
+        # Adding M2M table for field mentions on 'Post'
+        db.create_table(u'misago_post_mentions', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('post', models.ForeignKey(orm['misago.post'], null=False)),
+            ('user', models.ForeignKey(orm['misago.user'], null=False))
+        ))
+        db.create_unique(u'misago_post_mentions', ['post_id', 'user_id'])
+
+        # Adding model 'PruningPolicy'
+        db.create_table(u'misago_pruningpolicy', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('email', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('posts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('registered', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('last_visit', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+        ))
+        db.send_create_signal('misago', ['PruningPolicy'])
+
+        # Adding model 'Rank'
+        db.create_table(u'misago_rank', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('slug', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('special', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('as_tab', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('on_index', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('criteria', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['Rank'])
+
+        # Adding model 'Role'
+        db.create_table(u'misago_role', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('_special', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, db_column='special', blank=True)),
+            ('protected', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('_permissions', self.gf('django.db.models.fields.TextField')(null=True, db_column='permissions', blank=True)),
+        ))
+        db.send_create_signal('misago', ['Role'])
+
+        # Adding model 'Session'
+        db.create_table(u'misago_session', (
+            ('id', self.gf('django.db.models.fields.CharField')(max_length=42, primary_key=True)),
+            ('data', self.gf('django.db.models.fields.TextField')(db_column='session_data')),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='sessions', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
+            ('crawler', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
+            ('agent', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('start', self.gf('django.db.models.fields.DateTimeField')()),
+            ('last', self.gf('django.db.models.fields.DateTimeField')()),
+            ('team', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('rank', self.gf('django.db.models.fields.related.ForeignKey')(related_name='sessions', null=True, on_delete=models.SET_NULL, to=orm['misago.Rank'])),
+            ('admin', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('matched', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('hidden', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal('misago', ['Session'])
+
+        # Adding model 'Setting'
+        db.create_table(u'misago_setting', (
+            ('setting', self.gf('django.db.models.fields.CharField')(max_length=255, primary_key=True)),
+            ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.SettingsGroup'], to_field='key')),
+            ('value', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('value_default', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('normalize_to', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('field', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('extra', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('position', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('separator', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['Setting'])
+
+        # Adding model 'SettingsGroup'
+        db.create_table(u'misago_settingsgroup', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['SettingsGroup'])
+
+        # Adding model 'SignInAttempt'
+        db.create_table(u'misago_signinattempt', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
+            ('date', self.gf('django.db.models.fields.DateTimeField')()),
+        ))
+        db.send_create_signal('misago', ['SignInAttempt'])
+
+        # Adding model 'ThemeAdjustment'
+        db.create_table(u'misago_themeadjustment', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('theme', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
+            ('useragents', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['ThemeAdjustment'])
+
+        # Adding model 'Thread'
+        db.create_table(u'misago_thread', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
+            ('weight', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255)),
+            ('replies', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('replies_reported', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('replies_moderated', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('replies_deleted', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('merges', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('score', self.gf('django.db.models.fields.PositiveIntegerField')(default=30)),
+            ('upvotes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('downvotes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('start', self.gf('django.db.models.fields.DateTimeField')()),
+            ('start_post', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.Post'])),
+            ('start_poster', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'], null=True, on_delete=models.SET_NULL, blank=True)),
+            ('start_poster_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('start_poster_slug', self.gf('django.db.models.fields.SlugField')(max_length=255)),
+            ('start_poster_style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('last', self.gf('django.db.models.fields.DateTimeField')()),
+            ('last_post', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.Post'])),
+            ('last_poster', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.User'])),
+            ('last_poster_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('last_poster_slug', self.gf('django.db.models.fields.SlugField')(max_length=255, null=True, blank=True)),
+            ('last_poster_style', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('moderated', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('closed', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal('misago', ['Thread'])
+
+        # Adding M2M table for field participants on 'Thread'
+        db.create_table(u'misago_thread_participants', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('thread', models.ForeignKey(orm['misago.thread'], null=False)),
+            ('user', models.ForeignKey(orm['misago.user'], null=False))
+        ))
+        db.create_unique(u'misago_thread_participants', ['thread_id', 'user_id'])
+
+        # Adding model 'ThreadRead'
+        db.create_table(u'misago_threadread', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'])),
+            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
+            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
+            ('updated', self.gf('django.db.models.fields.DateTimeField')()),
+        ))
+        db.send_create_signal('misago', ['ThreadRead'])
+
+        # Adding model 'Token'
+        db.create_table(u'misago_token', (
+            ('id', self.gf('django.db.models.fields.CharField')(max_length=42, primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='signin_tokens', to=orm['misago.User'])),
+            ('created', self.gf('django.db.models.fields.DateTimeField')()),
+            ('accessed', self.gf('django.db.models.fields.DateTimeField')()),
+        ))
+        db.send_create_signal('misago', ['Token'])
+
+        # Adding model 'User'
+        db.create_table(u'misago_user', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('username', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('username_slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=255)),
+            ('email', self.gf('django.db.models.fields.EmailField')(max_length=255)),
+            ('email_hash', self.gf('django.db.models.fields.CharField')(unique=True, max_length=32)),
+            ('password', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('password_date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('avatar_type', self.gf('django.db.models.fields.CharField')(max_length=10, null=True, blank=True)),
+            ('avatar_image', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('avatar_original', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('avatar_temp', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('signature', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('signature_preparsed', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('join_date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('join_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
+            ('join_agent', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('last_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('last_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True, blank=True)),
+            ('last_agent', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('hide_activity', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('subscribe_start', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('subscribe_reply', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('receive_newsletters', self.gf('django.db.models.fields.BooleanField')(default=True)),
+            ('threads', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('posts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('votes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('karma_given_p', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('karma_given_n', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('karma_p', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('karma_n', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('following', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('followers', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('score', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('ranking', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('rank', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Rank'], null=True, on_delete=models.SET_NULL, blank=True)),
+            ('last_sync', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('last_post', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('last_search', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('alerts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('alerts_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('allow_pds', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('unread_pds', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+            ('sync_pds', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('activation', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('token', self.gf('django.db.models.fields.CharField')(max_length=12, null=True, blank=True)),
+            ('avatar_ban', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('avatar_ban_reason_user', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('avatar_ban_reason_admin', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('signature_ban', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('signature_ban_reason_user', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('signature_ban_reason_admin', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('timezone', self.gf('django.db.models.fields.CharField')(default='utc', max_length=255)),
+            ('is_team', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('acl_key', self.gf('django.db.models.fields.CharField')(max_length=12, null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['User'])
+
+        # Adding M2M table for field follows on 'User'
+        db.create_table(u'misago_user_follows', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('from_user', models.ForeignKey(orm['misago.user'], null=False)),
+            ('to_user', models.ForeignKey(orm['misago.user'], null=False))
+        ))
+        db.create_unique(u'misago_user_follows', ['from_user_id', 'to_user_id'])
+
+        # Adding M2M table for field ignores on 'User'
+        db.create_table(u'misago_user_ignores', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('from_user', models.ForeignKey(orm['misago.user'], null=False)),
+            ('to_user', models.ForeignKey(orm['misago.user'], null=False))
+        ))
+        db.create_unique(u'misago_user_ignores', ['from_user_id', 'to_user_id'])
+
+        # Adding M2M table for field roles on 'User'
+        db.create_table(u'misago_user_roles', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('user', models.ForeignKey(orm['misago.user'], null=False)),
+            ('role', models.ForeignKey(orm['misago.role'], null=False))
+        ))
+        db.create_unique(u'misago_user_roles', ['user_id', 'role_id'])
+
+        # Adding model 'UsernameChange'
+        db.create_table(u'misago_usernamechange', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='namechanges', to=orm['misago.User'])),
+            ('date', self.gf('django.db.models.fields.DateTimeField')()),
+            ('old_username', self.gf('django.db.models.fields.CharField')(max_length=255)),
+        ))
+        db.send_create_signal('misago', ['UsernameChange'])
+
+        # Adding model 'WatchedThread'
+        db.create_table(u'misago_watchedthread', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.User'])),
+            ('forum', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Forum'])),
+            ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['misago.Thread'])),
+            ('last_read', self.gf('django.db.models.fields.DateTimeField')()),
+            ('email', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal('misago', ['WatchedThread'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Alert'
+        db.delete_table(u'misago_alert')
+
+        # Deleting model 'Ban'
+        db.delete_table(u'misago_ban')
+
+        # Deleting model 'Change'
+        db.delete_table(u'misago_change')
+
+        # Deleting model 'Checkpoint'
+        db.delete_table(u'misago_checkpoint')
+
+        # Deleting model 'Fixture'
+        db.delete_table(u'misago_fixture')
+
+        # Deleting model 'Forum'
+        db.delete_table(u'misago_forum')
+
+        # Deleting model 'ForumRead'
+        db.delete_table(u'misago_forumread')
+
+        # Deleting model 'ForumRole'
+        db.delete_table(u'misago_forumrole')
+
+        # Deleting model 'Karma'
+        db.delete_table(u'misago_karma')
+
+        # Deleting model 'MonitorItem'
+        db.delete_table(u'misago_monitoritem')
+
+        # Deleting model 'Newsletter'
+        db.delete_table(u'misago_newsletter')
+
+        # Removing M2M table for field ranks on 'Newsletter'
+        db.delete_table('misago_newsletter_ranks')
+
+        # Deleting model 'Post'
+        db.delete_table(u'misago_post')
+
+        # Removing M2M table for field mentions on 'Post'
+        db.delete_table('misago_post_mentions')
+
+        # Deleting model 'PruningPolicy'
+        db.delete_table(u'misago_pruningpolicy')
+
+        # Deleting model 'Rank'
+        db.delete_table(u'misago_rank')
+
+        # Deleting model 'Role'
+        db.delete_table(u'misago_role')
+
+        # Deleting model 'Session'
+        db.delete_table(u'misago_session')
+
+        # Deleting model 'Setting'
+        db.delete_table(u'misago_setting')
+
+        # Deleting model 'SettingsGroup'
+        db.delete_table(u'misago_settingsgroup')
+
+        # Deleting model 'SignInAttempt'
+        db.delete_table(u'misago_signinattempt')
+
+        # Deleting model 'ThemeAdjustment'
+        db.delete_table(u'misago_themeadjustment')
+
+        # Deleting model 'Thread'
+        db.delete_table(u'misago_thread')
+
+        # Removing M2M table for field participants on 'Thread'
+        db.delete_table('misago_thread_participants')
+
+        # Deleting model 'ThreadRead'
+        db.delete_table(u'misago_threadread')
+
+        # Deleting model 'Token'
+        db.delete_table(u'misago_token')
+
+        # Deleting model 'User'
+        db.delete_table(u'misago_user')
+
+        # Removing M2M table for field follows on 'User'
+        db.delete_table('misago_user_follows')
+
+        # Removing M2M table for field ignores on 'User'
+        db.delete_table('misago_user_ignores')
+
+        # Removing M2M table for field roles on 'User'
+        db.delete_table('misago_user_roles')
+
+        # Deleting model 'UsernameChange'
+        db.delete_table(u'misago_usernamechange')
+
+        # Deleting model 'WatchedThread'
+        db.delete_table(u'misago_watchedthread')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.themeadjustment': {
+            'Meta': {'object_name': 'ThemeAdjustment'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'+'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 395 - 395
misago/migrations/0002_auto__del_field_session_hidden.py

@@ -1,396 +1,396 @@
-# -*- coding: 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):
-        # Deleting field 'Session.hidden'
-        db.delete_column(u'misago_session', 'hidden')
-
-
-    def backwards(self, orm):
-        # Adding field 'Session.hidden'
-        db.add_column(u'misago_session', 'hidden',
-                      self.gf('django.db.models.fields.BooleanField')(default=False),
-                      keep_default=False)
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.themeadjustment': {
-            'Meta': {'object_name': 'ThemeAdjustment'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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):
+        # Deleting field 'Session.hidden'
+        db.delete_column(u'misago_session', 'hidden')
+
+
+    def backwards(self, orm):
+        # Adding field 'Session.hidden'
+        db.add_column(u'misago_session', 'hidden',
+                      self.gf('django.db.models.fields.BooleanField')(default=False),
+                      keep_default=False)
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.themeadjustment': {
+            'Meta': {'object_name': 'ThemeAdjustment'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 414 - 414
misago/migrations/0003_auto__add_field_checkpoint_old_forum__add_field_checkpoint_old_forum_n.py

@@ -1,415 +1,415 @@
-# -*- coding: 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 field 'Checkpoint.old_forum'
-        db.add_column(u'misago_checkpoint', 'old_forum',
-                      self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, to=orm['misago.Forum']),
-                      keep_default=False)
-
-        # Adding field 'Checkpoint.old_forum_name'
-        db.add_column(u'misago_checkpoint', 'old_forum_name',
-                      self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True),
-                      keep_default=False)
-
-        # Adding field 'Checkpoint.old_forum_slug'
-        db.add_column(u'misago_checkpoint', 'old_forum_slug',
-                      self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Checkpoint.old_forum'
-        db.delete_column(u'misago_checkpoint', 'old_forum_id')
-
-        # Deleting field 'Checkpoint.old_forum_name'
-        db.delete_column(u'misago_checkpoint', 'old_forum_name')
-
-        # Deleting field 'Checkpoint.old_forum_slug'
-        db.delete_column(u'misago_checkpoint', 'old_forum_slug')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.themeadjustment': {
-            'Meta': {'object_name': 'ThemeAdjustment'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 field 'Checkpoint.old_forum'
+        db.add_column(u'misago_checkpoint', 'old_forum',
+                      self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, to=orm['misago.Forum']),
+                      keep_default=False)
+
+        # Adding field 'Checkpoint.old_forum_name'
+        db.add_column(u'misago_checkpoint', 'old_forum_name',
+                      self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True),
+                      keep_default=False)
+
+        # Adding field 'Checkpoint.old_forum_slug'
+        db.add_column(u'misago_checkpoint', 'old_forum_slug',
+                      self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Checkpoint.old_forum'
+        db.delete_column(u'misago_checkpoint', 'old_forum_id')
+
+        # Deleting field 'Checkpoint.old_forum_name'
+        db.delete_column(u'misago_checkpoint', 'old_forum_name')
+
+        # Deleting field 'Checkpoint.old_forum_slug'
+        db.delete_column(u'misago_checkpoint', 'old_forum_slug')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.themeadjustment': {
+            'Meta': {'object_name': 'ThemeAdjustment'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 399 - 399
misago/migrations/0004_auto__add_field_checkpoint_deleted.py

@@ -1,400 +1,400 @@
-# -*- coding: 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 field 'Checkpoint.deleted'
-        db.add_column(u'misago_checkpoint', 'deleted',
-                      self.gf('django.db.models.fields.BooleanField')(default=False),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Checkpoint.deleted'
-        db.delete_column(u'misago_checkpoint', 'deleted')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.themeadjustment': {
-            'Meta': {'object_name': 'ThemeAdjustment'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 field 'Checkpoint.deleted'
+        db.add_column(u'misago_checkpoint', 'deleted',
+                      self.gf('django.db.models.fields.BooleanField')(default=False),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Checkpoint.deleted'
+        db.delete_column(u'misago_checkpoint', 'deleted')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.themeadjustment': {
+            'Meta': {'object_name': 'ThemeAdjustment'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 400 - 400
misago/migrations/0005_auto__add_field_forum_pruned_archive.py

@@ -1,401 +1,401 @@
-# -*- coding: 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 field 'Forum.pruned_archive'
-        db.add_column(u'misago_forum', 'pruned_archive',
-                      self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.Forum']),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Forum.pruned_archive'
-        db.delete_column(u'misago_forum', 'pruned_archive_id')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.themeadjustment': {
-            'Meta': {'object_name': 'ThemeAdjustment'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 field 'Forum.pruned_archive'
+        db.add_column(u'misago_forum', 'pruned_archive',
+                      self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, on_delete=models.SET_NULL, to=orm['misago.Forum']),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Forum.pruned_archive'
+        db.delete_column(u'misago_forum', 'pruned_archive_id')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.themeadjustment': {
+            'Meta': {'object_name': 'ThemeAdjustment'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 404 - 404
misago/migrations/0006_auto.py

@@ -1,405 +1,405 @@
-# -*- coding: 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 M2M table for field roles on 'Rank'
-        db.create_table(u'misago_rank_roles', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('rank', models.ForeignKey(orm['misago.rank'], null=False)),
-            ('role', models.ForeignKey(orm['misago.role'], null=False))
-        ))
-        db.create_unique(u'misago_rank_roles', ['rank_id', 'role_id'])
-
-
-    def backwards(self, orm):
-        # Removing M2M table for field roles on 'Rank'
-        db.delete_table('misago_rank_roles')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.themeadjustment': {
-            'Meta': {'object_name': 'ThemeAdjustment'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 M2M table for field roles on 'Rank'
+        db.create_table(u'misago_rank_roles', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('rank', models.ForeignKey(orm['misago.rank'], null=False)),
+            ('role', models.ForeignKey(orm['misago.role'], null=False))
+        ))
+        db.create_unique(u'misago_rank_roles', ['rank_id', 'role_id'])
+
+
+    def backwards(self, orm):
+        # Removing M2M table for field roles on 'Rank'
+        db.delete_table('misago_rank_roles')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.themeadjustment': {
+            'Meta': {'object_name': 'ThemeAdjustment'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'theme': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'useragents': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 393 - 393
misago/migrations/0007_removethemeadjustments.py

@@ -1,394 +1,394 @@
-# -*- coding: 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):
-        db.delete_table('misago_themeadjustment')
-
-    def backwards(self, orm):
-        db.create_table(u'misago_themeadjustment', (
-            ('theme', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
-            ('useragents', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('misago', ['ThemeAdjustment'])
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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):
+        db.delete_table('misago_themeadjustment')
+
+    def backwards(self, orm):
+        db.create_table(u'misago_themeadjustment', (
+            ('theme', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
+            ('useragents', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('misago', ['ThemeAdjustment'])
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 396 - 396
misago/migrations/0008_auto__add_field_thread_report_for.py

@@ -1,397 +1,397 @@
-# -*- coding: 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 field 'Thread.report_for'
-        db.add_column(u'misago_thread', 'report_for',
-                      self.gf('django.db.models.fields.related.ForeignKey')(blank=True, db_index= True, related_name='reports', null=True, to=orm['misago.Post']),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Thread.report_for'
-        db.delete_column(u'misago_thread', 'report_for_id')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'reports'", 'null': 'True', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 field 'Thread.report_for'
+        db.add_column(u'misago_thread', 'report_for',
+                      self.gf('django.db.models.fields.related.ForeignKey')(blank=True, db_index= True, related_name='reports', null=True, to=orm['misago.Post']),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Thread.report_for'
+        db.delete_column(u'misago_thread', 'report_for_id')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'reports'", 'null': 'True', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 403 - 403
misago/migrations/0009_auto__chg_field_thread_report_for__add_field_monitoritem_type.py

@@ -1,404 +1,404 @@
-# -*- coding: 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):
-
-        # Changing field 'Thread.report_for'
-        db.alter_column(u'misago_thread', 'report_for_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, on_delete=models.SET_NULL, to=orm['misago.Post']))
-        # Adding field 'MonitorItem.type'
-        db.add_column(u'misago_monitoritem', 'type',
-                      self.gf('django.db.models.fields.CharField')(default='int', max_length=255),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-
-        # Changing field 'Thread.report_for'
-        db.alter_column(u'misago_thread', 'report_for_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['misago.Post']))
-        # Deleting field 'MonitorItem.type'
-        db.delete_column(u'misago_monitoritem', 'type')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'string'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'reports'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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):
+
+        # Changing field 'Thread.report_for'
+        db.alter_column(u'misago_thread', 'report_for_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, on_delete=models.SET_NULL, to=orm['misago.Post']))
+        # Adding field 'MonitorItem.type'
+        db.add_column(u'misago_monitoritem', 'type',
+                      self.gf('django.db.models.fields.CharField')(default='int', max_length=255),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+
+        # Changing field 'Thread.report_for'
+        db.alter_column(u'misago_thread', 'report_for_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['misago.Post']))
+        # Deleting field 'MonitorItem.type'
+        db.delete_column(u'misago_monitoritem', 'type')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'string'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'checkpoints': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'reports'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 395 - 395
misago/migrations/0010_auto__del_field_checkpoint_post__del_field_monitoritem_value__add_fiel.py

@@ -1,396 +1,396 @@
-# -*- coding: 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):
-        # Deleting field 'Checkpoint.post'
-        db.delete_column(u'misago_checkpoint', 'post_id')
-
-        # Deleting field 'Post.checkpoints'
-        db.delete_column(u'misago_post', 'checkpoints')
-
-
-    def backwards(self, orm):
-        raise RuntimeError("Cannot reverse this migration.")
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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):
+        # Deleting field 'Checkpoint.post'
+        db.delete_column(u'misago_checkpoint', 'post_id')
+
+        # Deleting field 'Post.checkpoints'
+        db.delete_column(u'misago_post', 'checkpoints')
+
+
+    def backwards(self, orm):
+        raise RuntimeError("Cannot reverse this migration.")
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'merge': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'merges': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 393 - 393
misago/migrations/0011_auto__del_field_thread_merges__del_field_post_merge.py

@@ -1,394 +1,394 @@
-# -*- coding: 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):
-        # Deleting field 'Thread.merges'
-        db.delete_column(u'misago_thread', 'merges')
-
-        # Deleting field 'Post.merge'
-        db.delete_column(u'misago_post', 'merge')
-
-
-    def backwards(self, orm):
-        raise RuntimeError("Cannot reverse this migration.")
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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):
+        # Deleting field 'Thread.merges'
+        db.delete_column(u'misago_thread', 'merges')
+
+        # Deleting field 'Post.merge'
+        db.delete_column(u'misago_post', 'merge')
+
+
+    def backwards(self, orm):
+        raise RuntimeError("Cannot reverse this migration.")
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 395 - 395
misago/migrations/0012_auto__add_field_post_current_date.py

@@ -1,396 +1,396 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-from django.utils import timezone
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding field 'Post.current_date'
-        db.add_column(u'misago_post', 'current_date',
-                      self.gf('django.db.models.fields.DateTimeField')(default=timezone.now(), db_index=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Post.current_date'
-        db.delete_column(u'misago_post', 'current_date')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+from django.utils import timezone
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Post.current_date'
+        db.add_column(u'misago_post', 'current_date',
+                      self.gf('django.db.models.fields.DateTimeField')(default=timezone.now(), db_index=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Post.current_date'
+        db.delete_column(u'misago_post', 'current_date')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 391 - 391
misago/migrations/0013_set_posts_current_date.py

@@ -1,391 +1,391 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import DataMigration
-from django.db import models
-
-class Migration(DataMigration):
-
-    def forwards(self, orm):
-        for post in orm.Post.objects.all(): 
-            post.current_date = post.edit_date or post.date 
-            post.save()
-
-    def backwards(self, orm):
-        raise RuntimeError("Cannot reverse this migration.")
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
-    complete_apps = ['misago']
-    symmetrical = True
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        for post in orm.Post.objects.all(): 
+            post.current_date = post.edit_date or post.date 
+            post.save()
+
+    def backwards(self, orm):
+        raise RuntimeError("Cannot reverse this migration.")
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
+    complete_apps = ['misago']
+    symmetrical = True

+ 390 - 390
misago/migrations/0014_auto__del_field_post_edit_date.py

@@ -1,391 +1,391 @@
-# -*- coding: 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):
-        # Deleting field 'Post.edit_date'
-        db.delete_column(u'misago_post', 'edit_date')
-
-
-    def backwards(self, orm):
-        raise RuntimeError("Cannot reverse this migration.")
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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):
+        # Deleting field 'Post.edit_date'
+        db.delete_column(u'misago_post', 'edit_date')
+
+
+    def backwards(self, orm):
+        raise RuntimeError("Cannot reverse this migration.")
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 388 - 388
misago/migrations/0015_remove_users_reported.py

@@ -1,388 +1,388 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import DataMigration
-from django.db import models
-
-class Migration(DataMigration):
-
-    def forwards(self, orm):
-        orm.MonitorItem.objects.filter(pk='users_reported').delete()
-
-    def backwards(self, orm):
-        raise RuntimeError("Cannot reverse this migration.")
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
-    complete_apps = ['misago']
-    symmetrical = True
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        orm.MonitorItem.objects.filter(pk='users_reported').delete()
+
+    def backwards(self, orm):
+        raise RuntimeError("Cannot reverse this migration.")
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
+    complete_apps = ['misago']
+    symmetrical = True

+ 394 - 394
misago/migrations/0016_auto__add_field_post_delete_date.py

@@ -1,395 +1,395 @@
-# -*- coding: 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 field 'Post.delete_date'
-        db.add_column(u'misago_post', 'delete_date',
-                      self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Post.delete_date'
-        db.delete_column(u'misago_post', 'delete_date')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'delete_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 field 'Post.delete_date'
+        db.add_column(u'misago_post', 'delete_date',
+                      self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Post.delete_date'
+        db.delete_column(u'misago_post', 'delete_date')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'delete_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 392 - 392
misago/migrations/0017_populate_post_delete_date.py

@@ -1,392 +1,392 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import DataMigration
-from django.db import models
-
-class Migration(DataMigration):
-
-    def forwards(self, orm):
-        for post in orm.Post.objects.filter(deleted=True):
-            post.delete_date = post.current_date
-            post.save()
-
-    def backwards(self, orm):
-        raise RuntimeError("Cannot reverse this migration.")
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'delete_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
-    complete_apps = ['misago']
-    symmetrical = True
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        for post in orm.Post.objects.filter(deleted=True):
+            post.delete_date = post.current_date
+            post.save()
+
+    def backwards(self, orm):
+        raise RuntimeError("Cannot reverse this migration.")
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'delete_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
+    complete_apps = ['misago']
+    symmetrical = True

+ 394 - 394
misago/migrations/0018_auto__add_field_watchedthread_starter__del_field_setting_value__add_fi.py

@@ -1,395 +1,395 @@
-# -*- coding: 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 field 'WatchedThread.starter'
-        db.add_column(u'misago_watchedthread', 'starter',
-                      self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, to=orm['misago.User']),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        raise RuntimeError("Cannot reverse this migration.")
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'delete_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'starter': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.User']"}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 field 'WatchedThread.starter'
+        db.add_column(u'misago_watchedthread', 'starter',
+                      self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, to=orm['misago.User']),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        raise RuntimeError("Cannot reverse this migration.")
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'delete_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'starter': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.User']"}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 396 - 396
misago/migrations/0019_auto__add_field_post_reports.py

@@ -1,397 +1,397 @@
-# -*- coding: 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 field 'Post.reports'
-        db.add_column(u'misago_post', 'reports',
-                      self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Post.reports'
-        db.delete_column(u'misago_post', 'reports')
-
-
-    models = {
-        'misago.alert': {
-            'Meta': {'object_name': 'Alert'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'message': ('django.db.models.fields.TextField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
-            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.ban': {
-            'Meta': {'object_name': 'Ban'},
-            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.change': {
-            'Meta': {'object_name': 'Change'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'post_content': ('django.db.models.fields.TextField', [], {}),
-            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.checkpoint': {
-            'Meta': {'object_name': 'Checkpoint'},
-            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.fixture': {
-            'Meta': {'object_name': 'Fixture'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.forum': {
-            'Meta': {'object_name': 'Forum'},
-            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
-            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
-            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
-        },
-        'misago.forumread': {
-            'Meta': {'object_name': 'ForumRead'},
-            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.forumrole': {
-            'Meta': {'object_name': 'ForumRole'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.karma': {
-            'Meta': {'object_name': 'Karma'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.monitoritem': {
-            'Meta': {'object_name': 'MonitorItem'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.newsletter': {
-            'Meta': {'object_name': 'Newsletter'},
-            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
-            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
-        },
-        'misago.post': {
-            'Meta': {'object_name': 'Post'},
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            'delete_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'post': ('django.db.models.fields.TextField', [], {}),
-            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
-            'reports': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.pruningpolicy': {
-            'Meta': {'object_name': 'PruningPolicy'},
-            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.rank': {
-            'Meta': {'object_name': 'Rank'},
-            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
-        },
-        'misago.role': {
-            'Meta': {'object_name': 'Role'},
-            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
-            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
-        },
-        'misago.session': {
-            'Meta': {'object_name': 'Session'},
-            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
-        },
-        'misago.setting': {
-            'Meta': {'object_name': 'Setting'},
-            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
-            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        'misago.settingsgroup': {
-            'Meta': {'object_name': 'SettingsGroup'},
-            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'misago.signinattempt': {
-            'Meta': {'object_name': 'SignInAttempt'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
-        },
-        'misago.thread': {
-            'Meta': {'object_name': 'Thread'},
-            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last': ('django.db.models.fields.DateTimeField', [], {}),
-            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
-            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start': ('django.db.models.fields.DateTimeField', [], {}),
-            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
-            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
-            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.threadread': {
-            'Meta': {'object_name': 'ThreadRead'},
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'updated': ('django.db.models.fields.DateTimeField', [], {}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        },
-        'misago.token': {
-            'Meta': {'object_name': 'Token'},
-            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
-            'created': ('django.db.models.fields.DateTimeField', [], {}),
-            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
-        },
-        'misago.user': {
-            'Meta': {'object_name': 'User'},
-            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
-            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
-            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
-            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
-            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
-            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
-            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
-            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
-            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
-            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
-            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
-            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
-        },
-        'misago.usernamechange': {
-            'Meta': {'object_name': 'UsernameChange'},
-            'date': ('django.db.models.fields.DateTimeField', [], {}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
-        },
-        'misago.watchedthread': {
-            'Meta': {'object_name': 'WatchedThread'},
-            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
-            'starter': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.User']"}),
-            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
-        }
-    }
-
+# -*- coding: 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 field 'Post.reports'
+        db.add_column(u'misago_post', 'reports',
+                      self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Post.reports'
+        db.delete_column(u'misago_post', 'reports')
+
+
+    models = {
+        'misago.alert': {
+            'Meta': {'object_name': 'Alert'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"}),
+            'variables': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.ban': {
+            'Meta': {'object_name': 'Ban'},
+            'ban': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'test': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.change': {
+            'Meta': {'object_name': 'Change'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'change': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'post_content': ('django.db.models.fields.TextField', [], {}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'thread_name_new': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread_name_old': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.checkpoint': {
+            'Meta': {'object_name': 'Checkpoint'},
+            'action': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'old_forum': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'old_forum_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'old_forum_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'target_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'target_user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.fixture': {
+            'Meta': {'object_name': 'Fixture'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.forum': {
+            'Meta': {'object_name': 'Forum'},
+            'attrs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Thread']"}),
+            'last_thread_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_thread_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_thread_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['misago.Forum']"}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'posts_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'prune_last': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'prune_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'pruned_archive': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Forum']"}),
+            'redirect': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'redirects': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'redirects_delta': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'show_details': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'threads_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
+        },
+        'misago.forumread': {
+            'Meta': {'object_name': 'ForumRead'},
+            'cleared': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.forumrole': {
+            'Meta': {'object_name': 'ForumRole'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.karma': {
+            'Meta': {'object_name': 'Karma'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user_slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.monitoritem': {
+            'Meta': {'object_name': 'MonitorItem'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'int'", 'max_length': '255'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.newsletter': {
+            'Meta': {'object_name': 'Newsletter'},
+            'content_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'content_plain': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignore_subscriptions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'progress': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'ranks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Rank']", 'symmetrical': 'False'}),
+            'step_size': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+        },
+        'misago.post': {
+            'Meta': {'object_name': 'Post'},
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'current_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            'delete_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'edit_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'edit_user_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edit_user_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'edits': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'mentions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mention_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'post': ('django.db.models.fields.TextField', [], {}),
+            'post_preparsed': ('django.db.models.fields.TextField', [], {}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'reported': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'reports': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'user_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.pruningpolicy': {
+            'Meta': {'object_name': 'PruningPolicy'},
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_visit': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'registered': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.rank': {
+            'Meta': {'object_name': 'Rank'},
+            'as_tab': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'criteria': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'on_index': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'special': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'misago.role': {
+            'Meta': {'object_name': 'Role'},
+            '_permissions': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'permissions'", 'blank': 'True'}),
+            '_special': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_column': "'special'", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'misago.session': {
+            'Meta': {'object_name': 'Session'},
+            'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'agent': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'crawler': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'data': ('django.db.models.fields.TextField', [], {'db_column': "'session_data'"}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'matched': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Rank']"}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"})
+        },
+        'misago.setting': {
+            'Meta': {'object_name': 'Setting'},
+            '_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'value'", 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'extra': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.SettingsGroup']", 'to_field': "'key'"}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'normalize_to': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'separator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'value_default': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'misago.settingsgroup': {
+            'Meta': {'object_name': 'SettingsGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'misago.signinattempt': {
+            'Meta': {'object_name': 'SignInAttempt'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
+        },
+        'misago.thread': {
+            'Meta': {'object_name': 'Thread'},
+            'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'downvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last': ('django.db.models.fields.DateTimeField', [], {}),
+            'last_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'last_poster': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.User']"}),
+            'last_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'last_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'participants': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'private_thread_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'replies': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_deleted': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_moderated': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'replies_reported': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'report_for': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'report_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'score': ('django.db.models.fields.PositiveIntegerField', [], {'default': '30'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start': ('django.db.models.fields.DateTimeField', [], {}),
+            'start_post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['misago.Post']"}),
+            'start_poster': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'start_poster_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'start_poster_slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+            'start_poster_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'upvotes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'weight': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.threadread': {
+            'Meta': {'object_name': 'ThreadRead'},
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        },
+        'misago.token': {
+            'Meta': {'object_name': 'Token'},
+            'accessed': ('django.db.models.fields.DateTimeField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '42', 'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'signin_tokens'", 'to': "orm['misago.User']"})
+        },
+        'misago.user': {
+            'Meta': {'object_name': 'User'},
+            'acl_key': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'activation': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'alerts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'alerts_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'allow_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'avatar_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'avatar_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'avatar_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_original': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_temp': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'avatar_type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'email_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'following': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'follows': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'follows_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'hide_activity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ignores': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ignores_set'", 'symmetrical': 'False', 'to': "orm['misago.User']"}),
+            'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'join_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'join_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'join_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+            'karma_given_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_given_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_n': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'karma_p': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'last_agent': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+            'last_post': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_search': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'password_date': ('django.db.models.fields.DateTimeField', [], {}),
+            'posts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'rank': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Rank']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'ranking': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'receive_newsletters': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'roles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['misago.Role']", 'symmetrical': 'False'}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'signature_ban_reason_admin': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_ban_reason_user': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'signature_preparsed': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subscribe_reply': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscribe_start': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'sync_pds': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'threads': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'timezone': ('django.db.models.fields.CharField', [], {'default': "'utc'", 'max_length': '255'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'unread_pds': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'username_slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}),
+            'votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'misago.usernamechange': {
+            'Meta': {'object_name': 'UsernameChange'},
+            'date': ('django.db.models.fields.DateTimeField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'old_username': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'namechanges'", 'to': "orm['misago.User']"})
+        },
+        'misago.watchedthread': {
+            'Meta': {'object_name': 'WatchedThread'},
+            'email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Forum']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_read': ('django.db.models.fields.DateTimeField', [], {}),
+            'starter': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['misago.User']"}),
+            'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.Thread']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['misago.User']"})
+        }
+    }
+
     complete_apps = ['misago']
     complete_apps = ['misago']

+ 24 - 24
misago/models/__init__.py

@@ -1,25 +1,25 @@
-from misago.models.alertmodel import Alert
-from misago.models.banmodel import Ban, BanCache
-from misago.models.changemodel import Change
-from misago.models.checkpointmodel import Checkpoint
-from misago.models.fixturemodel import Fixture
-from misago.models.forummodel import Forum
-from misago.models.forumreadmodel import ForumRead
-from misago.models.forumrolemodel import ForumRole
-from misago.models.karmamodel import Karma
-from misago.models.monitoritemmodel import MonitorItem
-from misago.models.newslettermodel import Newsletter
-from misago.models.postmodel import Post
-from misago.models.pruningpolicymodel import PruningPolicy
-from misago.models.rankmodel import Rank
-from misago.models.rolemodel import Role
-from misago.models.sessionmodel import Session
-from misago.models.settingmodel import Setting
-from misago.models.settingsgroupmodel import SettingsGroup
-from misago.models.signinattemptmodel import SignInAttempt
-from misago.models.threadmodel import Thread
-from misago.models.threadreadmodel import ThreadRead
-from misago.models.tokenmodel import Token
-from misago.models.usermodel import User, Guest, Crawler
-from misago.models.usernamechangemodel import UsernameChange
+from misago.models.alertmodel import Alert
+from misago.models.banmodel import Ban, BanCache
+from misago.models.changemodel import Change
+from misago.models.checkpointmodel import Checkpoint
+from misago.models.fixturemodel import Fixture
+from misago.models.forummodel import Forum
+from misago.models.forumreadmodel import ForumRead
+from misago.models.forumrolemodel import ForumRole
+from misago.models.karmamodel import Karma
+from misago.models.monitoritemmodel import MonitorItem
+from misago.models.newslettermodel import Newsletter
+from misago.models.postmodel import Post
+from misago.models.pruningpolicymodel import PruningPolicy
+from misago.models.rankmodel import Rank
+from misago.models.rolemodel import Role
+from misago.models.sessionmodel import Session
+from misago.models.settingmodel import Setting
+from misago.models.settingsgroupmodel import SettingsGroup
+from misago.models.signinattemptmodel import SignInAttempt
+from misago.models.threadmodel import Thread
+from misago.models.threadreadmodel import ThreadRead
+from misago.models.tokenmodel import Token
+from misago.models.usermodel import User, Guest, Crawler
+from misago.models.usernamechangemodel import UsernameChange
 from misago.models.watchedthreadmodel import WatchedThread
 from misago.models.watchedthreadmodel import WatchedThread

+ 77 - 77
misago/models/alertmodel.py

@@ -1,77 +1,77 @@
-from django.db import models
-import base64
-import cgi
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-
-class Alert(models.Model):
-    user = models.ForeignKey('User')
-    date = models.DateTimeField()
-    message = models.TextField()
-    variables = models.TextField(null=True, blank=True)
-
-    class Meta:
-        app_label = 'misago'
-
-    def vars(self):
-        try:
-            return pickle.loads(base64.decodestring(self.variables))
-        except Exception:
-            return {}
-
-    def text(self, var, value):
-        value = cgi.escape(value, True)
-        try:
-            self.vars_raw[var] = value
-        except AttributeError:
-            self.vars_raw = {var: value}
-        return self
-    
-    def strong(self, var, value):
-        try:
-            self.vars_raw[var] = '<strong>%s</strong>' % cgi.escape(value, True)
-        except AttributeError:
-            self.vars_raw = {var: '<strong>%s</strong>' % cgi.escape(value, True)}
-        return self
-
-    def url(self, var, value, href, attrs=None):
-        url = '<a href="%s"' % cgi.escape(href, True)
-        if attrs:
-            for k, v in attrs.iterator():
-                url += ' %s="%s"' % (k, cgi.escape(v, True))
-        url += '>%s</a>' % value
-        try:
-            self.vars_raw[var] = url
-        except AttributeError:
-            self.vars_raw = {var: url}
-        return self
-
-    def profile(self, var, user):
-        from django.core.urlresolvers import reverse
-        return self.url(var, user.username, reverse('user', kwargs={'user': user.pk, 'username': user.username_slug}))
-
-    def thread(self, var, thread_type, thread):
-        from django.core.urlresolvers import reverse
-        return self.url(var, thread.name, reverse(thread_type, kwargs={'thread': thread.pk, 'slug': thread.slug}))
-
-    def post(self, var, thread_type, thread, post):
-        from django.core.urlresolvers import reverse
-        return self.url(var, thread.name, reverse('%s_find' % thread_type, kwargs={'thread': thread.pk, 'slug': thread.slug, 'post': post.pk}))
-
-    def save_all(self, *args, **kwargs):
-        self.save(force_insert=True)
-        self.user.save(force_update=True)
-
-    def hydrate(self):
-        try:
-            self.variables = base64.encodestring(pickle.dumps(self.vars_raw, pickle.HIGHEST_PROTOCOL))
-        except AttributeError:
-            self.variables = base64.encodestring(pickle.dumps({}, pickle.HIGHEST_PROTOCOL))
-        return self
-
-    def save(self, *args, **kwargs):
-        self.hydrate()
-        super(Alert, self).save(*args, **kwargs)
-        return self.user
+from django.db import models
+import base64
+import cgi
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+class Alert(models.Model):
+    user = models.ForeignKey('User')
+    date = models.DateTimeField()
+    message = models.TextField()
+    variables = models.TextField(null=True, blank=True)
+
+    class Meta:
+        app_label = 'misago'
+
+    def vars(self):
+        try:
+            return pickle.loads(base64.decodestring(self.variables))
+        except Exception:
+            return {}
+
+    def text(self, var, value):
+        value = cgi.escape(value, True)
+        try:
+            self.vars_raw[var] = value
+        except AttributeError:
+            self.vars_raw = {var: value}
+        return self
+    
+    def strong(self, var, value):
+        try:
+            self.vars_raw[var] = '<strong>%s</strong>' % cgi.escape(value, True)
+        except AttributeError:
+            self.vars_raw = {var: '<strong>%s</strong>' % cgi.escape(value, True)}
+        return self
+
+    def url(self, var, value, href, attrs=None):
+        url = '<a href="%s"' % cgi.escape(href, True)
+        if attrs:
+            for k, v in attrs.iterator():
+                url += ' %s="%s"' % (k, cgi.escape(v, True))
+        url += '>%s</a>' % value
+        try:
+            self.vars_raw[var] = url
+        except AttributeError:
+            self.vars_raw = {var: url}
+        return self
+
+    def profile(self, var, user):
+        from django.core.urlresolvers import reverse
+        return self.url(var, user.username, reverse('user', kwargs={'user': user.pk, 'username': user.username_slug}))
+
+    def thread(self, var, thread_type, thread):
+        from django.core.urlresolvers import reverse
+        return self.url(var, thread.name, reverse(thread_type, kwargs={'thread': thread.pk, 'slug': thread.slug}))
+
+    def post(self, var, thread_type, thread, post):
+        from django.core.urlresolvers import reverse
+        return self.url(var, thread.name, reverse('%s_find' % thread_type, kwargs={'thread': thread.pk, 'slug': thread.slug, 'post': post.pk}))
+
+    def save_all(self, *args, **kwargs):
+        self.save(force_insert=True)
+        self.user.save(force_update=True)
+
+    def hydrate(self):
+        try:
+            self.variables = base64.encodestring(pickle.dumps(self.vars_raw, pickle.HIGHEST_PROTOCOL))
+        except AttributeError:
+            self.variables = base64.encodestring(pickle.dumps({}, pickle.HIGHEST_PROTOCOL))
+        return self
+
+    def save(self, *args, **kwargs):
+        self.hydrate()
+        super(Alert, self).save(*args, **kwargs)
+        return self.user

+ 91 - 91
misago/models/banmodel.py

@@ -1,91 +1,91 @@
-import re
-from django.db import models
-from django.db.models import Q
-from django.utils import timezone
-from misago.monitor import monitor
-
-BAN_NAME_EMAIL = 0
-BAN_NAME = 1
-BAN_EMAIL = 2
-BAN_IP = 3
-
-
-class BansManager(models.Manager):
-    def check_ban(self, ip=False, username=False, email=False):
-        bans_model = Ban.objects.filter(Q(expires=None) | Q(expires__gt=timezone.now()))
-        if not (ip and username and email):
-            if ip:
-                bans_model.filter(test=BAN_IP)
-            if username:
-                bans_model.filter(test=BAN_NAME_EMAIL)
-                bans_model.filter(test=BAN_NAME)
-            if email:
-                bans_model.filter(test=BAN_NAME_EMAIL)
-                bans_model.filter(test=BAN_EMAIL)
-        for ban in bans_model.order_by('-expires').iterator():
-            if (
-                # Check user name
-                ((username and (ban.test == BAN_NAME_EMAIL or ban.test == BAN_NAME))
-                and re.search('^' + re.escape(ban.ban).replace('\*', '(.*?)') + '$', username, flags=re.IGNORECASE))
-                or # Check user email
-                ((email and (ban.test == BAN_NAME_EMAIL or ban.test == BAN_EMAIL))
-                and re.search('^' + re.escape(ban.ban).replace('\*', '(.*?)') + '$', email, flags=re.IGNORECASE))
-                or # Check IP address
-                (ip and ban.test == BAN_IP
-                and re.search('^' + re.escape(ban.ban).replace('\*', '(.*?)') + '$', ip, flags=re.IGNORECASE))):
-                    return ban
-        return False
-
-
-class Ban(models.Model):
-    test = models.PositiveIntegerField(default=BAN_NAME_EMAIL)
-    ban = models.CharField(max_length=255)
-    reason_user = models.TextField(null=True, blank=True)
-    reason_admin = models.TextField(null=True, blank=True)
-    expires = models.DateTimeField(null=True, blank=True)
-
-    objects = BansManager()
-
-    class Meta:
-        app_label = 'misago'
-
-
-class BanCache(object):
-    def __init__(self):
-        self.banned = False
-        self.test = None
-        self.expires = None
-        self.reason_user = None
-        self.version = 0
-
-    def check_for_updates(self, request):
-        if (self.version < monitor['bans_version']
-                or (self.expires != None and self.expires < timezone.now())):
-            self.version = monitor['bans_version']
-
-            # Check Ban
-            if request.user.is_authenticated():
-                ban = Ban.objects.check_ban(
-                                ip=request.session.get_ip(request),
-                                username=request.user.username,
-                                email=request.user.email
-                                )
-            else:
-                ban = Ban.objects.check_ban(ip=request.session.get_ip(request))
-
-            # Update ban cache
-            if ban:
-                self.banned = True
-                self.reason_user = ban.reason_user
-                self.expires = ban.expires
-                self.test = ban.test
-            else:
-                self.banned = False
-                self.reason_user = None
-                self.expires = None
-                self.test = None
-            return True
-        return False
-
-    def is_banned(self):
-        return self.banned
+import re
+from django.db import models
+from django.db.models import Q
+from django.utils import timezone
+from misago.monitor import monitor
+
+BAN_NAME_EMAIL = 0
+BAN_NAME = 1
+BAN_EMAIL = 2
+BAN_IP = 3
+
+
+class BansManager(models.Manager):
+    def check_ban(self, ip=False, username=False, email=False):
+        bans_model = Ban.objects.filter(Q(expires=None) | Q(expires__gt=timezone.now()))
+        if not (ip and username and email):
+            if ip:
+                bans_model.filter(test=BAN_IP)
+            if username:
+                bans_model.filter(test=BAN_NAME_EMAIL)
+                bans_model.filter(test=BAN_NAME)
+            if email:
+                bans_model.filter(test=BAN_NAME_EMAIL)
+                bans_model.filter(test=BAN_EMAIL)
+        for ban in bans_model.order_by('-expires').iterator():
+            if (
+                # Check user name
+                ((username and (ban.test == BAN_NAME_EMAIL or ban.test == BAN_NAME))
+                and re.search('^' + re.escape(ban.ban).replace('\*', '(.*?)') + '$', username, flags=re.IGNORECASE))
+                or # Check user email
+                ((email and (ban.test == BAN_NAME_EMAIL or ban.test == BAN_EMAIL))
+                and re.search('^' + re.escape(ban.ban).replace('\*', '(.*?)') + '$', email, flags=re.IGNORECASE))
+                or # Check IP address
+                (ip and ban.test == BAN_IP
+                and re.search('^' + re.escape(ban.ban).replace('\*', '(.*?)') + '$', ip, flags=re.IGNORECASE))):
+                    return ban
+        return False
+
+
+class Ban(models.Model):
+    test = models.PositiveIntegerField(default=BAN_NAME_EMAIL)
+    ban = models.CharField(max_length=255)
+    reason_user = models.TextField(null=True, blank=True)
+    reason_admin = models.TextField(null=True, blank=True)
+    expires = models.DateTimeField(null=True, blank=True)
+
+    objects = BansManager()
+
+    class Meta:
+        app_label = 'misago'
+
+
+class BanCache(object):
+    def __init__(self):
+        self.banned = False
+        self.test = None
+        self.expires = None
+        self.reason_user = None
+        self.version = 0
+
+    def check_for_updates(self, request):
+        if (self.version < monitor['bans_version']
+                or (self.expires != None and self.expires < timezone.now())):
+            self.version = monitor['bans_version']
+
+            # Check Ban
+            if request.user.is_authenticated():
+                ban = Ban.objects.check_ban(
+                                ip=request.session.get_ip(request),
+                                username=request.user.username,
+                                email=request.user.email
+                                )
+            else:
+                ban = Ban.objects.check_ban(ip=request.session.get_ip(request))
+
+            # Update ban cache
+            if ban:
+                self.banned = True
+                self.reason_user = ban.reason_user
+                self.expires = ban.expires
+                self.test = ban.test
+            else:
+                self.banned = False
+                self.reason_user = None
+                self.expires = None
+                self.test = None
+            return True
+        return False
+
+    def is_banned(self):
+        return self.banned

+ 61 - 61
misago/models/changemodel.py

@@ -1,61 +1,61 @@
-from django.db import models
-from misago.signals import (merge_post, merge_thread, move_forum_content,
-                            move_post, move_thread, rename_user)
-
-class Change(models.Model):
-    forum = models.ForeignKey('Forum')
-    thread = models.ForeignKey('Thread')
-    post = models.ForeignKey('Post')
-    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
-    user_name = models.CharField(max_length=255)
-    user_slug = models.CharField(max_length=255)
-    date = models.DateTimeField()
-    ip = models.GenericIPAddressField()
-    agent = models.CharField(max_length=255)
-    reason = models.CharField(max_length=255, null=True, blank=True)
-    thread_name_new = models.CharField(max_length=255, null=True, blank=True)
-    thread_name_old = models.CharField(max_length=255, null=True, blank=True)
-    post_content = models.TextField()
-    size = models.IntegerField(default=0)
-    change = models.IntegerField(default=0)
-
-    class Meta:
-        app_label = 'misago'
-
-
-def rename_user_handler(sender, **kwargs):
-    Change.objects.filter(user=sender).update(
-                                              user_name=sender.username,
-                                              user_slug=sender.username_slug,
-                                              )
-rename_user.connect(rename_user_handler, dispatch_uid="rename_user_changes")
-
-
-def move_forum_content_handler(sender, **kwargs):
-    Change.objects.filter(forum=sender).update(forum=kwargs['move_to'])
-
-move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_changes")
-
-
-def move_thread_handler(sender, **kwargs):
-    Change.objects.filter(thread=sender).update(forum=kwargs['move_to'])
-
-move_thread.connect(move_thread_handler, dispatch_uid="move_thread_changes")
-
-
-def merge_thread_handler(sender, **kwargs):
-    Change.objects.filter(thread=sender).update(thread=kwargs['new_thread'])
-
-merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_changes")
-
-
-def move_posts_handler(sender, **kwargs):
-    Change.objects.filter(post=sender).update(forum=kwargs['move_to'].forum, thread=kwargs['move_to'])
-
-move_post.connect(move_posts_handler, dispatch_uid="move_posts_changes")
-
-
-def merge_posts_handler(sender, **kwargs):
-    Change.objects.filter(post=sender).update(post=kwargs['new_post'])
-
-merge_post.connect(merge_posts_handler, dispatch_uid="merge_posts_changes")
+from django.db import models
+from misago.signals import (merge_post, merge_thread, move_forum_content,
+                            move_post, move_thread, rename_user)
+
+class Change(models.Model):
+    forum = models.ForeignKey('Forum')
+    thread = models.ForeignKey('Thread')
+    post = models.ForeignKey('Post')
+    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
+    user_name = models.CharField(max_length=255)
+    user_slug = models.CharField(max_length=255)
+    date = models.DateTimeField()
+    ip = models.GenericIPAddressField()
+    agent = models.CharField(max_length=255)
+    reason = models.CharField(max_length=255, null=True, blank=True)
+    thread_name_new = models.CharField(max_length=255, null=True, blank=True)
+    thread_name_old = models.CharField(max_length=255, null=True, blank=True)
+    post_content = models.TextField()
+    size = models.IntegerField(default=0)
+    change = models.IntegerField(default=0)
+
+    class Meta:
+        app_label = 'misago'
+
+
+def rename_user_handler(sender, **kwargs):
+    Change.objects.filter(user=sender).update(
+                                              user_name=sender.username,
+                                              user_slug=sender.username_slug,
+                                              )
+rename_user.connect(rename_user_handler, dispatch_uid="rename_user_changes")
+
+
+def move_forum_content_handler(sender, **kwargs):
+    Change.objects.filter(forum=sender).update(forum=kwargs['move_to'])
+
+move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_changes")
+
+
+def move_thread_handler(sender, **kwargs):
+    Change.objects.filter(thread=sender).update(forum=kwargs['move_to'])
+
+move_thread.connect(move_thread_handler, dispatch_uid="move_thread_changes")
+
+
+def merge_thread_handler(sender, **kwargs):
+    Change.objects.filter(thread=sender).update(thread=kwargs['new_thread'])
+
+merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_changes")
+
+
+def move_posts_handler(sender, **kwargs):
+    Change.objects.filter(post=sender).update(forum=kwargs['move_to'].forum, thread=kwargs['move_to'])
+
+move_post.connect(move_posts_handler, dispatch_uid="move_posts_changes")
+
+
+def merge_posts_handler(sender, **kwargs):
+    Change.objects.filter(post=sender).update(post=kwargs['new_post'])
+
+merge_post.connect(merge_posts_handler, dispatch_uid="merge_posts_changes")

+ 60 - 60
misago/models/checkpointmodel.py

@@ -1,60 +1,60 @@
-from django.db import models
-from misago.signals import (merge_post, merge_thread, move_forum_content,
-                            move_post, move_thread, rename_forum, rename_user)
-
-class Checkpoint(models.Model):
-    forum = models.ForeignKey('Forum')
-    thread = models.ForeignKey('Thread')
-    action = models.CharField(max_length=255)
-    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
-    user_name = models.CharField(max_length=255)
-    user_slug = models.CharField(max_length=255)
-    target_user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL, related_name='+')
-    target_user_name = models.CharField(max_length=255, null=True, blank=True)
-    target_user_slug = models.CharField(max_length=255, null=True, blank=True)
-    old_forum = models.ForeignKey('Forum', null=True, blank=True, related_name='+')
-    old_forum_name = models.CharField(max_length=255, null=True, blank=True)
-    old_forum_slug = models.CharField(max_length=255, null=True, blank=True)
-    date = models.DateTimeField()
-    ip = models.GenericIPAddressField()
-    agent = models.CharField(max_length=255)
-    deleted = models.BooleanField(default=False)
-
-    class Meta:
-        app_label = 'misago'
-
-
-def rename_forum_handler(sender, **kwargs):
-    Checkpoint.objects.filter(old_forum=sender).update(
-                                                  old_forum_name=sender.name,
-                                                  old_forum_slug=sender.slug,
-                                                  )
-
-rename_forum.connect(rename_forum_handler, dispatch_uid="rename_forum_checkpoints")
-
-
-def rename_user_handler(sender, **kwargs):
-    Checkpoint.objects.filter(user=sender).update(
-                                                  user_name=sender.username,
-                                                  user_slug=sender.username_slug,
-                                                  )
-
-rename_user.connect(rename_user_handler, dispatch_uid="rename_user_checkpoints")
-
-
-def move_forum_content_handler(sender, **kwargs):
-    Checkpoint.objects.filter(forum=sender).update(forum=kwargs['move_to'])
-
-move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_checkpoints")
-
-
-def move_thread_handler(sender, **kwargs):
-    Checkpoint.objects.filter(thread=sender).update(forum=kwargs['move_to'])
-
-move_thread.connect(move_thread_handler, dispatch_uid="move_thread_checkpoints")
-
-
-def merge_thread_handler(sender, **kwargs):
-    Checkpoint.objects.filter(thread=sender).delete()
-
-merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_checkpoints")
+from django.db import models
+from misago.signals import (merge_post, merge_thread, move_forum_content,
+                            move_post, move_thread, rename_forum, rename_user)
+
+class Checkpoint(models.Model):
+    forum = models.ForeignKey('Forum')
+    thread = models.ForeignKey('Thread')
+    action = models.CharField(max_length=255)
+    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
+    user_name = models.CharField(max_length=255)
+    user_slug = models.CharField(max_length=255)
+    target_user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL, related_name='+')
+    target_user_name = models.CharField(max_length=255, null=True, blank=True)
+    target_user_slug = models.CharField(max_length=255, null=True, blank=True)
+    old_forum = models.ForeignKey('Forum', null=True, blank=True, related_name='+')
+    old_forum_name = models.CharField(max_length=255, null=True, blank=True)
+    old_forum_slug = models.CharField(max_length=255, null=True, blank=True)
+    date = models.DateTimeField()
+    ip = models.GenericIPAddressField()
+    agent = models.CharField(max_length=255)
+    deleted = models.BooleanField(default=False)
+
+    class Meta:
+        app_label = 'misago'
+
+
+def rename_forum_handler(sender, **kwargs):
+    Checkpoint.objects.filter(old_forum=sender).update(
+                                                  old_forum_name=sender.name,
+                                                  old_forum_slug=sender.slug,
+                                                  )
+
+rename_forum.connect(rename_forum_handler, dispatch_uid="rename_forum_checkpoints")
+
+
+def rename_user_handler(sender, **kwargs):
+    Checkpoint.objects.filter(user=sender).update(
+                                                  user_name=sender.username,
+                                                  user_slug=sender.username_slug,
+                                                  )
+
+rename_user.connect(rename_user_handler, dispatch_uid="rename_user_checkpoints")
+
+
+def move_forum_content_handler(sender, **kwargs):
+    Checkpoint.objects.filter(forum=sender).update(forum=kwargs['move_to'])
+
+move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_checkpoints")
+
+
+def move_thread_handler(sender, **kwargs):
+    Checkpoint.objects.filter(thread=sender).update(forum=kwargs['move_to'])
+
+move_thread.connect(move_thread_handler, dispatch_uid="move_thread_checkpoints")
+
+
+def merge_thread_handler(sender, **kwargs):
+    Checkpoint.objects.filter(thread=sender).delete()
+
+merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_checkpoints")

+ 6 - 6
misago/models/fixturemodel.py

@@ -1,7 +1,7 @@
-from django.db import models
-
-class Fixture(models.Model):
-    name = models.CharField(max_length=255)
-
-    class Meta:
+from django.db import models
+
+class Fixture(models.Model):
+    name = models.CharField(max_length=255)
+
+    class Meta:
         app_label = 'misago'
         app_label = 'misago'

+ 342 - 342
misago/models/forummodel.py

@@ -1,343 +1,343 @@
-import urlparse
-import threading
-from mptt.managers import TreeManager
-from mptt.models import MPTTModel, TreeForeignKey
-from django.conf import settings
-from django.core.cache import cache
-from django.core.urlresolvers import reverse
-from django.db import models
-from django.db.models import Sum
-from django.utils.translation import ugettext_lazy as _
-from misago.signals import delete_forum_content, move_forum_content, rename_forum, rename_user
-
-_thread_local = threading.local()
-
-class ForumManager(TreeManager):
-    @property
-    def forums_tree(self):
-        try:
-            return _thread_local.misago_forums_tree
-        except AttributeError:
-            _thread_local.misago_forums_tree = None
-        return _thread_local.misago_forums_tree
-
-    @forums_tree.setter
-    def forums_tree(self, value):
-        _thread_local.misago_forums_tree = value
-
-    def special_pk(self, name):
-        self.populate_tree()
-        return self.forums_tree.get(name).pk
-
-    def special_model(self, name):
-        self.populate_tree()
-        return self.forums_tree.get(name)
-
-    def populate_tree(self, force=False):
-        if not self.forums_tree:
-            self.forums_tree = cache.get('forums_tree')
-        if not self.forums_tree or force:
-            self.forums_tree = {}
-            for forum in Forum.objects.order_by('lft'):
-                self.forums_tree[forum.pk] = forum
-                if forum.special:
-                    self.forums_tree[forum.special] = forum
-            cache.set('forums_tree', self.forums_tree)
-
-    def forum_parents(self, forum, include_self=False):
-        self.populate_tree()
-        parents = []
-        parent = self.forums_tree[forum]
-        if include_self:
-            parents.append(parent)
-        while parent.level > 1:
-            parent = self.forums_tree[parent.parent_id]
-            parents.append(parent)
-        result = []
-        for i in reversed(parents):
-            result.append(i)
-        return list(result)
-
-    def parents_aware_forum(self, forum):
-        self.populate_tree()
-        proxy = Forum()
-        try:
-            proxy.id = forum.pk
-            proxy.pk = forum.pk
-        except AttributeError:
-            proxy.id = forum
-            proxy.pk = forum
-        proxy.closed = False
-        for parent in self.forum_parents(proxy.pk):
-            if parent.closed:
-                proxy.closed = True
-                return proxy
-        return proxy
-
-    def treelist(self, acl, parent=None, tracker=None):
-        complete_list = []
-        forums_list = []
-        parents = {}
-
-        if parent:
-            queryset = Forum.objects.filter(pk__in=acl.known_forums).filter(lft__gt=parent.lft).filter(rght__lt=parent.rght).order_by('lft')
-        else:
-            queryset = Forum.objects.filter(pk__in=acl.known_forums).order_by('lft')
-
-        for forum in queryset:
-            forum.subforums = []
-            forum.is_read = False
-            if tracker:
-                forum.is_read = tracker.is_read(forum)
-            parents[forum.pk] = forum
-            complete_list.append(forum)
-            if forum.parent_id in parents:
-                parents[forum.parent_id].subforums.append(forum)
-            else:
-                forums_list.append(forum)
-
-        # Second iteration - sum up forum counters
-        for forum in reversed(complete_list):
-            if forum.parent_id in parents and parents[forum.parent_id].type != 'redirect':
-                parents[forum.parent_id].threads += forum.threads
-                parents[forum.parent_id].posts += forum.posts
-                if acl.can_browse(forum.pk):
-                    # If forum is unread, make parent unread too
-                    if not forum.is_read:
-                        parents[forum.parent_id].is_read = False
-                    # Sum stats
-                    if forum.last_thread_date and (not parents[forum.parent_id].last_thread_date or forum.last_thread_date > parents[forum.parent_id].last_thread_date):
-                        parents[forum.parent_id].last_thread_id = forum.last_thread_id
-                        parents[forum.parent_id].last_thread_name = forum.last_thread_name
-                        parents[forum.parent_id].last_thread_slug = forum.last_thread_slug
-                        parents[forum.parent_id].last_thread_date = forum.last_thread_date
-                        parents[forum.parent_id].last_poster_id = forum.last_poster_id
-                        parents[forum.parent_id].last_poster_name = forum.last_poster_name
-                        parents[forum.parent_id].last_poster_slug = forum.last_poster_slug
-                        parents[forum.parent_id].last_poster_style = forum.last_poster_style
-        return forums_list
-    
-    def ignored_users(self, user, forums):
-        check_ids = []
-        for forum in forums:
-            forum.last_poster_ignored = False
-            if user.is_authenticated() and user.pk != forum.last_poster_id and forum.last_poster_id and not forum.last_poster_id in check_ids:
-                check_ids.append(forum.last_poster_id)
-        ignored_ids = []
-        if check_ids and user.is_authenticated():
-            for user in user.ignores.filter(id__in=check_ids).values('id'):
-                ignored_ids.append(user['id'])
-
-    def readable_forums(self, acl, include_special=False):
-        self.populate_tree()
-        readable = []
-        for pk, forum in self.forums_tree.items():
-            if ((include_special or not forum.special) and 
-                    acl.forums.can_browse(forum.pk) and
-                    acl.threads.acl[forum.pk]['can_read_threads'] == 2):
-                readable.append(forum.pk)
-        return readable
-
-    def starter_readable_forums(self, acl):
-        self.populate_tree()
-        readable = []
-        for pk, forum in self.forums_tree.items():
-            if (not forum.special and 
-                    acl.forums.can_browse(forum.pk) and
-                    acl.threads.acl[forum.pk]['can_read_threads'] == 1):
-                readable.append(forum.pk)
-        return readable
-
-
-    def forum_by_name(self, forum, acl):
-        forums = self.readable_forums(acl, True)
-        forum = forum.lower()
-        for f in forums:
-            f = self.forums_tree[f]
-            if forum == unicode(f).lower():
-                return f
-        forum_len = len(forum)
-        for f in forums:
-            f = self.forums_tree[f]
-            name = unicode(f).lower()
-            if forum == unicode(f).lower()[0:forum_len]:
-                return f
-        return None
-
-
-class Forum(MPTTModel):
-    parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
-    type = models.CharField(max_length=12)
-    special = models.CharField(max_length=255, null=True, blank=True)
-    name = models.CharField(max_length=255)
-    slug = models.SlugField(max_length=255)
-    description = models.TextField(null=True, blank=True)
-    description_preparsed = models.TextField(null=True, blank=True)
-    threads = models.PositiveIntegerField(default=0)
-    threads_delta = models.PositiveIntegerField(default=0)
-    posts = models.PositiveIntegerField(default=0)
-    posts_delta = models.IntegerField(default=0)
-    redirects = models.PositiveIntegerField(default=0)
-    redirects_delta = models.IntegerField(default=0)
-    last_thread = models.ForeignKey('Thread', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
-    last_thread_name = models.CharField(max_length=255, null=True, blank=True)
-    last_thread_slug = models.SlugField(max_length=255, null=True, blank=True)
-    last_thread_date = models.DateTimeField(null=True, blank=True)
-    last_poster = models.ForeignKey('User', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
-    last_poster_name = models.CharField(max_length=255, null=True, blank=True)
-    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
-    last_poster_style = models.CharField(max_length=255, null=True, blank=True)
-    prune_start = models.PositiveIntegerField(default=0)
-    prune_last = models.PositiveIntegerField(default=0)
-    pruned_archive = models.ForeignKey('self', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
-    redirect = models.CharField(max_length=255, null=True, blank=True)
-    attrs = models.CharField(max_length=255, null=True, blank=True)
-    show_details = models.BooleanField(default=True)
-    style = models.CharField(max_length=255, null=True, blank=True)
-    closed = models.BooleanField(default=False)
-
-    objects = ForumManager()
-
-    class Meta:
-        app_label = 'misago'
-    
-    def save(self, *args, **kwargs):
-        super(Forum, self).save(*args, **kwargs)
-        cache.delete('forums_tree')
-    
-    def delete(self, *args, **kwargs):
-        delete_forum_content.send(sender=self)
-        super(Forum, self).delete(*args, **kwargs)
-        cache.delete('forums_tree')
-
-    def __unicode__(self):
-        if self.special == 'private_threads':
-           return unicode(_('Private Threads'))
-        if self.special == 'reports':
-           return unicode(_('Reports'))
-        if self.special == 'root':
-           return unicode(_('Root Category'))
-        return unicode(self.name)
-
-    @property
-    def url(self):
-        if self.special == 'private_threads':
-           reverse('private_threads')
-        if self.special == 'reports':
-           reverse('reports')
-        if self.type == 'category':
-            return reverse('category', kwargs={'forum': self.pk, 'slug': self.slug})
-        if self.type == 'redirect':
-            return reverse('redirect', kwargs={'forum': self.pk, 'slug': self.slug})
-        return reverse('forum', kwargs={'forum': self.pk, 'slug': self.slug})
-
-    def thread_link(self, extra):
-        if self.special == 'private_threads':
-           route_prefix = 'private_thread'
-        if self.special == 'reports':
-           route_prefix = 'report'
-        else:
-            route_prefix = 'thread'
-        if extra:
-            return '%s_%s' % (route_prefix, extra) if extra else route_prefix
-        return route_prefix
-
-    def thread_url(self, thread, route=None):
-        route_prefix = 'thread'
-        if self.special:
-            route_prefix = self.special[0:-1]
-        link = '%s_%s' % (route_prefix, route) if route else route_prefix
-        return reverse(link, kwargs={'thread': thread.pk, 'slug': thread.slug})
-
-    def set_description(self, description):
-        self.description = description.strip()
-        self.description_preparsed = ''
-        if self.description:
-            import markdown
-            self.description_preparsed = markdown.markdown(description, safe_mode='escape', output_format=settings.OUTPUT_FORMAT)
-
-    def copy_permissions(self, target):
-        if target.pk != self.pk:
-            from misago.models import Role
-            for role in Role.objects.all():
-                perms = role.permissions
-                try:
-                    perms['forums'][self.pk] = perms['forums'][target.pk]
-                    role.permissions = perms
-                    role.save(force_update=True)
-                except KeyError:
-                    pass
-
-    def move_content(self, target):
-        move_forum_content.send(sender=self, move_to=target)
-
-    def sync_name(self):
-        rename_forum.send(sender=self)
-
-    def attr(self, att):
-        if self.attrs:
-            return att in self.attrs.split()
-        return False
-
-    def redirect_domain(self):
-        hostname = urlparse.urlparse(self.redirect).hostname
-        scheme = urlparse.urlparse(self.redirect).scheme
-        if scheme:
-            scheme = '%s://' % scheme
-        return '%s%s' % (scheme, hostname)
-
-    def new_last_thread(self, thread):
-        self.last_thread = thread
-        self.last_thread_name = thread.name
-        self.last_thread_slug = thread.slug
-        self.last_thread_date = thread.last
-        self.last_poster = thread.last_poster
-        self.last_poster_name = thread.last_poster_name
-        self.last_poster_slug = thread.last_poster_slug
-        self.last_poster_style = thread.last_poster_style
-
-    def sync_last(self):
-        self.last_poster = None
-        self.last_poster_name = None
-        self.last_poster_slug = None
-        self.last_poster_style = None
-        self.last_thread = None
-        self.last_thread_date = None
-        self.last_thread_name = None
-        self.last_thread_slug = None
-        try:
-            last_thread = self.thread_set.filter(moderated=False).filter(deleted=False).order_by('-last').all()[:1][0]
-            self.last_poster_name = last_thread.last_poster_name
-            self.last_poster_slug = last_thread.last_poster_slug
-            self.last_poster_style = last_thread.last_poster_style
-            if last_thread.last_poster:
-                self.last_poster = last_thread.last_poster
-            self.last_thread = last_thread
-            self.last_thread_date = last_thread.last
-            self.last_thread_name = last_thread.name
-            self.last_thread_slug = last_thread.slug
-        except (IndexError, AttributeError):
-            pass
-
-    def sync(self):
-        threads_qs = self.thread_set.filter(moderated=False).filter(deleted=False)
-        self.posts = self.threads = threads_qs.count()
-        replies = threads_qs.aggregate(Sum('replies'))
-        if replies['replies__sum']:
-            self.posts += replies['replies__sum']
-        self.sync_last()
-
-    def prune(self):
-        pass
-
-
-"""
-Signals
-"""
-def rename_user_handler(sender, **kwargs):
-    Forum.objects.filter(last_poster=sender).update(
-                                                    last_poster_name=sender.username,
-                                                    last_poster_slug=sender.username_slug,
-                                                    )
-
+import urlparse
+import threading
+from mptt.managers import TreeManager
+from mptt.models import MPTTModel, TreeForeignKey
+from django.conf import settings
+from django.core.cache import cache
+from django.core.urlresolvers import reverse
+from django.db import models
+from django.db.models import Sum
+from django.utils.translation import ugettext_lazy as _
+from misago.signals import delete_forum_content, move_forum_content, rename_forum, rename_user
+
+_thread_local = threading.local()
+
+class ForumManager(TreeManager):
+    @property
+    def forums_tree(self):
+        try:
+            return _thread_local.misago_forums_tree
+        except AttributeError:
+            _thread_local.misago_forums_tree = None
+        return _thread_local.misago_forums_tree
+
+    @forums_tree.setter
+    def forums_tree(self, value):
+        _thread_local.misago_forums_tree = value
+
+    def special_pk(self, name):
+        self.populate_tree()
+        return self.forums_tree.get(name).pk
+
+    def special_model(self, name):
+        self.populate_tree()
+        return self.forums_tree.get(name)
+
+    def populate_tree(self, force=False):
+        if not self.forums_tree:
+            self.forums_tree = cache.get('forums_tree')
+        if not self.forums_tree or force:
+            self.forums_tree = {}
+            for forum in Forum.objects.order_by('lft'):
+                self.forums_tree[forum.pk] = forum
+                if forum.special:
+                    self.forums_tree[forum.special] = forum
+            cache.set('forums_tree', self.forums_tree)
+
+    def forum_parents(self, forum, include_self=False):
+        self.populate_tree()
+        parents = []
+        parent = self.forums_tree[forum]
+        if include_self:
+            parents.append(parent)
+        while parent.level > 1:
+            parent = self.forums_tree[parent.parent_id]
+            parents.append(parent)
+        result = []
+        for i in reversed(parents):
+            result.append(i)
+        return list(result)
+
+    def parents_aware_forum(self, forum):
+        self.populate_tree()
+        proxy = Forum()
+        try:
+            proxy.id = forum.pk
+            proxy.pk = forum.pk
+        except AttributeError:
+            proxy.id = forum
+            proxy.pk = forum
+        proxy.closed = False
+        for parent in self.forum_parents(proxy.pk):
+            if parent.closed:
+                proxy.closed = True
+                return proxy
+        return proxy
+
+    def treelist(self, acl, parent=None, tracker=None):
+        complete_list = []
+        forums_list = []
+        parents = {}
+
+        if parent:
+            queryset = Forum.objects.filter(pk__in=acl.known_forums).filter(lft__gt=parent.lft).filter(rght__lt=parent.rght).order_by('lft')
+        else:
+            queryset = Forum.objects.filter(pk__in=acl.known_forums).order_by('lft')
+
+        for forum in queryset:
+            forum.subforums = []
+            forum.is_read = False
+            if tracker:
+                forum.is_read = tracker.is_read(forum)
+            parents[forum.pk] = forum
+            complete_list.append(forum)
+            if forum.parent_id in parents:
+                parents[forum.parent_id].subforums.append(forum)
+            else:
+                forums_list.append(forum)
+
+        # Second iteration - sum up forum counters
+        for forum in reversed(complete_list):
+            if forum.parent_id in parents and parents[forum.parent_id].type != 'redirect':
+                parents[forum.parent_id].threads += forum.threads
+                parents[forum.parent_id].posts += forum.posts
+                if acl.can_browse(forum.pk):
+                    # If forum is unread, make parent unread too
+                    if not forum.is_read:
+                        parents[forum.parent_id].is_read = False
+                    # Sum stats
+                    if forum.last_thread_date and (not parents[forum.parent_id].last_thread_date or forum.last_thread_date > parents[forum.parent_id].last_thread_date):
+                        parents[forum.parent_id].last_thread_id = forum.last_thread_id
+                        parents[forum.parent_id].last_thread_name = forum.last_thread_name
+                        parents[forum.parent_id].last_thread_slug = forum.last_thread_slug
+                        parents[forum.parent_id].last_thread_date = forum.last_thread_date
+                        parents[forum.parent_id].last_poster_id = forum.last_poster_id
+                        parents[forum.parent_id].last_poster_name = forum.last_poster_name
+                        parents[forum.parent_id].last_poster_slug = forum.last_poster_slug
+                        parents[forum.parent_id].last_poster_style = forum.last_poster_style
+        return forums_list
+    
+    def ignored_users(self, user, forums):
+        check_ids = []
+        for forum in forums:
+            forum.last_poster_ignored = False
+            if user.is_authenticated() and user.pk != forum.last_poster_id and forum.last_poster_id and not forum.last_poster_id in check_ids:
+                check_ids.append(forum.last_poster_id)
+        ignored_ids = []
+        if check_ids and user.is_authenticated():
+            for user in user.ignores.filter(id__in=check_ids).values('id'):
+                ignored_ids.append(user['id'])
+
+    def readable_forums(self, acl, include_special=False):
+        self.populate_tree()
+        readable = []
+        for pk, forum in self.forums_tree.items():
+            if ((include_special or not forum.special) and 
+                    acl.forums.can_browse(forum.pk) and
+                    acl.threads.acl[forum.pk]['can_read_threads'] == 2):
+                readable.append(forum.pk)
+        return readable
+
+    def starter_readable_forums(self, acl):
+        self.populate_tree()
+        readable = []
+        for pk, forum in self.forums_tree.items():
+            if (not forum.special and 
+                    acl.forums.can_browse(forum.pk) and
+                    acl.threads.acl[forum.pk]['can_read_threads'] == 1):
+                readable.append(forum.pk)
+        return readable
+
+
+    def forum_by_name(self, forum, acl):
+        forums = self.readable_forums(acl, True)
+        forum = forum.lower()
+        for f in forums:
+            f = self.forums_tree[f]
+            if forum == unicode(f).lower():
+                return f
+        forum_len = len(forum)
+        for f in forums:
+            f = self.forums_tree[f]
+            name = unicode(f).lower()
+            if forum == unicode(f).lower()[0:forum_len]:
+                return f
+        return None
+
+
+class Forum(MPTTModel):
+    parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
+    type = models.CharField(max_length=12)
+    special = models.CharField(max_length=255, null=True, blank=True)
+    name = models.CharField(max_length=255)
+    slug = models.SlugField(max_length=255)
+    description = models.TextField(null=True, blank=True)
+    description_preparsed = models.TextField(null=True, blank=True)
+    threads = models.PositiveIntegerField(default=0)
+    threads_delta = models.PositiveIntegerField(default=0)
+    posts = models.PositiveIntegerField(default=0)
+    posts_delta = models.IntegerField(default=0)
+    redirects = models.PositiveIntegerField(default=0)
+    redirects_delta = models.IntegerField(default=0)
+    last_thread = models.ForeignKey('Thread', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
+    last_thread_name = models.CharField(max_length=255, null=True, blank=True)
+    last_thread_slug = models.SlugField(max_length=255, null=True, blank=True)
+    last_thread_date = models.DateTimeField(null=True, blank=True)
+    last_poster = models.ForeignKey('User', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
+    last_poster_name = models.CharField(max_length=255, null=True, blank=True)
+    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
+    last_poster_style = models.CharField(max_length=255, null=True, blank=True)
+    prune_start = models.PositiveIntegerField(default=0)
+    prune_last = models.PositiveIntegerField(default=0)
+    pruned_archive = models.ForeignKey('self', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
+    redirect = models.CharField(max_length=255, null=True, blank=True)
+    attrs = models.CharField(max_length=255, null=True, blank=True)
+    show_details = models.BooleanField(default=True)
+    style = models.CharField(max_length=255, null=True, blank=True)
+    closed = models.BooleanField(default=False)
+
+    objects = ForumManager()
+
+    class Meta:
+        app_label = 'misago'
+    
+    def save(self, *args, **kwargs):
+        super(Forum, self).save(*args, **kwargs)
+        cache.delete('forums_tree')
+    
+    def delete(self, *args, **kwargs):
+        delete_forum_content.send(sender=self)
+        super(Forum, self).delete(*args, **kwargs)
+        cache.delete('forums_tree')
+
+    def __unicode__(self):
+        if self.special == 'private_threads':
+           return unicode(_('Private Threads'))
+        if self.special == 'reports':
+           return unicode(_('Reports'))
+        if self.special == 'root':
+           return unicode(_('Root Category'))
+        return unicode(self.name)
+
+    @property
+    def url(self):
+        if self.special == 'private_threads':
+           reverse('private_threads')
+        if self.special == 'reports':
+           reverse('reports')
+        if self.type == 'category':
+            return reverse('category', kwargs={'forum': self.pk, 'slug': self.slug})
+        if self.type == 'redirect':
+            return reverse('redirect', kwargs={'forum': self.pk, 'slug': self.slug})
+        return reverse('forum', kwargs={'forum': self.pk, 'slug': self.slug})
+
+    def thread_link(self, extra):
+        if self.special == 'private_threads':
+           route_prefix = 'private_thread'
+        if self.special == 'reports':
+           route_prefix = 'report'
+        else:
+            route_prefix = 'thread'
+        if extra:
+            return '%s_%s' % (route_prefix, extra) if extra else route_prefix
+        return route_prefix
+
+    def thread_url(self, thread, route=None):
+        route_prefix = 'thread'
+        if self.special:
+            route_prefix = self.special[0:-1]
+        link = '%s_%s' % (route_prefix, route) if route else route_prefix
+        return reverse(link, kwargs={'thread': thread.pk, 'slug': thread.slug})
+
+    def set_description(self, description):
+        self.description = description.strip()
+        self.description_preparsed = ''
+        if self.description:
+            import markdown
+            self.description_preparsed = markdown.markdown(description, safe_mode='escape', output_format=settings.OUTPUT_FORMAT)
+
+    def copy_permissions(self, target):
+        if target.pk != self.pk:
+            from misago.models import Role
+            for role in Role.objects.all():
+                perms = role.permissions
+                try:
+                    perms['forums'][self.pk] = perms['forums'][target.pk]
+                    role.permissions = perms
+                    role.save(force_update=True)
+                except KeyError:
+                    pass
+
+    def move_content(self, target):
+        move_forum_content.send(sender=self, move_to=target)
+
+    def sync_name(self):
+        rename_forum.send(sender=self)
+
+    def attr(self, att):
+        if self.attrs:
+            return att in self.attrs.split()
+        return False
+
+    def redirect_domain(self):
+        hostname = urlparse.urlparse(self.redirect).hostname
+        scheme = urlparse.urlparse(self.redirect).scheme
+        if scheme:
+            scheme = '%s://' % scheme
+        return '%s%s' % (scheme, hostname)
+
+    def new_last_thread(self, thread):
+        self.last_thread = thread
+        self.last_thread_name = thread.name
+        self.last_thread_slug = thread.slug
+        self.last_thread_date = thread.last
+        self.last_poster = thread.last_poster
+        self.last_poster_name = thread.last_poster_name
+        self.last_poster_slug = thread.last_poster_slug
+        self.last_poster_style = thread.last_poster_style
+
+    def sync_last(self):
+        self.last_poster = None
+        self.last_poster_name = None
+        self.last_poster_slug = None
+        self.last_poster_style = None
+        self.last_thread = None
+        self.last_thread_date = None
+        self.last_thread_name = None
+        self.last_thread_slug = None
+        try:
+            last_thread = self.thread_set.filter(moderated=False).filter(deleted=False).order_by('-last').all()[:1][0]
+            self.last_poster_name = last_thread.last_poster_name
+            self.last_poster_slug = last_thread.last_poster_slug
+            self.last_poster_style = last_thread.last_poster_style
+            if last_thread.last_poster:
+                self.last_poster = last_thread.last_poster
+            self.last_thread = last_thread
+            self.last_thread_date = last_thread.last
+            self.last_thread_name = last_thread.name
+            self.last_thread_slug = last_thread.slug
+        except (IndexError, AttributeError):
+            pass
+
+    def sync(self):
+        threads_qs = self.thread_set.filter(moderated=False).filter(deleted=False)
+        self.posts = self.threads = threads_qs.count()
+        replies = threads_qs.aggregate(Sum('replies'))
+        if replies['replies__sum']:
+            self.posts += replies['replies__sum']
+        self.sync_last()
+
+    def prune(self):
+        pass
+
+
+"""
+Signals
+"""
+def rename_user_handler(sender, **kwargs):
+    Forum.objects.filter(last_poster=sender).update(
+                                                    last_poster_name=sender.username,
+                                                    last_poster_slug=sender.username_slug,
+                                                    )
+
 rename_user.connect(rename_user_handler, dispatch_uid='rename_forums_last_poster')
 rename_user.connect(rename_user_handler, dispatch_uid='rename_forums_last_poster')

+ 28 - 28
misago/models/forumreadmodel.py

@@ -1,29 +1,29 @@
-from datetime import timedelta
-from django.conf import settings
-from django.db import models
-from django.utils import timezone
-from misago.signals import move_forum_content
-
-class ForumRead(models.Model):
-    user = models.ForeignKey('User')
-    forum = models.ForeignKey('Forum')
-    updated = models.DateTimeField()
-    cleared = models.DateTimeField()
-    
-    class Meta:
-        app_label = 'misago'
-
-    def get_threads(self):
-        from misago.models import ThreadRead
-        
-        threads = {}
-        for thread in ThreadRead.objects.filter(user_id=self.user_id, forum_id=self.forum_id, updated__gte=(timezone.now() - timedelta(days=settings.READS_TRACKER_LENGTH))):
-            threads[thread.thread_id] = thread
-        return threads
-
-
-def move_forum_content_handler(sender, **kwargs):
-    ForumRead.objects.filter(forum=sender).delete()
-    ForumRead.objects.filter(forum=kwargs['move_to']).delete()
-
+from datetime import timedelta
+from django.conf import settings
+from django.db import models
+from django.utils import timezone
+from misago.signals import move_forum_content
+
+class ForumRead(models.Model):
+    user = models.ForeignKey('User')
+    forum = models.ForeignKey('Forum')
+    updated = models.DateTimeField()
+    cleared = models.DateTimeField()
+    
+    class Meta:
+        app_label = 'misago'
+
+    def get_threads(self):
+        from misago.models import ThreadRead
+        
+        threads = {}
+        for thread in ThreadRead.objects.filter(user_id=self.user_id, forum_id=self.forum_id, updated__gte=(timezone.now() - timedelta(days=settings.READS_TRACKER_LENGTH))):
+            threads[thread.thread_id] = thread
+        return threads
+
+
+def move_forum_content_handler(sender, **kwargs):
+    ForumRead.objects.filter(forum=sender).delete()
+    ForumRead.objects.filter(forum=kwargs['move_to']).delete()
+
 move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_reads")
 move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_reads")

+ 37 - 37
misago/models/forumrolemodel.py

@@ -1,37 +1,37 @@
-from django.db import models
-from django.utils.translation import ugettext as _
-import base64
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-
-class ForumRole(models.Model):
-    name = models.CharField(max_length=255)
-    _permissions = models.TextField(db_column = 'permissions', null=True, blank=True)
-    permissions_cache = {}
-
-    class Meta:
-        app_label = 'misago'
-
-    def __unicode__(self):
-        return unicode(_(self.name))
-
-    @property
-    def permissions(self):
-        if self.permissions_cache:
-            return self.permissions_cache
-
-        try:
-            self.permissions_cache = pickle.loads(base64.decodestring(self._permissions))
-        except Exception:
-            # ValueError, SuspiciousOperation, unpickling exceptions. If any of
-            # these happen, just return an empty dictionary (an empty permissions list).
-            self.permissions_cache = {}
-
-        return self.permissions_cache
-
-    @permissions.setter
-    def permissions(self, permissions):
-        self.permissions_cache = permissions
-        self._permissions = base64.encodestring(pickle.dumps(permissions, pickle.HIGHEST_PROTOCOL))
+from django.db import models
+from django.utils.translation import ugettext as _
+import base64
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+class ForumRole(models.Model):
+    name = models.CharField(max_length=255)
+    _permissions = models.TextField(db_column = 'permissions', null=True, blank=True)
+    permissions_cache = {}
+
+    class Meta:
+        app_label = 'misago'
+
+    def __unicode__(self):
+        return unicode(_(self.name))
+
+    @property
+    def permissions(self):
+        if self.permissions_cache:
+            return self.permissions_cache
+
+        try:
+            self.permissions_cache = pickle.loads(base64.decodestring(self._permissions))
+        except Exception:
+            # ValueError, SuspiciousOperation, unpickling exceptions. If any of
+            # these happen, just return an empty dictionary (an empty permissions list).
+            self.permissions_cache = {}
+
+        return self.permissions_cache
+
+    @permissions.setter
+    def permissions(self, permissions):
+        self.permissions_cache = permissions
+        self._permissions = base64.encodestring(pickle.dumps(permissions, pickle.HIGHEST_PROTOCOL))

+ 73 - 73
misago/models/karmamodel.py

@@ -1,73 +1,73 @@
-from django.db import models
-from django.db.models import Sum
-from misago.signals import (merge_post, merge_thread, move_forum_content,
-                            move_post, move_thread, rename_user, sync_user_profile)
-
-class Karma(models.Model):
-    forum = models.ForeignKey('Forum')
-    thread = models.ForeignKey('Thread')
-    post = models.ForeignKey('Post')
-    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
-    user_name = models.CharField(max_length=255)
-    user_slug = models.CharField(max_length=255)
-    date = models.DateTimeField()
-    ip = models.GenericIPAddressField()
-    agent = models.CharField(max_length=255)
-    score = models.IntegerField(default=0)
-
-    class Meta:
-        app_label = 'misago'
-
-
-def rename_user_handler(sender, **kwargs):
-    Karma.objects.filter(user=sender).update(
-                                             user_name=sender.username,
-                                             user_slug=sender.username_slug,
-                                             )
-
-rename_user.connect(rename_user_handler, dispatch_uid="rename_user_karmas")
-
-
-def move_forum_content_handler(sender, **kwargs):
-    Karma.objects.filter(forum=sender).update(forum=kwargs['move_to'])
-
-move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_karmas")
-
-
-def move_thread_handler(sender, **kwargs):
-    Karma.objects.filter(thread=sender).update(forum=kwargs['move_to'])
-
-move_thread.connect(move_thread_handler, dispatch_uid="move_thread_karmas")
-
-
-def merge_thread_handler(sender, **kwargs):
-    Karma.objects.filter(thread=sender).update(thread=kwargs['new_thread'])
-
-merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_karmas")
-
-
-def move_posts_handler(sender, **kwargs):
-    Karma.objects.filter(post=sender).update(forum=kwargs['move_to'].forum, thread=kwargs['move_to'])
-
-move_post.connect(move_posts_handler, dispatch_uid="move_posts_karmas")
-
-
-def merge_posts_handler(sender, **kwargs):
-    Karma.objects.filter(post=sender).update(post=kwargs['new_post'])
-    kwargs['new_post'].upvotes += sender.upvotes
-    kwargs['new_post'].downvotes += sender.downvotes
-
-merge_post.connect(merge_posts_handler, dispatch_uid="merge_posts_karmas")
-
-
-def sync_user_handler(sender, **kwargs):
-    sender.karma_given_p = sender.karma_set.filter(score__gt=0).count()
-    sender.karma_given_n = sender.karma_set.filter(score__lt=0).count()
-    sender.karma_p = sender.post_set.all().aggregate(Sum('upvotes'))['upvotes__sum']
-    if not sender.karma_p:
-        sender.karma_p = 0
-    sender.karma_n = sender.post_set.all().aggregate(Sum('downvotes'))['downvotes__sum']
-    if not sender.karma_n:
-        sender.karma_n = 0
-
-sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_karmas")
+from django.db import models
+from django.db.models import Sum
+from misago.signals import (merge_post, merge_thread, move_forum_content,
+                            move_post, move_thread, rename_user, sync_user_profile)
+
+class Karma(models.Model):
+    forum = models.ForeignKey('Forum')
+    thread = models.ForeignKey('Thread')
+    post = models.ForeignKey('Post')
+    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
+    user_name = models.CharField(max_length=255)
+    user_slug = models.CharField(max_length=255)
+    date = models.DateTimeField()
+    ip = models.GenericIPAddressField()
+    agent = models.CharField(max_length=255)
+    score = models.IntegerField(default=0)
+
+    class Meta:
+        app_label = 'misago'
+
+
+def rename_user_handler(sender, **kwargs):
+    Karma.objects.filter(user=sender).update(
+                                             user_name=sender.username,
+                                             user_slug=sender.username_slug,
+                                             )
+
+rename_user.connect(rename_user_handler, dispatch_uid="rename_user_karmas")
+
+
+def move_forum_content_handler(sender, **kwargs):
+    Karma.objects.filter(forum=sender).update(forum=kwargs['move_to'])
+
+move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_karmas")
+
+
+def move_thread_handler(sender, **kwargs):
+    Karma.objects.filter(thread=sender).update(forum=kwargs['move_to'])
+
+move_thread.connect(move_thread_handler, dispatch_uid="move_thread_karmas")
+
+
+def merge_thread_handler(sender, **kwargs):
+    Karma.objects.filter(thread=sender).update(thread=kwargs['new_thread'])
+
+merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_karmas")
+
+
+def move_posts_handler(sender, **kwargs):
+    Karma.objects.filter(post=sender).update(forum=kwargs['move_to'].forum, thread=kwargs['move_to'])
+
+move_post.connect(move_posts_handler, dispatch_uid="move_posts_karmas")
+
+
+def merge_posts_handler(sender, **kwargs):
+    Karma.objects.filter(post=sender).update(post=kwargs['new_post'])
+    kwargs['new_post'].upvotes += sender.upvotes
+    kwargs['new_post'].downvotes += sender.downvotes
+
+merge_post.connect(merge_posts_handler, dispatch_uid="merge_posts_karmas")
+
+
+def sync_user_handler(sender, **kwargs):
+    sender.karma_given_p = sender.karma_set.filter(score__gt=0).count()
+    sender.karma_given_n = sender.karma_set.filter(score__lt=0).count()
+    sender.karma_p = sender.post_set.all().aggregate(Sum('upvotes'))['upvotes__sum']
+    if not sender.karma_p:
+        sender.karma_p = 0
+    sender.karma_n = sender.post_set.all().aggregate(Sum('downvotes'))['downvotes__sum']
+    if not sender.karma_n:
+        sender.karma_n = 0
+
+sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_karmas")

+ 26 - 26
misago/models/monitoritemmodel.py

@@ -1,26 +1,26 @@
-from django.db import models
-
-class MonitorItem(models.Model):
-    id = models.CharField(max_length=255, primary_key=True)
-    _value = models.TextField(db_column="value", blank=True, null=True)
-    type = models.CharField(max_length=255, default="int")
-    updated = models.DateTimeField(blank=True, null=True)
-
-    class Meta:
-        app_label = 'misago'
-
-    @property
-    def value(self):
-        if self.type in ("int", "integer"):
-            return int(self._value)
-        if self.type == "float":
-            return float(self._value)
-        return self._value
-
-    @value.setter
-    def value(self, v):
-        if self.type in ("int", "integer"):
-            self._value = int(v)
-        if self.type == "float":
-            self._value = float(v)
-        self._value = v
+from django.db import models
+
+class MonitorItem(models.Model):
+    id = models.CharField(max_length=255, primary_key=True)
+    _value = models.TextField(db_column="value", blank=True, null=True)
+    type = models.CharField(max_length=255, default="int")
+    updated = models.DateTimeField(blank=True, null=True)
+
+    class Meta:
+        app_label = 'misago'
+
+    @property
+    def value(self):
+        if self.type in ("int", "integer"):
+            return int(self._value)
+        if self.type == "float":
+            return float(self._value)
+        return self._value
+
+    @value.setter
+    def value(self, v):
+        if self.type in ("int", "integer"):
+            self._value = int(v)
+        if self.type == "float":
+            self._value = float(v)
+        self._value = v

+ 35 - 35
misago/models/newslettermodel.py

@@ -1,36 +1,36 @@
-from django.db import models
-from misago.utils.strings import random_string
-
-class Newsletter(models.Model):
-    name = models.CharField(max_length=255)
-    token = models.CharField(max_length=32)
-    step_size = models.PositiveIntegerField(default=0)
-    progress = models.PositiveIntegerField(default=0)
-    content_html = models.TextField(null=True, blank=True)
-    content_plain = models.TextField(null=True, blank=True)
-    ignore_subscriptions = models.BooleanField(default=False)
-    ranks = models.ManyToManyField('Rank')
-
-    class Meta:
-        app_label = 'misago'
-
-    def generate_token(self):
-        self.token = random_string(32)
-
-    def parse_name(self, tokens):
-        name = self.name
-        for key in tokens:
-            name = name.replace(key, tokens[key])
-        return name
-
-    def parse_html(self, tokens):
-        content_html = self.content_html
-        for key in tokens:
-            content_html = content_html.replace(key, tokens[key])
-        return content_html
-
-    def parse_plain(self, tokens):
-        content_plain = self.content_plain
-        for key in tokens:
-            content_plain = content_plain.replace(key, tokens[key])
+from django.db import models
+from misago.utils.strings import random_string
+
+class Newsletter(models.Model):
+    name = models.CharField(max_length=255)
+    token = models.CharField(max_length=32)
+    step_size = models.PositiveIntegerField(default=0)
+    progress = models.PositiveIntegerField(default=0)
+    content_html = models.TextField(null=True, blank=True)
+    content_plain = models.TextField(null=True, blank=True)
+    ignore_subscriptions = models.BooleanField(default=False)
+    ranks = models.ManyToManyField('Rank')
+
+    class Meta:
+        app_label = 'misago'
+
+    def generate_token(self):
+        self.token = random_string(32)
+
+    def parse_name(self, tokens):
+        name = self.name
+        for key in tokens:
+            name = name.replace(key, tokens[key])
+        return name
+
+    def parse_html(self, tokens):
+        content_html = self.content_html
+        for key in tokens:
+            content_html = content_html.replace(key, tokens[key])
+        return content_html
+
+    def parse_plain(self, tokens):
+        content_plain = self.content_plain
+        for key in tokens:
+            content_plain = content_plain.replace(key, tokens[key])
         return content_plain
         return content_plain

+ 186 - 186
misago/models/postmodel.py

@@ -1,186 +1,186 @@
-from django.db import models
-from django.db.models import F
-from django.db.models.signals import pre_save, pre_delete
-from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
-from misago.markdown import clear_markdown
-from misago.signals import (delete_user_content, merge_post, merge_thread,
-                            move_forum_content, move_post, move_thread,
-                            rename_user, sync_user_profile)
-from misago.utils.translation import ugettext_lazy
-
-class PostManager(models.Manager):
-    def filter_stats(self, start, end):
-        return self.filter(date__gte=start).filter(date__lte=end)
-
-
-class Post(models.Model):
-    forum = models.ForeignKey('Forum')
-    thread = models.ForeignKey('Thread')
-    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
-    user_name = models.CharField(max_length=255)
-    ip = models.GenericIPAddressField()
-    agent = models.CharField(max_length=255)
-    post = models.TextField()
-    post_preparsed = models.TextField()
-    upvotes = models.PositiveIntegerField(default=0)
-    downvotes = models.PositiveIntegerField(default=0)
-    mentions = models.ManyToManyField('User', related_name="mention_set")
-    date = models.DateTimeField()
-    current_date = models.DateTimeField(db_index=True)
-    edits = models.PositiveIntegerField(default=0)
-    edit_reason = models.CharField(max_length=255, null=True, blank=True)
-    edit_user = models.ForeignKey('User', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
-    edit_user_name = models.CharField(max_length=255, null=True, blank=True)
-    edit_user_slug = models.SlugField(max_length=255, null=True, blank=True)
-    delete_date = models.DateTimeField(null=True, blank=True)
-    reported = models.BooleanField(default=False, db_index=True)
-    reports = models.CharField(max_length=255, null=True, blank=True)
-    moderated = models.BooleanField(default=False)
-    deleted = models.BooleanField(default=False)
-    protected = models.BooleanField(default=False)
-
-    objects = PostManager()
-
-    statistics_name = _('New Posts')
-
-    class Meta:
-        app_label = 'misago'
-
-    @property
-    def timeline_date(self):
-        return self.date
-
-    def save(self, *args, **kwargs):
-        self.current_date = timezone.now()
-        return super(Post, self).save(*args, **kwargs)
-
-    def delete(self, *args, **kwargs):
-        """
-        FUGLY HAX for weird stuff that happens with
-        relations on model deletion in MySQL
-        """
-        if self.reported:
-            self.report_set.update(report_for=None)
-        return super(Post, self).delete(*args, **kwargs)
-
-    def get_date(self):
-        return self.date
-
-    def quote(self):
-        quote = []
-        quote.append('@%s' % self.user_name)
-        for line in self.post.splitlines():
-            quote.append('> %s' % line)
-        quote.append('\r\n')
-        return '\r\n'.join(quote)
-
-    @property
-    def post_clean(self):
-        return clear_markdown(self.post_preparsed)
-
-    def move_to(self, thread):
-        move_post.send(sender=self, move_to=thread)
-        self.thread = thread
-        self.forum = thread.forum
-        
-    def merge_with(self, post):
-        post.post = '%s\n- - -\n%s' % (post.post, self.post)
-        merge_post.send(sender=self, new_post=post)
-
-    def notify_mentioned(self, request, thread_type, users):
-        from misago.acl.builder import acl
-        from misago.acl.exceptions import ACLError403, ACLError404
-        
-        mentioned = self.mentions.all()
-        for slug, user in users.items():
-            if user.pk != request.user.pk and user not in mentioned:
-                self.mentions.add(user)
-                try:                    
-                    acl = acl(request, user)
-                    acl.forums.allow_forum_view(self.forum)
-                    acl.threads.allow_thread_view(user, self.thread)
-                    acl.threads.allow_post_view(user, self.thread, self)
-                    if not user.is_ignoring(request.user):
-                        alert = user.alert(ugettext_lazy("%(username)s has mentioned you in his reply in thread %(thread)s").message)
-                        alert.profile('username', request.user)
-                        alert.post('thread', thread_type, self.thread, self)
-                        alert.save_all()
-                except (ACLError403, ACLError404):
-                    pass
-
-    def is_reported(self):
-        self.reported = self.report_set.filter(weight=2).count() > 0
-
-    def live_report(self):
-        try:
-            return self.report_set.filter(weight=2)[0]
-        except IndexError:
-            return None
-
-    def add_reporter(self, user):
-        if not self.reports:
-            self.reports = ','
-        self.reports += '%s,' % user.pk
-
-    def reported_by(self, user):
-        if not self.reports:
-            return False
-        try:
-            return ',%s,' % user.pk in self.reports
-        except AttributeError:
-            return ',%s,' % user in self.reports
-
-
-def rename_user_handler(sender, **kwargs):
-    Post.objects.filter(user=sender).update(
-                                            user_name=sender.username,
-                                            current_date=timezone.now(),
-                                            )
-    Post.objects.filter(edit_user=sender).update(
-                                                 edit_user_name=sender.username,
-                                                 edit_user_slug=sender.username_slug,
-                                                 )
-
-rename_user.connect(rename_user_handler, dispatch_uid="rename_user_posts")
-
-
-def delete_user_content_handler(sender, **kwargs):
-    from misago.models import Thread
-
-    threads = []
-    for post in sender.post_set.distinct().values('thread_id').iterator():
-        if not post['thread_id'] in threads:
-            threads.append(post['thread_id'])
-
-    sender.post_set.all().delete()
-
-    for thread in Thread.objects.filter(id__in=threads):
-        thread.sync()
-        thread.save(force_update=True)
-
-delete_user_content.connect(delete_user_content_handler, dispatch_uid="delete_user_posts")
-
-
-def move_forum_content_handler(sender, **kwargs):
-    Post.objects.filter(forum=sender).update(forum=kwargs['move_to'])
-
-move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_posts")
-
-
-def move_thread_handler(sender, **kwargs):
-    Post.objects.filter(thread=sender).update(forum=kwargs['move_to'])
-
-move_thread.connect(move_thread_handler, dispatch_uid="move_thread_posts")
-
-
-def merge_thread_handler(sender, **kwargs):
-    Post.objects.filter(thread=sender).update(thread=kwargs['new_thread'])
-
-merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_posts")
-
-
-def sync_user_handler(sender, **kwargs):
-    sender.posts = sender.post_set.count()
-
-sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_posts")
+from django.db import models
+from django.db.models import F
+from django.db.models.signals import pre_save, pre_delete
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
+from misago.markdown import clear_markdown
+from misago.signals import (delete_user_content, merge_post, merge_thread,
+                            move_forum_content, move_post, move_thread,
+                            rename_user, sync_user_profile)
+from misago.utils.translation import ugettext_lazy
+
+class PostManager(models.Manager):
+    def filter_stats(self, start, end):
+        return self.filter(date__gte=start).filter(date__lte=end)
+
+
+class Post(models.Model):
+    forum = models.ForeignKey('Forum')
+    thread = models.ForeignKey('Thread')
+    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
+    user_name = models.CharField(max_length=255)
+    ip = models.GenericIPAddressField()
+    agent = models.CharField(max_length=255)
+    post = models.TextField()
+    post_preparsed = models.TextField()
+    upvotes = models.PositiveIntegerField(default=0)
+    downvotes = models.PositiveIntegerField(default=0)
+    mentions = models.ManyToManyField('User', related_name="mention_set")
+    date = models.DateTimeField()
+    current_date = models.DateTimeField(db_index=True)
+    edits = models.PositiveIntegerField(default=0)
+    edit_reason = models.CharField(max_length=255, null=True, blank=True)
+    edit_user = models.ForeignKey('User', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
+    edit_user_name = models.CharField(max_length=255, null=True, blank=True)
+    edit_user_slug = models.SlugField(max_length=255, null=True, blank=True)
+    delete_date = models.DateTimeField(null=True, blank=True)
+    reported = models.BooleanField(default=False, db_index=True)
+    reports = models.CharField(max_length=255, null=True, blank=True)
+    moderated = models.BooleanField(default=False)
+    deleted = models.BooleanField(default=False)
+    protected = models.BooleanField(default=False)
+
+    objects = PostManager()
+
+    statistics_name = _('New Posts')
+
+    class Meta:
+        app_label = 'misago'
+
+    @property
+    def timeline_date(self):
+        return self.date
+
+    def save(self, *args, **kwargs):
+        self.current_date = timezone.now()
+        return super(Post, self).save(*args, **kwargs)
+
+    def delete(self, *args, **kwargs):
+        """
+        FUGLY HAX for weird stuff that happens with
+        relations on model deletion in MySQL
+        """
+        if self.reported:
+            self.report_set.update(report_for=None)
+        return super(Post, self).delete(*args, **kwargs)
+
+    def get_date(self):
+        return self.date
+
+    def quote(self):
+        quote = []
+        quote.append('@%s' % self.user_name)
+        for line in self.post.splitlines():
+            quote.append('> %s' % line)
+        quote.append('\r\n')
+        return '\r\n'.join(quote)
+
+    @property
+    def post_clean(self):
+        return clear_markdown(self.post_preparsed)
+
+    def move_to(self, thread):
+        move_post.send(sender=self, move_to=thread)
+        self.thread = thread
+        self.forum = thread.forum
+        
+    def merge_with(self, post):
+        post.post = '%s\n- - -\n%s' % (post.post, self.post)
+        merge_post.send(sender=self, new_post=post)
+
+    def notify_mentioned(self, request, thread_type, users):
+        from misago.acl.builder import acl
+        from misago.acl.exceptions import ACLError403, ACLError404
+        
+        mentioned = self.mentions.all()
+        for slug, user in users.items():
+            if user.pk != request.user.pk and user not in mentioned:
+                self.mentions.add(user)
+                try:                    
+                    acl = acl(request, user)
+                    acl.forums.allow_forum_view(self.forum)
+                    acl.threads.allow_thread_view(user, self.thread)
+                    acl.threads.allow_post_view(user, self.thread, self)
+                    if not user.is_ignoring(request.user):
+                        alert = user.alert(ugettext_lazy("%(username)s has mentioned you in his reply in thread %(thread)s").message)
+                        alert.profile('username', request.user)
+                        alert.post('thread', thread_type, self.thread, self)
+                        alert.save_all()
+                except (ACLError403, ACLError404):
+                    pass
+
+    def is_reported(self):
+        self.reported = self.report_set.filter(weight=2).count() > 0
+
+    def live_report(self):
+        try:
+            return self.report_set.filter(weight=2)[0]
+        except IndexError:
+            return None
+
+    def add_reporter(self, user):
+        if not self.reports:
+            self.reports = ','
+        self.reports += '%s,' % user.pk
+
+    def reported_by(self, user):
+        if not self.reports:
+            return False
+        try:
+            return ',%s,' % user.pk in self.reports
+        except AttributeError:
+            return ',%s,' % user in self.reports
+
+
+def rename_user_handler(sender, **kwargs):
+    Post.objects.filter(user=sender).update(
+                                            user_name=sender.username,
+                                            current_date=timezone.now(),
+                                            )
+    Post.objects.filter(edit_user=sender).update(
+                                                 edit_user_name=sender.username,
+                                                 edit_user_slug=sender.username_slug,
+                                                 )
+
+rename_user.connect(rename_user_handler, dispatch_uid="rename_user_posts")
+
+
+def delete_user_content_handler(sender, **kwargs):
+    from misago.models import Thread
+
+    threads = []
+    for post in sender.post_set.distinct().values('thread_id').iterator():
+        if not post['thread_id'] in threads:
+            threads.append(post['thread_id'])
+
+    sender.post_set.all().delete()
+
+    for thread in Thread.objects.filter(id__in=threads):
+        thread.sync()
+        thread.save(force_update=True)
+
+delete_user_content.connect(delete_user_content_handler, dispatch_uid="delete_user_posts")
+
+
+def move_forum_content_handler(sender, **kwargs):
+    Post.objects.filter(forum=sender).update(forum=kwargs['move_to'])
+
+move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_posts")
+
+
+def move_thread_handler(sender, **kwargs):
+    Post.objects.filter(thread=sender).update(forum=kwargs['move_to'])
+
+move_thread.connect(move_thread_handler, dispatch_uid="move_thread_posts")
+
+
+def merge_thread_handler(sender, **kwargs):
+    Post.objects.filter(thread=sender).update(thread=kwargs['new_thread'])
+
+merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_posts")
+
+
+def sync_user_handler(sender, **kwargs):
+    sender.posts = sender.post_set.count()
+
+sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_posts")

+ 52 - 52
misago/models/pruningpolicymodel.py

@@ -1,52 +1,52 @@
-from datetime import timedelta
-from django.core.exceptions import ValidationError
-from django.db import models
-from django.db.models import Q
-from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
-
-class PruningPolicy(models.Model):
-    name = models.CharField(max_length=255)
-    email = models.CharField(max_length=255, null=True, blank=True)
-    posts = models.PositiveIntegerField(default=0)
-    registered = models.PositiveIntegerField(default=0)
-    last_visit = models.PositiveIntegerField(default=0)
-
-    class Meta:
-        app_label = 'misago'
-
-    def clean(self):
-        if not (self.email and self.posts and self.registered and self.last_visit):
-            raise ValidationError(_("Pruning policy must have at least one pruning criteria set to be valid."))
-
-    def make_queryset(self):
-        from misago.models import User
-        queryset = User.objects
-
-        if self.email:
-            if ',' in self.email:
-                qs = None
-                for name in self.email.split(','):
-                    name = name.strip().lower()
-                    if name:
-                        if qs:
-                            qs = qs | Q(email__iendswith=name)
-                        else:
-                            qs = Q(email__iendswith=name)
-                if qs:
-                    queryset = queryset.filter(qs)
-            else:
-                queryset = queryset.filter(email__iendswith=self.email)
-
-        if self.posts:
-            queryset = queryset.filter(posts__lt=self.posts)
-
-        if self.registered:
-            date = timezone.now() - timedelta(days=self.registered)
-            queryset = queryset.filter(join_date__gte=date)
-
-        if self.last_visit:
-            date = timezone.now() - timedelta(days=self.last_visit)
-            queryset = queryset.filter(last_date__gte=date)
-
-        return queryset
+from datetime import timedelta
+from django.core.exceptions import ValidationError
+from django.db import models
+from django.db.models import Q
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
+
+class PruningPolicy(models.Model):
+    name = models.CharField(max_length=255)
+    email = models.CharField(max_length=255, null=True, blank=True)
+    posts = models.PositiveIntegerField(default=0)
+    registered = models.PositiveIntegerField(default=0)
+    last_visit = models.PositiveIntegerField(default=0)
+
+    class Meta:
+        app_label = 'misago'
+
+    def clean(self):
+        if not (self.email and self.posts and self.registered and self.last_visit):
+            raise ValidationError(_("Pruning policy must have at least one pruning criteria set to be valid."))
+
+    def make_queryset(self):
+        from misago.models import User
+        queryset = User.objects
+
+        if self.email:
+            if ',' in self.email:
+                qs = None
+                for name in self.email.split(','):
+                    name = name.strip().lower()
+                    if name:
+                        if qs:
+                            qs = qs | Q(email__iendswith=name)
+                        else:
+                            qs = Q(email__iendswith=name)
+                if qs:
+                    queryset = queryset.filter(qs)
+            else:
+                queryset = queryset.filter(email__iendswith=self.email)
+
+        if self.posts:
+            queryset = queryset.filter(posts__lt=self.posts)
+
+        if self.registered:
+            date = timezone.now() - timedelta(days=self.registered)
+            queryset = queryset.filter(join_date__gte=date)
+
+        if self.last_visit:
+            date = timezone.now() - timedelta(days=self.last_visit)
+            queryset = queryset.filter(last_date__gte=date)
+
+        return queryset

+ 97 - 97
misago/models/rankmodel.py

@@ -1,97 +1,97 @@
-import math
-from django.conf import settings
-from django.db import models, connection, transaction
-from django.utils.translation import ugettext_lazy as _
-
-class Rank(models.Model):
-    """
-    Misago User Rank
-    Ranks are ready style/title pairs that are assigned to users either by admin (special ranks) or as result of user activity.
-    """
-    name = models.CharField(max_length=255)
-    slug = models.CharField(max_length=255, null=True, blank=True)
-    description = models.TextField(null=True, blank=True)
-    style = models.CharField(max_length=255, null=True, blank=True)
-    title = models.CharField(max_length=255, null=True, blank=True)
-    special = models.BooleanField(default=False)
-    as_tab = models.BooleanField(default=False)
-    on_index = models.BooleanField(default=False)
-    order = models.IntegerField(default=0)
-    criteria = models.CharField(max_length=255, null=True, blank=True)
-    roles = models.ManyToManyField('Role')
-
-    class Meta:
-        app_label = 'misago'
-
-    def __unicode__(self):
-        return unicode(_(self.name))
-
-    def assign_rank(self, users=0, special_ranks=None):
-        if not self.criteria or self.special or users == 0:
-            # Rank cant be rolled in
-            return False
-
-        if self.criteria == "0":
-            # Just update all fellows
-            User.objects.exclude(rank__in=special_ranks).update(rank=self)
-        else:
-            # Count number of users to update
-            if self.criteria[-1] == '%':
-                criteria = int(self.criteria[0:-1])
-                criteria = int(math.ceil(float(users / 100.0) * criteria))
-            else:
-                criteria = int(self.criteria)
-
-            # Join special ranks
-            if special_ranks:
-                special_ranks = ','.join(special_ranks)
-
-            # Run raw query
-            cursor = connection.cursor()
-            try:
-                # Postgresql
-                if (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql_psycopg2'
-                    or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'):
-                    if special_ranks:
-                        cursor.execute('''UPDATE misago_user
-                            FROM (
-                                SELECT id
-                                FROM misago_user
-                                WHERE rank_id NOT IN (%s)
-                                ORDER BY score DESC LIMIT %s
-                                ) AS updateable
-                            SET rank_id=%s
-                            WHERE id = updateable.id
-                            RETURNING *''' % (self.id, special_ranks, criteria))
-                    else:
-                        cursor.execute('''UPDATE misago_user
-                            FROM (
-                                SELECT id
-                                FROM misago_user
-                                ORDER BY score DESC LIMIT %s
-                                ) AS updateable
-                            SET rank_id=%s
-                            WHERE id = updateable.id
-                            RETURNING *''', [self.id, criteria])
-
-                # MySQL, SQLite and Oracle
-                if (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.mysql'
-                    or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3'
-                    or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.oracle'):
-                    if special_ranks:
-                        cursor.execute('''UPDATE misago_user
-                            SET rank_id=%s
-                            WHERE rank_id NOT IN (%s)
-                            ORDER BY score DESC
-                            LIMIT %s''' % (self.id, special_ranks, criteria))
-                    else:
-                        cursor.execute('''UPDATE misago_user
-                        SET rank_id=%s
-                        ORDER BY score DESC
-                        LIMIT %s''', [self.id, criteria])
-            except Exception as e:
-                print 'Error updating users ranking: %s' % e
-
-            transaction.commit_unless_managed()
-
-        return True
+import math
+from django.conf import settings
+from django.db import models, connection, transaction
+from django.utils.translation import ugettext_lazy as _
+
+class Rank(models.Model):
+    """
+    Misago User Rank
+    Ranks are ready style/title pairs that are assigned to users either by admin (special ranks) or as result of user activity.
+    """
+    name = models.CharField(max_length=255)
+    slug = models.CharField(max_length=255, null=True, blank=True)
+    description = models.TextField(null=True, blank=True)
+    style = models.CharField(max_length=255, null=True, blank=True)
+    title = models.CharField(max_length=255, null=True, blank=True)
+    special = models.BooleanField(default=False)
+    as_tab = models.BooleanField(default=False)
+    on_index = models.BooleanField(default=False)
+    order = models.IntegerField(default=0)
+    criteria = models.CharField(max_length=255, null=True, blank=True)
+    roles = models.ManyToManyField('Role')
+
+    class Meta:
+        app_label = 'misago'
+
+    def __unicode__(self):
+        return unicode(_(self.name))
+
+    def assign_rank(self, users=0, special_ranks=None):
+        if not self.criteria or self.special or users == 0:
+            # Rank cant be rolled in
+            return False
+
+        if self.criteria == "0":
+            # Just update all fellows
+            User.objects.exclude(rank__in=special_ranks).update(rank=self)
+        else:
+            # Count number of users to update
+            if self.criteria[-1] == '%':
+                criteria = int(self.criteria[0:-1])
+                criteria = int(math.ceil(float(users / 100.0) * criteria))
+            else:
+                criteria = int(self.criteria)
+
+            # Join special ranks
+            if special_ranks:
+                special_ranks = ','.join(special_ranks)
+
+            # Run raw query
+            cursor = connection.cursor()
+            try:
+                # Postgresql
+                if (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql_psycopg2'
+                    or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'):
+                    if special_ranks:
+                        cursor.execute('''UPDATE misago_user
+                            FROM (
+                                SELECT id
+                                FROM misago_user
+                                WHERE rank_id NOT IN (%s)
+                                ORDER BY score DESC LIMIT %s
+                                ) AS updateable
+                            SET rank_id=%s
+                            WHERE id = updateable.id
+                            RETURNING *''' % (self.id, special_ranks, criteria))
+                    else:
+                        cursor.execute('''UPDATE misago_user
+                            FROM (
+                                SELECT id
+                                FROM misago_user
+                                ORDER BY score DESC LIMIT %s
+                                ) AS updateable
+                            SET rank_id=%s
+                            WHERE id = updateable.id
+                            RETURNING *''', [self.id, criteria])
+
+                # MySQL, SQLite and Oracle
+                if (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.mysql'
+                    or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3'
+                    or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.oracle'):
+                    if special_ranks:
+                        cursor.execute('''UPDATE misago_user
+                            SET rank_id=%s
+                            WHERE rank_id NOT IN (%s)
+                            ORDER BY score DESC
+                            LIMIT %s''' % (self.id, special_ranks, criteria))
+                    else:
+                        cursor.execute('''UPDATE misago_user
+                        SET rank_id=%s
+                        ORDER BY score DESC
+                        LIMIT %s''', [self.id, criteria])
+            except Exception as e:
+                print 'Error updating users ranking: %s' % e
+
+            transaction.commit_unless_managed()
+
+        return True

+ 45 - 45
misago/models/rolemodel.py

@@ -1,46 +1,46 @@
-from django.db import models
-from django.utils.translation import ugettext as _
-import base64
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-
-class Role(models.Model):
-    """
-    Misago User Role model
-    """
-    name = models.CharField(max_length=255)
-    _special = models.CharField(db_column='special', max_length=255,null=True,blank=True)
-    protected = models.BooleanField(default=False)
-    _permissions = models.TextField(db_column='permissions', null=True, blank=True)
-    permissions_cache = {}
-
-    class Meta:
-        app_label = 'misago'
-    
-    def __unicode__(self):
-        return unicode(_(self.name))
-    
-    @property
-    def special(self):
-        return self._special
-
-    @property
-    def permissions(self):
-        if self.permissions_cache:
-            return self.permissions_cache
-
-        try:
-            self.permissions_cache = pickle.loads(base64.decodestring(self._permissions))
-        except Exception:
-            # ValueError, SuspiciousOperation, unpickling exceptions. If any of
-            # these happen, just return an empty dictionary (an empty permissions list).
-            self.permissions_cache = {}
-
-        return self.permissions_cache
-
-    @permissions.setter
-    def permissions(self, permissions):
-        self.permissions_cache = permissions
+from django.db import models
+from django.utils.translation import ugettext as _
+import base64
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+class Role(models.Model):
+    """
+    Misago User Role model
+    """
+    name = models.CharField(max_length=255)
+    _special = models.CharField(db_column='special', max_length=255,null=True,blank=True)
+    protected = models.BooleanField(default=False)
+    _permissions = models.TextField(db_column='permissions', null=True, blank=True)
+    permissions_cache = {}
+
+    class Meta:
+        app_label = 'misago'
+    
+    def __unicode__(self):
+        return unicode(_(self.name))
+    
+    @property
+    def special(self):
+        return self._special
+
+    @property
+    def permissions(self):
+        if self.permissions_cache:
+            return self.permissions_cache
+
+        try:
+            self.permissions_cache = pickle.loads(base64.decodestring(self._permissions))
+        except Exception:
+            # ValueError, SuspiciousOperation, unpickling exceptions. If any of
+            # these happen, just return an empty dictionary (an empty permissions list).
+            self.permissions_cache = {}
+
+        return self.permissions_cache
+
+    @permissions.setter
+    def permissions(self, permissions):
+        self.permissions_cache = permissions
         self._permissions = base64.encodestring(pickle.dumps(permissions, pickle.HIGHEST_PROTOCOL))
         self._permissions = base64.encodestring(pickle.dumps(permissions, pickle.HIGHEST_PROTOCOL))

+ 17 - 17
misago/models/sessionmodel.py

@@ -1,18 +1,18 @@
-from django.db import models
-
-class Session(models.Model):
-    id = models.CharField(max_length=42, primary_key=True)
-    data = models.TextField(db_column="session_data")
-    user = models.ForeignKey('User', related_name='sessions', null=True, on_delete=models.SET_NULL)
-    crawler = models.CharField(max_length=255, blank=True, null=True)
-    ip = models.GenericIPAddressField()
-    agent = models.CharField(max_length=255)
-    start = models.DateTimeField()
-    last = models.DateTimeField()
-    team = models.BooleanField(default=False)
-    rank = models.ForeignKey('Rank', related_name='sessions', null=True, on_delete=models.SET_NULL)
-    admin = models.BooleanField(default=False)
-    matched = models.BooleanField(default=False)
-
-    class Meta:
+from django.db import models
+
+class Session(models.Model):
+    id = models.CharField(max_length=42, primary_key=True)
+    data = models.TextField(db_column="session_data")
+    user = models.ForeignKey('User', related_name='sessions', null=True, on_delete=models.SET_NULL)
+    crawler = models.CharField(max_length=255, blank=True, null=True)
+    ip = models.GenericIPAddressField()
+    agent = models.CharField(max_length=255)
+    start = models.DateTimeField()
+    last = models.DateTimeField()
+    team = models.BooleanField(default=False)
+    rank = models.ForeignKey('Rank', related_name='sessions', null=True, on_delete=models.SET_NULL)
+    admin = models.BooleanField(default=False)
+    matched = models.BooleanField(default=False)
+
+    class Meta:
         app_label = 'misago'
         app_label = 'misago'

+ 139 - 139
misago/models/settingmodel.py

@@ -1,139 +1,139 @@
-import base64
-from django import forms
-from django.core import validators
-from django.db import models
-from django.utils.translation import ugettext_lazy as _
-from misago.utils.timezones import tzlist
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-
-class Setting(models.Model):
-    setting = models.CharField(max_length=255, primary_key=True)
-    group = models.ForeignKey('SettingsGroup', to_field='key')
-    _value = models.TextField(db_column='value', null=True, blank=True)
-    value_default = models.TextField(null=True, blank=True)
-    normalize_to = models.CharField(max_length=255)
-    field = models.CharField(max_length=255)
-    extra = models.TextField(null=True, blank=True)
-    position = models.IntegerField(default=0)
-    separator = models.CharField(max_length=255, null=True, blank=True)
-    name = models.CharField(max_length=255)
-    description = models.TextField(null=True, blank=True)
-
-    class Meta:
-        app_label = 'misago'
-
-    def get_extra(self):
-        return pickle.loads(base64.decodestring(self.extra))
-
-    @property
-    def value(self):
-        if self.normalize_to == 'array':
-            return self._value.split(',')
-        if self.normalize_to == 'integer':
-            return int(self._value)
-        if self.normalize_to == 'float':
-            return float(self._value)
-        if self.normalize_to == 'boolean':
-            return self._value == "1"
-        return self._value
-
-    @value.setter
-    def value(self, value):
-        if self.normalize_to == 'array':
-            self._value = ','.join(value)
-        elif self.normalize_to == 'integer':
-            self._value = int(value)
-        elif self.normalize_to == 'float':
-            self._value = float(value)
-        elif self.normalize_to == 'boolean':
-            self._value = 1 if value else 0
-        else:
-            self._value = value
-        if not self._value and self.value_default:
-            self._value = self.value_default
-        return self._value
-
-    def get_field(self):
-        from misago.forms import YesNoSwitch
-        
-        extra = self.get_extra()
-
-        # Set validators
-        field_validators = []
-        if 'min' in extra:
-            if self.normalize_to == 'string' or self.normalize_to == 'array':
-                field_validators.append(validators.MinLengthValidator(extra['min']))
-            if self.normalize_to == 'integer' or self.normalize_to == 'float':
-                field_validators.append(validators.MinValueValidator(extra['min']))
-        if 'max' in extra:
-            if self.normalize_to == 'string' or self.normalize_to == 'array':
-                field_validators.append(validators.MaxLengthValidator(extra['max']))
-            if self.normalize_to == 'integer' or self.normalize_to == 'float':
-                field_validators.append(validators.MaxValueValidator(extra['max']))
-
-        # Yes-no
-        if self.field == 'yesno':
-            return forms.BooleanField(
-                                   initial=self.value,
-                                   label=_(self.name),
-                                   help_text=_(self.description) if self.description else None,
-                                   required=False,
-                                   widget=YesNoSwitch,
-                                   )
-
-        # Multi-list
-        if self.field == 'mlist':
-            return forms.MultipleChoiceField(
-                                     initial=self.value,
-                                     label=_(self.name),
-                                     help_text=_(self.description) if self.description else None,
-                                     widget=forms.CheckboxSelectMultiple,
-                                     validators=field_validators,
-                                     required=False,
-                                     choices=extra['choices']
-                                     )
-
-        # Select or choice
-        if self.field == 'select' or self.field == 'choice':
-            # Timezone list?
-            if extra['choices'] == '#TZ#':
-                extra['choices'] = tzlist()
-            return forms.ChoiceField(
-                                     initial=self.value,
-                                     label=_(self.name),
-                                     help_text=_(self.description) if self.description else None,
-                                     widget=forms.RadioSelect if self.field == 'choice' else forms.Select,
-                                     validators=field_validators,
-                                     required=False,
-                                     choices=extra['choices']
-                                     )
-
-        # Textarea
-        if self.field == 'textarea':
-            return forms.CharField(
-                                   initial=self.value,
-                                   label=_(self.name),
-                                   help_text=_(self.description) if self.description else None,
-                                   validators=field_validators,
-                                   required=False,
-                                   widget=forms.Textarea
-                                   )
-
-        # Default input
-        default_input = forms.CharField
-        if self.normalize_to == 'integer':
-            default_input = forms.IntegerField
-        if self.normalize_to == 'float':
-            default_input = forms.FloatField
-
-        # Make text-input
-        return default_input(
-                             initial=self.value,
-                             label=_(self.name),
-                             help_text=_(self.description) if self.description else None,
-                             validators=field_validators,
-                             required=False,
-                             )
+import base64
+from django import forms
+from django.core import validators
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from misago.utils.timezones import tzlist
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+class Setting(models.Model):
+    setting = models.CharField(max_length=255, primary_key=True)
+    group = models.ForeignKey('SettingsGroup', to_field='key')
+    _value = models.TextField(db_column='value', null=True, blank=True)
+    value_default = models.TextField(null=True, blank=True)
+    normalize_to = models.CharField(max_length=255)
+    field = models.CharField(max_length=255)
+    extra = models.TextField(null=True, blank=True)
+    position = models.IntegerField(default=0)
+    separator = models.CharField(max_length=255, null=True, blank=True)
+    name = models.CharField(max_length=255)
+    description = models.TextField(null=True, blank=True)
+
+    class Meta:
+        app_label = 'misago'
+
+    def get_extra(self):
+        return pickle.loads(base64.decodestring(self.extra))
+
+    @property
+    def value(self):
+        if self.normalize_to == 'array':
+            return self._value.split(',')
+        if self.normalize_to == 'integer':
+            return int(self._value)
+        if self.normalize_to == 'float':
+            return float(self._value)
+        if self.normalize_to == 'boolean':
+            return self._value == "1"
+        return self._value
+
+    @value.setter
+    def value(self, value):
+        if self.normalize_to == 'array':
+            self._value = ','.join(value)
+        elif self.normalize_to == 'integer':
+            self._value = int(value)
+        elif self.normalize_to == 'float':
+            self._value = float(value)
+        elif self.normalize_to == 'boolean':
+            self._value = 1 if value else 0
+        else:
+            self._value = value
+        if not self._value and self.value_default:
+            self._value = self.value_default
+        return self._value
+
+    def get_field(self):
+        from misago.forms import YesNoSwitch
+        
+        extra = self.get_extra()
+
+        # Set validators
+        field_validators = []
+        if 'min' in extra:
+            if self.normalize_to == 'string' or self.normalize_to == 'array':
+                field_validators.append(validators.MinLengthValidator(extra['min']))
+            if self.normalize_to == 'integer' or self.normalize_to == 'float':
+                field_validators.append(validators.MinValueValidator(extra['min']))
+        if 'max' in extra:
+            if self.normalize_to == 'string' or self.normalize_to == 'array':
+                field_validators.append(validators.MaxLengthValidator(extra['max']))
+            if self.normalize_to == 'integer' or self.normalize_to == 'float':
+                field_validators.append(validators.MaxValueValidator(extra['max']))
+
+        # Yes-no
+        if self.field == 'yesno':
+            return forms.BooleanField(
+                                   initial=self.value,
+                                   label=_(self.name),
+                                   help_text=_(self.description) if self.description else None,
+                                   required=False,
+                                   widget=YesNoSwitch,
+                                   )
+
+        # Multi-list
+        if self.field == 'mlist':
+            return forms.MultipleChoiceField(
+                                     initial=self.value,
+                                     label=_(self.name),
+                                     help_text=_(self.description) if self.description else None,
+                                     widget=forms.CheckboxSelectMultiple,
+                                     validators=field_validators,
+                                     required=False,
+                                     choices=extra['choices']
+                                     )
+
+        # Select or choice
+        if self.field == 'select' or self.field == 'choice':
+            # Timezone list?
+            if extra['choices'] == '#TZ#':
+                extra['choices'] = tzlist()
+            return forms.ChoiceField(
+                                     initial=self.value,
+                                     label=_(self.name),
+                                     help_text=_(self.description) if self.description else None,
+                                     widget=forms.RadioSelect if self.field == 'choice' else forms.Select,
+                                     validators=field_validators,
+                                     required=False,
+                                     choices=extra['choices']
+                                     )
+
+        # Textarea
+        if self.field == 'textarea':
+            return forms.CharField(
+                                   initial=self.value,
+                                   label=_(self.name),
+                                   help_text=_(self.description) if self.description else None,
+                                   validators=field_validators,
+                                   required=False,
+                                   widget=forms.Textarea
+                                   )
+
+        # Default input
+        default_input = forms.CharField
+        if self.normalize_to == 'integer':
+            default_input = forms.IntegerField
+        if self.normalize_to == 'float':
+            default_input = forms.FloatField
+
+        # Make text-input
+        return default_input(
+                             initial=self.value,
+                             label=_(self.name),
+                             help_text=_(self.description) if self.description else None,
+                             validators=field_validators,
+                             required=False,
+                             )

+ 14 - 14
misago/models/settingsgroupmodel.py

@@ -1,15 +1,15 @@
-from django.db import models
-
-class SettingsGroup(models.Model):
-    key = models.CharField(max_length=255, unique=True)
-    name = models.CharField(max_length=255)
-    description = models.TextField(null=True, blank=True)
-
-    class Meta:
-        app_label = 'misago'
-
-    def is_active(self, active_group):
-        try:
-            return self.pk == active_group.pk
-        except AttributeError:
+from django.db import models
+
+class SettingsGroup(models.Model):
+    key = models.CharField(max_length=255, unique=True)
+    name = models.CharField(max_length=255)
+    description = models.TextField(null=True, blank=True)
+
+    class Meta:
+        app_label = 'misago'
+
+    def is_active(self, active_group):
+        try:
+            return self.pk == active_group.pk
+        except AttributeError:
             return False
             return False

+ 38 - 38
misago/models/signinattemptmodel.py

@@ -1,39 +1,39 @@
-from datetime import timedelta
-from django.db import models
-from django.utils import timezone
-from misago.conf import settings
-
-class SignInAttemptsManager(models.Manager):
-    """
-    IP's that have exhausted their quota of sign-in attempts are automatically banned for set amount of time.
-
-    That IP ban cuts bad IP address from signing into board by either making another sign-in attempts or
-    registering "fresh" account.
-    """
-    def register_attempt(self, ip):
-        attempt = SignInAttempt(ip=ip, date=timezone.now())
-        attempt.save(force_insert=True)
-
-    def is_jammed(self, ip):
-        # Limit is off, dont jam IPs?
-        if settings.attempts_limit == 0:
-            return False
-        # Check jam
-        if settings.jams_lifetime > 0:
-            attempts = SignInAttempt.objects.filter(
-                                                    date__gt=timezone.now() - timedelta(minutes=settings.jams_lifetime),
-                                                    ip=ip
-                                                    )
-        else:
-            attempts = SignInAttempt.objects.filter(ip=ip)
-        return attempts.count() > settings.attempts_limit
-
-
-class SignInAttempt(models.Model):
-    ip = models.GenericIPAddressField()
-    date = models.DateTimeField()
-
-    objects = SignInAttemptsManager()
-
-    class Meta:
+from datetime import timedelta
+from django.db import models
+from django.utils import timezone
+from misago.conf import settings
+
+class SignInAttemptsManager(models.Manager):
+    """
+    IP's that have exhausted their quota of sign-in attempts are automatically banned for set amount of time.
+
+    That IP ban cuts bad IP address from signing into board by either making another sign-in attempts or
+    registering "fresh" account.
+    """
+    def register_attempt(self, ip):
+        attempt = SignInAttempt(ip=ip, date=timezone.now())
+        attempt.save(force_insert=True)
+
+    def is_jammed(self, ip):
+        # Limit is off, dont jam IPs?
+        if settings.attempts_limit == 0:
+            return False
+        # Check jam
+        if settings.jams_lifetime > 0:
+            attempts = SignInAttempt.objects.filter(
+                                                    date__gt=timezone.now() - timedelta(minutes=settings.jams_lifetime),
+                                                    ip=ip
+                                                    )
+        else:
+            attempts = SignInAttempt.objects.filter(ip=ip)
+        return attempts.count() > settings.attempts_limit
+
+
+class SignInAttempt(models.Model):
+    ip = models.GenericIPAddressField()
+    date = models.DateTimeField()
+
+    objects = SignInAttemptsManager()
+
+    class Meta:
         app_label = 'misago'
         app_label = 'misago'

+ 303 - 303
misago/models/threadmodel.py

@@ -1,303 +1,303 @@
-from datetime import timedelta
-from django.conf import settings
-from django.db import models
-from django.db.models.signals import pre_save, pre_delete
-from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
-from misago.signals import (delete_user_content, merge_thread, move_forum_content,
-                            move_thread, rename_user, sync_user_profile)
-from misago.utils.strings import slugify
-
-class ThreadManager(models.Manager):
-    def filter_stats(self, start, end):
-        return self.filter(start__gte=start).filter(start__lte=end)
-
-    def with_reads(self, queryset, user):
-        from misago.models import ForumRead, ThreadRead
-
-        threads = []
-        threads_dict = {}
-        forum_reads = {}
-        cutoff = timezone.now() - timedelta(days=settings.READS_TRACKER_LENGTH)
-
-        if user.is_authenticated() and user.join_date > cutoff:
-            cutoff = user.join_date
-            for row in ForumRead.objects.filter(user=user).values('forum_id', 'cleared'):
-                forum_reads[row['forum_id']] = row['cleared']
-
-        for thread in queryset:
-            thread.is_read = True
-            if user.is_authenticated() and thread.last > cutoff:
-                try:
-                    thread.is_read = thread.last <= forum_reads[thread.forum_id]
-                except KeyError:
-                    pass
-
-            threads.append(thread)
-            threads_dict[thread.pk] = thread
-
-        if user.is_authenticated():
-            for read in ThreadRead.objects.filter(user=user).filter(thread__in=threads_dict.keys()):
-                try:
-                    threads_dict[read.thread_id].is_read = (threads_dict[read.thread_id].last <= cutoff or 
-                                                            threads_dict[read.thread_id].last <= read.updated or
-                                                            threads_dict[read.thread_id].last <= forum_reads[read.forum_id])
-                except KeyError:
-                    pass
-
-        return threads
-
-
-class Thread(models.Model):
-    forum = models.ForeignKey('Forum')
-    weight = models.PositiveIntegerField(default=0)
-    name = models.CharField(max_length=255)
-    slug = models.SlugField(max_length=255)
-    replies = models.PositiveIntegerField(default=0)
-    replies_reported = models.PositiveIntegerField(default=0)
-    replies_moderated = models.PositiveIntegerField(default=0)
-    replies_deleted = models.PositiveIntegerField(default=0)
-    score = models.PositiveIntegerField(default=30)
-    upvotes = models.PositiveIntegerField(default=0)
-    downvotes = models.PositiveIntegerField(default=0)
-    start = models.DateTimeField()
-    start_post = models.ForeignKey('Post', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
-    start_poster = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
-    start_poster_name = models.CharField(max_length=255)
-    start_poster_slug = models.SlugField(max_length=255)
-    start_poster_style = models.CharField(max_length=255, null=True, blank=True)
-    last = models.DateTimeField()
-    last_post = models.ForeignKey('Post', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
-    last_poster = models.ForeignKey('User', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
-    last_poster_name = models.CharField(max_length=255, null=True, blank=True)
-    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
-    last_poster_style = models.CharField(max_length=255, null=True, blank=True)
-    participants = models.ManyToManyField('User', related_name='private_thread_set')
-    report_for = models.ForeignKey('Post', related_name='report_set', null=True, blank=True, on_delete=models.SET_NULL)
-    moderated = models.BooleanField(default=False)
-    deleted = models.BooleanField(default=False)
-    closed = models.BooleanField(default=False)
-
-    objects = ThreadManager()
-
-    statistics_name = _('New Threads')
-
-    class Meta:
-        app_label = 'misago'
-
-    @property
-    def timeline_date(self):
-        return self.start
-
-    def delete(self, *args, **kwargs):
-        """
-        FUGLY HAX for weird stuff that happens with
-        relations on model deletion in MySQL
-        """
-        if self.replies_reported:
-            clear_reports = [post.pk for post in self.post_set.filter(reported=True)]
-            if clear_reports:
-                Thread.objects.filter(report_for__in=clear_reports).update(report_for=None)
-        return super(Thread, self).delete(*args, **kwargs)
-
-    def get_date(self):
-        return self.start
-
-    def add_checkpoints_to_posts(self, show_all, posts, start=None, stop=None):
-        qs = self.checkpoint_set.all()
-        if start:
-            qs = qs.filter(date__gte=start)
-        if stop:
-            qs = qs.filter(date__lte=stop)
-        if not show_all:
-            qs = qs.filter(deleted=False)
-        checkpoints = [i for i in qs]
-
-        i_max = len(posts) - 1
-        for i, post in enumerate(posts):
-            post.checkpoints_visible = []
-            for c in checkpoints:
-                if ((i == 0 and c.date <= post.date)
-                        or (c.date >= post.date and (i == i_max or c.date < posts[i+1].date))):
-                    post.checkpoints_visible.append(c)
-
-    def set_checkpoint(self, request, action, user=None, forum=None):
-        if request.user.is_authenticated():
-            self.checkpoint_set.create(
-                                       forum=self.forum,
-                                       thread=self,
-                                       action=action,
-                                       user=request.user,
-                                       user_name=request.user.username,
-                                       user_slug=request.user.username_slug,
-                                       date=timezone.now(),
-                                       ip=request.session.get_ip(request),
-                                       agent=request.META.get('HTTP_USER_AGENT'),
-                                       target_user=user,
-                                       target_user_name=(user.username if user else None),
-                                       target_user_slug=(user.username_slug if user else None),
-                                       old_forum=forum,
-                                       old_forum_name=(forum.name if forum else None),
-                                       old_forum_slug=(forum.slug if forum else None),
-                                       )
-
-    def new_start_post(self, post):
-        self.start = post.date
-        self.start_post = post
-        self.start_poster = post.user
-        self.start_poster_name = post.user.username
-        self.start_poster_slug = post.user.username_slug
-        if post.user.rank_id and post.user.rank.style:
-            self.start_poster_style = post.user.rank.style
-
-    def new_last_post(self, post):
-        self.last = post.date
-        self.last_post = post
-        self.last_poster = post.user
-        self.last_poster_name = post.user.username
-        self.last_poster_slug = post.user.username_slug
-        if post.user.rank_id and post.user.rank.style:
-            self.last_poster_style = post.user.rank.style
-
-    def move_to(self, move_to):
-        move_thread.send(sender=self, move_to=move_to)
-        self.forum = move_to
-
-    def merge_with(self, thread):
-        merge_thread.send(sender=self, new_thread=thread)
-
-    def update_current_dates(self):
-        self.post_set.update(current_date=timezone.now())
-
-    def sync(self):
-        # Counters
-        self.replies = self.post_set.filter(moderated=False).count() - 1
-        if self.replies < 0:
-            self.replies = 0
-        self.replies_reported = self.post_set.filter(reported=True).count()
-        self.replies_moderated = self.post_set.filter(moderated=True).count()
-        self.replies_deleted = self.post_set.filter(deleted=True).count()
-        # First post
-        start_post = self.post_set.order_by('id')[0:][0]
-        self.start = start_post.date
-        self.start_post = start_post
-        self.start_poster = start_post.user
-        self.start_poster_name = start_post.user_name
-        self.start_poster_slug = slugify(start_post.user_name)
-        self.start_poster_style = start_post.user.rank.style if start_post.user and start_post.user.rank else ''
-        self.upvotes = start_post.upvotes
-        self.downvotes = start_post.downvotes
-        # Last visible post
-        if self.replies > 0:
-            last_post = self.post_set.order_by('-id').filter(moderated=False)[0:][0]
-        else:
-            last_post = start_post
-        self.last = last_post.date
-        self.last_post = last_post
-        self.last_poster = last_post.user
-        self.last_poster_name = last_post.user_name
-        self.last_poster_slug = slugify(last_post.user_name)
-        self.last_poster_style = last_post.user.rank.style if last_post.user and last_post.user.rank else ''
-        # Flags
-        self.moderated = start_post.moderated
-        self.deleted = start_post.deleted
-        
-    def email_watchers(self, request, thread_type, post):
-        from misago.acl.builder import acl
-        from misago.acl.exceptions import ACLError403, ACLError404
-        from misago.models import ThreadRead, WatchedThread
-
-        notified = []
-        for watch in WatchedThread.objects.filter(thread=self).filter(last_read__gte=self.previous_last.date):
-            user = watch.user
-            if user.pk != request.user.pk:
-                try:
-                    user_acl = acl(request, user)
-                    user_acl.forums.allow_forum_view(self.forum)
-                    user_acl.threads.allow_thread_view(user, self)
-                    user_acl.threads.allow_post_view(user, self, post)
-                    if not user.is_ignoring(request.user):
-                        if watch.email:
-                            user.email_user(
-                                            request,
-                                            '%s_reply_notification' % thread_type,
-                                            _('New reply in thread "%(thread)s"') % {'thread': self.name},
-                                            {'author': request.user, 'post': post, 'thread': self}
-                                            )
-                        notified.append(user)
-                except (ACLError403, ACLError404):
-                    pass
-        return notified
-
-
-def rename_user_handler(sender, **kwargs):
-    Thread.objects.filter(start_poster=sender).update(
-                                                     start_poster_name=sender.username,
-                                                     start_poster_slug=sender.username_slug,
-                                                     )
-    Thread.objects.filter(last_poster=sender).update(
-                                                     last_poster_name=sender.username,
-                                                     last_poster_slug=sender.username_slug,
-                                                     )
-
-rename_user.connect(rename_user_handler, dispatch_uid="rename_user_threads")
-
-
-def report_update_handler(sender, **kwargs):
-    if sender == Thread:
-        thread = kwargs.get('instance')
-        if thread.weight < 2 and thread.report_for_id:
-            reported_post = thread.report_for
-            if reported_post.reported:
-                reported_post.reported = False
-                reported_post.reports = None
-                reported_post.save(force_update=True)
-                reported_post.thread.replies_reported -= 1
-                reported_post.thread.save(force_update=True)
-
-pre_save.connect(report_update_handler, dispatch_uid="sync_post_reports_on_update")
-
-
-def report_delete_handler(sender, **kwargs):
-    if sender == Thread:
-        thread = kwargs.get('instance')
-        if thread.report_for_id:
-            reported_post = thread.report_for
-            if reported_post.reported:
-                reported_post.reported = False
-                reported_post.reports = None
-                reported_post.save(force_update=True)
-                reported_post.thread.replies_reported -= 1
-                reported_post.thread.save(force_update=True)
-
-pre_delete.connect(report_delete_handler, dispatch_uid="sync_post_reports_on_delete")
-
-
-def delete_user_content_handler(sender, **kwargs):
-    for thread in Thread.objects.filter(start_poster=sender):
-        thread.delete()
-
-delete_user_content.connect(delete_user_content_handler, dispatch_uid="delete_user_threads")
-
-
-def move_forum_content_handler(sender, **kwargs):
-    Thread.objects.filter(forum=sender).update(forum=kwargs['move_to'])
-
-move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_threads")
-
-
-def delete_user_handler(sender, instance, using, **kwargs):
-    from misago.models import User
-    if sender == User:
-        for thread in instance.private_thread_set.all():
-            thread.participants.remove(instance)
-            if not thread.participants.count():
-                thread.delete()
-
-pre_delete.connect(delete_user_handler, dispatch_uid="delete_user_participations")
-
-
-def sync_user_handler(sender, **kwargs):
-    sender.threads = sender.thread_set.count()
-
-sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_threads")
+from datetime import timedelta
+from django.conf import settings
+from django.db import models
+from django.db.models.signals import pre_save, pre_delete
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
+from misago.signals import (delete_user_content, merge_thread, move_forum_content,
+                            move_thread, rename_user, sync_user_profile)
+from misago.utils.strings import slugify
+
+class ThreadManager(models.Manager):
+    def filter_stats(self, start, end):
+        return self.filter(start__gte=start).filter(start__lte=end)
+
+    def with_reads(self, queryset, user):
+        from misago.models import ForumRead, ThreadRead
+
+        threads = []
+        threads_dict = {}
+        forum_reads = {}
+        cutoff = timezone.now() - timedelta(days=settings.READS_TRACKER_LENGTH)
+
+        if user.is_authenticated() and user.join_date > cutoff:
+            cutoff = user.join_date
+            for row in ForumRead.objects.filter(user=user).values('forum_id', 'cleared'):
+                forum_reads[row['forum_id']] = row['cleared']
+
+        for thread in queryset:
+            thread.is_read = True
+            if user.is_authenticated() and thread.last > cutoff:
+                try:
+                    thread.is_read = thread.last <= forum_reads[thread.forum_id]
+                except KeyError:
+                    pass
+
+            threads.append(thread)
+            threads_dict[thread.pk] = thread
+
+        if user.is_authenticated():
+            for read in ThreadRead.objects.filter(user=user).filter(thread__in=threads_dict.keys()):
+                try:
+                    threads_dict[read.thread_id].is_read = (threads_dict[read.thread_id].last <= cutoff or 
+                                                            threads_dict[read.thread_id].last <= read.updated or
+                                                            threads_dict[read.thread_id].last <= forum_reads[read.forum_id])
+                except KeyError:
+                    pass
+
+        return threads
+
+
+class Thread(models.Model):
+    forum = models.ForeignKey('Forum')
+    weight = models.PositiveIntegerField(default=0)
+    name = models.CharField(max_length=255)
+    slug = models.SlugField(max_length=255)
+    replies = models.PositiveIntegerField(default=0)
+    replies_reported = models.PositiveIntegerField(default=0)
+    replies_moderated = models.PositiveIntegerField(default=0)
+    replies_deleted = models.PositiveIntegerField(default=0)
+    score = models.PositiveIntegerField(default=30)
+    upvotes = models.PositiveIntegerField(default=0)
+    downvotes = models.PositiveIntegerField(default=0)
+    start = models.DateTimeField()
+    start_post = models.ForeignKey('Post', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
+    start_poster = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
+    start_poster_name = models.CharField(max_length=255)
+    start_poster_slug = models.SlugField(max_length=255)
+    start_poster_style = models.CharField(max_length=255, null=True, blank=True)
+    last = models.DateTimeField()
+    last_post = models.ForeignKey('Post', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
+    last_poster = models.ForeignKey('User', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
+    last_poster_name = models.CharField(max_length=255, null=True, blank=True)
+    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
+    last_poster_style = models.CharField(max_length=255, null=True, blank=True)
+    participants = models.ManyToManyField('User', related_name='private_thread_set')
+    report_for = models.ForeignKey('Post', related_name='report_set', null=True, blank=True, on_delete=models.SET_NULL)
+    moderated = models.BooleanField(default=False)
+    deleted = models.BooleanField(default=False)
+    closed = models.BooleanField(default=False)
+
+    objects = ThreadManager()
+
+    statistics_name = _('New Threads')
+
+    class Meta:
+        app_label = 'misago'
+
+    @property
+    def timeline_date(self):
+        return self.start
+
+    def delete(self, *args, **kwargs):
+        """
+        FUGLY HAX for weird stuff that happens with
+        relations on model deletion in MySQL
+        """
+        if self.replies_reported:
+            clear_reports = [post.pk for post in self.post_set.filter(reported=True)]
+            if clear_reports:
+                Thread.objects.filter(report_for__in=clear_reports).update(report_for=None)
+        return super(Thread, self).delete(*args, **kwargs)
+
+    def get_date(self):
+        return self.start
+
+    def add_checkpoints_to_posts(self, show_all, posts, start=None, stop=None):
+        qs = self.checkpoint_set.all()
+        if start:
+            qs = qs.filter(date__gte=start)
+        if stop:
+            qs = qs.filter(date__lte=stop)
+        if not show_all:
+            qs = qs.filter(deleted=False)
+        checkpoints = [i for i in qs]
+
+        i_max = len(posts) - 1
+        for i, post in enumerate(posts):
+            post.checkpoints_visible = []
+            for c in checkpoints:
+                if ((i == 0 and c.date <= post.date)
+                        or (c.date >= post.date and (i == i_max or c.date < posts[i+1].date))):
+                    post.checkpoints_visible.append(c)
+
+    def set_checkpoint(self, request, action, user=None, forum=None):
+        if request.user.is_authenticated():
+            self.checkpoint_set.create(
+                                       forum=self.forum,
+                                       thread=self,
+                                       action=action,
+                                       user=request.user,
+                                       user_name=request.user.username,
+                                       user_slug=request.user.username_slug,
+                                       date=timezone.now(),
+                                       ip=request.session.get_ip(request),
+                                       agent=request.META.get('HTTP_USER_AGENT'),
+                                       target_user=user,
+                                       target_user_name=(user.username if user else None),
+                                       target_user_slug=(user.username_slug if user else None),
+                                       old_forum=forum,
+                                       old_forum_name=(forum.name if forum else None),
+                                       old_forum_slug=(forum.slug if forum else None),
+                                       )
+
+    def new_start_post(self, post):
+        self.start = post.date
+        self.start_post = post
+        self.start_poster = post.user
+        self.start_poster_name = post.user.username
+        self.start_poster_slug = post.user.username_slug
+        if post.user.rank_id and post.user.rank.style:
+            self.start_poster_style = post.user.rank.style
+
+    def new_last_post(self, post):
+        self.last = post.date
+        self.last_post = post
+        self.last_poster = post.user
+        self.last_poster_name = post.user.username
+        self.last_poster_slug = post.user.username_slug
+        if post.user.rank_id and post.user.rank.style:
+            self.last_poster_style = post.user.rank.style
+
+    def move_to(self, move_to):
+        move_thread.send(sender=self, move_to=move_to)
+        self.forum = move_to
+
+    def merge_with(self, thread):
+        merge_thread.send(sender=self, new_thread=thread)
+
+    def update_current_dates(self):
+        self.post_set.update(current_date=timezone.now())
+
+    def sync(self):
+        # Counters
+        self.replies = self.post_set.filter(moderated=False).count() - 1
+        if self.replies < 0:
+            self.replies = 0
+        self.replies_reported = self.post_set.filter(reported=True).count()
+        self.replies_moderated = self.post_set.filter(moderated=True).count()
+        self.replies_deleted = self.post_set.filter(deleted=True).count()
+        # First post
+        start_post = self.post_set.order_by('id')[0:][0]
+        self.start = start_post.date
+        self.start_post = start_post
+        self.start_poster = start_post.user
+        self.start_poster_name = start_post.user_name
+        self.start_poster_slug = slugify(start_post.user_name)
+        self.start_poster_style = start_post.user.rank.style if start_post.user and start_post.user.rank else ''
+        self.upvotes = start_post.upvotes
+        self.downvotes = start_post.downvotes
+        # Last visible post
+        if self.replies > 0:
+            last_post = self.post_set.order_by('-id').filter(moderated=False)[0:][0]
+        else:
+            last_post = start_post
+        self.last = last_post.date
+        self.last_post = last_post
+        self.last_poster = last_post.user
+        self.last_poster_name = last_post.user_name
+        self.last_poster_slug = slugify(last_post.user_name)
+        self.last_poster_style = last_post.user.rank.style if last_post.user and last_post.user.rank else ''
+        # Flags
+        self.moderated = start_post.moderated
+        self.deleted = start_post.deleted
+        
+    def email_watchers(self, request, thread_type, post):
+        from misago.acl.builder import acl
+        from misago.acl.exceptions import ACLError403, ACLError404
+        from misago.models import ThreadRead, WatchedThread
+
+        notified = []
+        for watch in WatchedThread.objects.filter(thread=self).filter(last_read__gte=self.previous_last.date):
+            user = watch.user
+            if user.pk != request.user.pk:
+                try:
+                    user_acl = acl(request, user)
+                    user_acl.forums.allow_forum_view(self.forum)
+                    user_acl.threads.allow_thread_view(user, self)
+                    user_acl.threads.allow_post_view(user, self, post)
+                    if not user.is_ignoring(request.user):
+                        if watch.email:
+                            user.email_user(
+                                            request,
+                                            '%s_reply_notification' % thread_type,
+                                            _('New reply in thread "%(thread)s"') % {'thread': self.name},
+                                            {'author': request.user, 'post': post, 'thread': self}
+                                            )
+                        notified.append(user)
+                except (ACLError403, ACLError404):
+                    pass
+        return notified
+
+
+def rename_user_handler(sender, **kwargs):
+    Thread.objects.filter(start_poster=sender).update(
+                                                     start_poster_name=sender.username,
+                                                     start_poster_slug=sender.username_slug,
+                                                     )
+    Thread.objects.filter(last_poster=sender).update(
+                                                     last_poster_name=sender.username,
+                                                     last_poster_slug=sender.username_slug,
+                                                     )
+
+rename_user.connect(rename_user_handler, dispatch_uid="rename_user_threads")
+
+
+def report_update_handler(sender, **kwargs):
+    if sender == Thread:
+        thread = kwargs.get('instance')
+        if thread.weight < 2 and thread.report_for_id:
+            reported_post = thread.report_for
+            if reported_post.reported:
+                reported_post.reported = False
+                reported_post.reports = None
+                reported_post.save(force_update=True)
+                reported_post.thread.replies_reported -= 1
+                reported_post.thread.save(force_update=True)
+
+pre_save.connect(report_update_handler, dispatch_uid="sync_post_reports_on_update")
+
+
+def report_delete_handler(sender, **kwargs):
+    if sender == Thread:
+        thread = kwargs.get('instance')
+        if thread.report_for_id:
+            reported_post = thread.report_for
+            if reported_post.reported:
+                reported_post.reported = False
+                reported_post.reports = None
+                reported_post.save(force_update=True)
+                reported_post.thread.replies_reported -= 1
+                reported_post.thread.save(force_update=True)
+
+pre_delete.connect(report_delete_handler, dispatch_uid="sync_post_reports_on_delete")
+
+
+def delete_user_content_handler(sender, **kwargs):
+    for thread in Thread.objects.filter(start_poster=sender):
+        thread.delete()
+
+delete_user_content.connect(delete_user_content_handler, dispatch_uid="delete_user_threads")
+
+
+def move_forum_content_handler(sender, **kwargs):
+    Thread.objects.filter(forum=sender).update(forum=kwargs['move_to'])
+
+move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_threads")
+
+
+def delete_user_handler(sender, instance, using, **kwargs):
+    from misago.models import User
+    if sender == User:
+        for thread in instance.private_thread_set.all():
+            thread.participants.remove(instance)
+            if not thread.participants.count():
+                thread.delete()
+
+pre_delete.connect(delete_user_handler, dispatch_uid="delete_user_participations")
+
+
+def sync_user_handler(sender, **kwargs):
+    sender.threads = sender.thread_set.count()
+
+sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_threads")

+ 22 - 22
misago/models/threadreadmodel.py

@@ -1,23 +1,23 @@
-from django.db import models
-from misago.signals import move_forum_content, move_thread
-
-class ThreadRead(models.Model):
-    user = models.ForeignKey('User')
-    forum = models.ForeignKey('Forum')
-    thread = models.ForeignKey('Thread')
-    updated = models.DateTimeField()
-
-    class Meta:
-        app_label = 'misago'
-
-
-def move_forum_content_handler(sender, **kwargs):
-    ThreadRead.objects.filter(forum=sender).update(forum=kwargs['move_to'])
-
-move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_threads_reads")
-
-
-def move_thread_handler(sender, **kwargs):
-    ThreadRead.objects.filter(thread=sender).delete()
-
+from django.db import models
+from misago.signals import move_forum_content, move_thread
+
+class ThreadRead(models.Model):
+    user = models.ForeignKey('User')
+    forum = models.ForeignKey('Forum')
+    thread = models.ForeignKey('Thread')
+    updated = models.DateTimeField()
+
+    class Meta:
+        app_label = 'misago'
+
+
+def move_forum_content_handler(sender, **kwargs):
+    ThreadRead.objects.filter(forum=sender).update(forum=kwargs['move_to'])
+
+move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_threads_reads")
+
+
+def move_thread_handler(sender, **kwargs):
+    ThreadRead.objects.filter(thread=sender).delete()
+
 move_thread.connect(move_thread_handler, dispatch_uid="move_thread_reads")
 move_thread.connect(move_thread_handler, dispatch_uid="move_thread_reads")

+ 9 - 9
misago/models/tokenmodel.py

@@ -1,10 +1,10 @@
-from django.db import models
-
-class Token(models.Model):
-    id = models.CharField(max_length=42, primary_key=True)
-    user = models.ForeignKey('User', related_name='signin_tokens')
-    created = models.DateTimeField()
-    accessed = models.DateTimeField()
-
-    class Meta:
+from django.db import models
+
+class Token(models.Model):
+    id = models.CharField(max_length=42, primary_key=True)
+    user = models.ForeignKey('User', related_name='signin_tokens')
+    created = models.DateTimeField()
+    accessed = models.DateTimeField()
+
+    class Meta:
         app_label = 'misago'
         app_label = 'misago'

+ 581 - 581
misago/models/usermodel.py

@@ -1,582 +1,582 @@
-import hashlib
-from datetime import timedelta
-import math
-from random import choice
-from path import path
-from django.contrib.auth.hashers import (
-    check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
-from django.core.cache import cache, InvalidCacheBackendError
-from django.core.exceptions import ValidationError
-from django.core.mail import EmailMultiAlternatives
-from django.db import models
-from django.template import RequestContext
-from django.utils import timezone as tz_util
-from django.utils.translation import ugettext_lazy as _
-from misago.acl.builder import acl
-from misago.conf import settings
-from misago.monitor import monitor, UpdatingMonitor
-from misago.signals import delete_user_content, rename_user, sync_user_profile
-from misago.template.loader import render_to_string
-from misago.utils.avatars import avatar_size
-from misago.utils.strings import random_string, slugify
-from misago.validators import validate_username, validate_password, validate_email
-
-class UserManager(models.Manager):
-    """
-    User Manager provides us with some additional methods for users
-    """
-    def get_blank_user(self):
-        blank_user = User(
-                        join_date=tz_util.now(),
-                        join_ip='127.0.0.1'
-                        )
-        return blank_user
-
-    def resync_monitor(self):
-        with UpdatingMonitor() as cm:
-            monitor['users'] = self.filter(activation=0).count()
-            monitor['users_inactive'] = self.filter(activation__gt=0).count()
-            last_user = self.filter(activation=0).latest('id')
-            monitor['last_user'] = last_user.pk
-            monitor['last_user_name'] = last_user.username
-            monitor['last_user_slug'] = last_user.username_slug
-
-    def create_user(self, username, email, password, timezone=False, ip='127.0.0.1', agent='', no_roles=False, activation=0, request=False):
-        token = ''
-        if activation > 0:
-            token = random_string(12)
-
-        timezone = timezone or settings.default_timezone
-
-        # Get first rank
-        try:
-            from misago.models import Rank
-            default_rank = Rank.objects.filter(special=0).order_by('-order')[0]
-        except IndexError:
-            default_rank = None
-
-        # Store user in database
-        new_user = User(
-                        last_sync=tz_util.now(),
-                        join_date=tz_util.now(),
-                        join_ip=ip,
-                        join_agent=agent,
-                        activation=activation,
-                        token=token,
-                        timezone=timezone,
-                        rank=default_rank,
-                        subscribe_start=settings.subscribe_start,
-                        subscribe_reply=settings.subscribe_reply,
-                        )
-
-        validate_username(username)
-        validate_password(password)
-        new_user.set_username(username)
-        new_user.set_email(email)
-        new_user.set_password(password)
-        new_user.full_clean()
-        new_user.default_avatar()
-        new_user.save(force_insert=True)
-
-        # Set user roles?
-        if not no_roles:
-            from misago.models import Role
-            new_user.roles.add(Role.objects.get(_special='registered'))
-            new_user.make_acl_key()
-            new_user.save(force_update=True)
-
-        # Update forum stats
-        with UpdatingMonitor() as cm:
-            if activation == 0:
-                monitor.increase('users')
-                monitor['last_user'] = new_user.pk
-                monitor['last_user_name'] = new_user.username
-                monitor['last_user_slug'] = new_user.username_slug
-            else:
-                monitor.increase('users_inactive')
-
-        # Return new user
-        return new_user
-
-    def get_by_email(self, email):
-        return self.get(email_hash=hashlib.md5(email).hexdigest())
-
-    def filter_stats(self, start, end):
-        return self.filter(join_date__gte=start).filter(join_date__lte=end)
-
-
-class User(models.Model):
-    """
-    Misago User model
-    """
-    username = models.CharField(max_length=255)
-    username_slug = models.SlugField(max_length=255, unique=True,
-                                     error_messages={'unique': _("This user name is already in use by another user.")})
-    email = models.EmailField(max_length=255, validators=[validate_email])
-    email_hash = models.CharField(max_length=32, unique=True,
-                                     error_messages={'unique': _("This email address is already in use by another user.")})
-    password = models.CharField(max_length=255)
-    password_date = models.DateTimeField()
-    avatar_type = models.CharField(max_length=10, null=True, blank=True)
-    avatar_image = models.CharField(max_length=255, null=True, blank=True)
-    avatar_original = models.CharField(max_length=255, null=True, blank=True)
-    avatar_temp = models.CharField(max_length=255, null=True, blank=True)
-    signature = models.TextField(null=True, blank=True)
-    signature_preparsed = models.TextField(null=True, blank=True)
-    join_date = models.DateTimeField()
-    join_ip = models.GenericIPAddressField()
-    join_agent = models.TextField(null=True, blank=True)
-    last_date = models.DateTimeField(null=True, blank=True)
-    last_ip = models.GenericIPAddressField(null=True, blank=True)
-    last_agent = models.TextField(null=True, blank=True)
-    hide_activity = models.PositiveIntegerField(default=0)
-    subscribe_start = models.PositiveIntegerField(default=0)
-    subscribe_reply = models.PositiveIntegerField(default=0)
-    receive_newsletters = models.BooleanField(default=True)
-    threads = models.PositiveIntegerField(default=0)
-    posts = models.PositiveIntegerField(default=0)
-    votes = models.PositiveIntegerField(default=0)
-    karma_given_p = models.PositiveIntegerField(default=0)
-    karma_given_n = models.PositiveIntegerField(default=0)
-    karma_p = models.PositiveIntegerField(default=0)
-    karma_n = models.PositiveIntegerField(default=0)
-    following = models.PositiveIntegerField(default=0)
-    followers = models.PositiveIntegerField(default=0)
-    score = models.IntegerField(default=0)
-    ranking = models.PositiveIntegerField(default=0)
-    rank = models.ForeignKey('Rank', null=True, blank=True, on_delete=models.SET_NULL)
-    last_sync = models.DateTimeField(null=True, blank=True)
-    follows = models.ManyToManyField('self', related_name='follows_set', symmetrical=False)
-    ignores = models.ManyToManyField('self', related_name='ignores_set', symmetrical=False)
-    title = models.CharField(max_length=255, null=True, blank=True)
-    last_post = models.DateTimeField(null=True, blank=True)
-    last_search = models.DateTimeField(null=True, blank=True)
-    alerts = models.PositiveIntegerField(default=0)
-    alerts_date = models.DateTimeField(null=True, blank=True)
-    allow_pds = models.PositiveIntegerField(default=0)
-    unread_pds = models.PositiveIntegerField(default=0)
-    sync_pds = models.BooleanField(default=False)
-    activation = models.IntegerField(default=0)
-    token = models.CharField(max_length=12, null=True, blank=True)
-    avatar_ban = models.BooleanField(default=False)
-    avatar_ban_reason_user = models.TextField(null=True, blank=True)
-    avatar_ban_reason_admin = models.TextField(null=True, blank=True)
-    signature_ban = models.BooleanField(default=False)
-    signature_ban_reason_user = models.TextField(null=True, blank=True)
-    signature_ban_reason_admin = models.TextField(null=True, blank=True)
-    timezone = models.CharField(max_length=255, default='utc')
-    roles = models.ManyToManyField('Role')
-    is_team = models.BooleanField(default=False)
-    acl_key = models.CharField(max_length=12, null=True, blank=True)
-
-    objects = UserManager()
-
-    ACTIVATION_NONE = 0
-    ACTIVATION_USER = 1
-    ACTIVATION_ADMIN = 2
-    ACTIVATION_CREDENTIALS = 3
-
-    statistics_name = _('Users Registrations')
-
-    class Meta:
-        app_label = 'misago'
-
-    def is_god(self):
-        try:
-            return self.is_god_cache
-        except AttributeError:
-            for user in settings.ADMINS:
-                if user[1].lower() == self.email:
-                    self.is_god_cache = True
-                    return True
-            self.is_god_cache = False
-            return False
-
-    def is_anonymous(self):
-        return False
-
-    def is_authenticated(self):
-        return True
-
-    def is_crawler(self):
-        return False
-
-    def is_protected(self):
-        for role in self.roles.all():
-            if role.protected:
-                return True
-        return False
-
-    def lock_avatar(self):
-        # Kill existing avatar and lock our ability to change it
-        self.delete_avatar()
-        self.avatar_ban = True
-
-        # Pick new one from _locked gallery
-        galleries = path(settings.STATICFILES_DIRS[0]).joinpath('avatars').joinpath('_locked')
-        avatars_list = galleries.files('*.gif')
-        avatars_list += galleries.files('*.jpg')
-        avatars_list += galleries.files('*.jpeg')
-        avatars_list += galleries.files('*.png')
-        self.avatar_type = 'gallery'
-        self.avatar_image = '/'.join(path(choice(avatars_list)).splitall()[-2:])
-
-    def default_avatar(self):
-        if settings.default_avatar == 'gallery':
-            try:
-                avatars_list = []
-                try:
-                    # First try, _default path
-                    galleries = path(settings.STATICFILES_DIRS[0]).joinpath('avatars').joinpath('_default')
-                    avatars_list += galleries.files('*.gif')
-                    avatars_list += galleries.files('*.jpg')
-                    avatars_list += galleries.files('*.jpeg')
-                    avatars_list += galleries.files('*.png')
-                except Exception as e:
-                    pass
-                # Second try, all paths
-                if not avatars_list:
-                    avatars_list = []
-                    for directory in path(settings.STATICFILES_DIRS[0]).joinpath('avatars').dirs():
-                        if not directory[-7:] == '_locked' and not directory[-7:] == '_thumbs':
-                            avatars_list += directory.files('*.gif')
-                            avatars_list += directory.files('*.jpg')
-                            avatars_list += directory.files('*.jpeg')
-                            avatars_list += directory.files('*.png')
-                if avatars_list:
-                    # Pick random avatar from list
-                    self.avatar_type = 'gallery'
-                    self.avatar_image = '/'.join(path(choice(avatars_list)).splitall()[-2:])
-                    return True
-            except Exception as e:
-                pass
-
-        self.avatar_type = 'gravatar'
-        self.avatar_image = None
-        return True
-
-    def delete_avatar_temp(self):
-        if self.avatar_temp:
-            try:
-                av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_temp)
-                if not av_file.isdir():
-                    av_file.remove()
-            except Exception:
-                pass
-
-        self.avatar_temp = None
-
-    def delete_avatar_original(self):
-        if self.avatar_original:
-            try:
-                av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_original)
-                if not av_file.isdir():
-                    av_file.remove()
-            except Exception:
-                pass
-
-        self.avatar_original = None
-
-    def delete_avatar_image(self):
-        if self.avatar_image:
-            for size in settings.AVATAR_SIZES[1:]:
-                try:
-                    av_file = path(settings.MEDIA_ROOT + 'avatars/' + str(size) + '_' + self.avatar_image)
-                    if not av_file.isdir():
-                        av_file.remove()
-                except Exception:
-                    pass
-            try:
-                av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_image)
-                if not av_file.isdir():
-                    av_file.remove()
-            except Exception:
-                pass
-
-        self.avatar_image = None
-
-    def delete_avatar(self):
-        self.delete_avatar_temp()
-        self.delete_avatar_original()
-        self.delete_avatar_image()
-
-    def delete_content(self):
-        delete_user_content.send(sender=self)
-
-    def delete(self, *args, **kwargs):
-        self.delete_avatar()
-        super(User, self).delete(*args, **kwargs)
-
-    def set_username(self, username):
-        self.username = username.strip()
-        self.username_slug = slugify(username)
-
-    def sync_username(self):
-        rename_user.send(sender=self)
-
-    def is_username_valid(self, e):
-        try:
-            raise ValidationError(e.message_dict['username'])
-        except KeyError:
-            pass
-        try:
-            raise ValidationError(e.message_dict['username_slug'])
-        except KeyError:
-            pass
-
-    def is_email_valid(self, e):
-        try:
-            raise ValidationError(e.message_dict['email'])
-        except KeyError:
-            pass
-        try:
-            raise ValidationError(e.message_dict['email_hash'])
-        except KeyError:
-            pass
-
-    def is_password_valid(self, e):
-        try:
-            raise ValidationError(e.message_dict['password'])
-        except KeyError:
-            pass
-
-    def set_email(self, email):
-        self.email = email.strip().lower()
-        self.email_hash = hashlib.md5(self.email).hexdigest()
-
-    def set_password(self, raw_password):
-        self.password_date = tz_util.now()
-        self.password = make_password(raw_password.strip())
-
-    def set_last_visit(self, ip, agent):
-        self.last_date = tz_util.now()
-        self.last_ip = ip
-        self.last_agent = agent
-
-    def check_password(self, raw_password, mobile=False):
-        """
-        Returns a boolean of whether the raw_password was correct. Handles
-        hashing formats behind the scenes.
-        """
-        def setter(raw_password):
-            self.set_password(raw_password)
-            self.save()
-
-        # Is standard password allright?
-        if check_password(raw_password, self.password, setter):
-            return True
-
-        # Check mobile password?
-        if mobile:
-            raw_password = raw_password[:1].lower() + raw_password[1:]
-        else:
-            password_reversed = u''
-            for c in raw_password:
-                r = c.upper()
-                if r == c:
-                    r = c.lower()
-                password_reversed += r
-            raw_password = password_reversed
-        return check_password(raw_password, self.password, setter)
-
-    def is_following(self, user):
-        try:
-            return self.follows.filter(id=user.pk).count() > 0
-        except AttributeError:
-            return self.follows.filter(id=user).count() > 0
-
-    def is_ignoring(self, user):
-        try:
-            return self.ignores.filter(id=user.pk).count() > 0
-        except AttributeError:
-            return self.ignores.filter(id=user).count() > 0
-        
-    def ignored_users(self):
-        return [item['id'] for item in self.ignores.values('id')]
-
-    def allow_pd_invite(self, user):
-        # PD's from nobody
-        if self.allow_pds == 3:
-            return False
-        # PD's from followed
-        if self.allow_pds == 2:
-            return self.is_following(user)
-        # PD's from non-ignored
-        if self.allow_pds == 1:
-            return not self.is_ignoring(user)
-        return True
-
-    def get_roles(self):
-        if self.rank:
-            return self.roles.all() | self.rank.roles.all()
-        return self.roles.all()
-
-    def make_acl_key(self, force=False):
-        if not force and self.acl_key:
-            return self.acl_key
-        roles_ids = []
-        for role in self.roles.all():
-            roles_ids.append(role.pk)
-        for role in self.rank.roles.all():
-            if not role.pk in roles_ids:
-                roles_ids.append(role.pk)
-        roles_ids.sort()
-        self.acl_key = 'acl_%s' % hashlib.md5('_'.join(str(x) for x in roles_ids)).hexdigest()[0:8]
-        self.save(update_fields=('acl_key',))
-        return self.acl_key
-
-    def acl(self, request):
-        return acl(request, self)
-
-    def get_avatar(self, size=None):
-        image_size = avatar_size(size) if size else None
-
-        # Get uploaded avatar
-        if self.avatar_type == 'upload':
-            image_prefix = '%s_' % image_size if image_size else ''
-            return settings.MEDIA_URL + 'avatars/' + image_prefix + self.avatar_image
-
-        # Get gallery avatar
-        if self.avatar_type == 'gallery':
-            image_prefix = '_thumbs/%s/' % image_size if image_size else ''
-            return settings.STATIC_URL + 'avatars/' + image_prefix + self.avatar_image
-
-        # No avatar found, get gravatar
-        if not image_size:
-            image_size = settings.AVATAR_SIZES[0]
-        return 'http://www.gravatar.com/avatar/%s?s=%s' % (hashlib.md5(self.email).hexdigest(), image_size)
-
-    def get_ranking(self):
-        if not self.ranking:
-            self.ranking = User.objects.filter(score__gt=self.score).count() + 1
-            self.save(force_update=True)
-        return self.ranking
-
-    def get_title(self):
-        if self.title:
-            return self.title
-        if self.rank:
-            return self.rank.title
-        return None
-
-    def get_style(self):
-        if self.rank:
-            return self.rank.style
-        return ''
-
-    def email_user(self, request, template, subject, context={}):
-        context = RequestContext(request, context)
-        context['author'] = context['user']
-        context['user'] = self
-
-        email_html = render_to_string('_email/%s.html' % template,
-                                      context_instance=context)
-        email_text = render_to_string('_email/%s.txt' % template,
-                                      context_instance=context)
-
-        # Set message recipient
-        if settings.DEBUG and settings.CATCH_ALL_EMAIL_ADDRESS:
-            recipient = settings.CATCH_ALL_EMAIL_ADDRESS
-        else:
-            recipient = self.email
-
-        # Build message and add it to queue
-        email = EmailMultiAlternatives(subject, email_text, settings.EMAIL_HOST_USER, [recipient])
-        email.attach_alternative(email_html, "text/html")
-        request.mails_queue.append(email)
-
-    def get_activation(self):
-        activations = ['none', 'user', 'admin', 'credentials']
-        return activations[self.activation]
-
-    def alert(self, message):
-        from misago.models import Alert
-        self.alerts += 1
-        return Alert(user=self, message=message, date=tz_util.now())
-
-    def sync_unread_pds(self, unread):
-        self.unread_pds = unread
-        self.sync_pds = False
-
-    def get_date(self):
-        return self.join_date
-
-    def sync_profile(self):
-        if (settings.PROFILES_SYNC_FREQUENCY > 0 and
-                self.last_sync <= tz_util.now() - timedelta(days=settings.PROFILES_SYNC_FREQUENCY)):
-            sync_user_profile.send(sender=self)
-            self.last_sync = tz_util.now()
-            return True
-        return False
-
-    def timeline(self, qs, length=100):
-        posts = {}
-        now = tz_util.now()
-        for item in qs.iterator():
-            diff = (now - item.timeline_date).days
-            try:
-                posts[diff] += 1
-            except KeyError:
-                posts[diff] = 1
-
-        graph = []
-        for i in reversed(range(100)):
-            try:
-                graph.append(posts[i])
-            except KeyError:
-                graph.append(0)
-        return graph
-
-
-class Guest(object):
-    """
-    Misago Guest dummy
-    """
-    id = -1
-    pk = -1
-    is_team = False
-
-    def is_anonymous(self):
-        return True
-
-    def is_authenticated(self):
-        return False
-
-    def is_crawler(self):
-        return False
-
-    def get_roles(self):
-        from misago.models import Role
-        return Role.objects.filter(_special='guest')
-
-    def make_acl_key(self):
-        return 'acl_guest'
-
-
-class Crawler(Guest):
-    """
-    Misago Crawler dummy
-    """
-    is_team = False
-
-    def __init__(self, username):
-        self.username = username
-
-    def is_anonymous(self):
-        return False
-
-    def is_authenticated(self):
-        return False
-
-    def is_crawler(self):
-        return True
-
-
-"""
-Signals handlers
-"""
-def sync_user_handler(sender, **kwargs):
-    sender.following = sender.follows.count()
-    sender.followers = sender.follows_set.count()
-
+import hashlib
+from datetime import timedelta
+import math
+from random import choice
+from path import path
+from django.contrib.auth.hashers import (
+    check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
+from django.core.cache import cache, InvalidCacheBackendError
+from django.core.exceptions import ValidationError
+from django.core.mail import EmailMultiAlternatives
+from django.db import models
+from django.template import RequestContext
+from django.utils import timezone as tz_util
+from django.utils.translation import ugettext_lazy as _
+from misago.acl.builder import acl
+from misago.conf import settings
+from misago.monitor import monitor, UpdatingMonitor
+from misago.signals import delete_user_content, rename_user, sync_user_profile
+from misago.template.loader import render_to_string
+from misago.utils.avatars import avatar_size
+from misago.utils.strings import random_string, slugify
+from misago.validators import validate_username, validate_password, validate_email
+
+class UserManager(models.Manager):
+    """
+    User Manager provides us with some additional methods for users
+    """
+    def get_blank_user(self):
+        blank_user = User(
+                        join_date=tz_util.now(),
+                        join_ip='127.0.0.1'
+                        )
+        return blank_user
+
+    def resync_monitor(self):
+        with UpdatingMonitor() as cm:
+            monitor['users'] = self.filter(activation=0).count()
+            monitor['users_inactive'] = self.filter(activation__gt=0).count()
+            last_user = self.filter(activation=0).latest('id')
+            monitor['last_user'] = last_user.pk
+            monitor['last_user_name'] = last_user.username
+            monitor['last_user_slug'] = last_user.username_slug
+
+    def create_user(self, username, email, password, timezone=False, ip='127.0.0.1', agent='', no_roles=False, activation=0, request=False):
+        token = ''
+        if activation > 0:
+            token = random_string(12)
+
+        timezone = timezone or settings.default_timezone
+
+        # Get first rank
+        try:
+            from misago.models import Rank
+            default_rank = Rank.objects.filter(special=0).order_by('-order')[0]
+        except IndexError:
+            default_rank = None
+
+        # Store user in database
+        new_user = User(
+                        last_sync=tz_util.now(),
+                        join_date=tz_util.now(),
+                        join_ip=ip,
+                        join_agent=agent,
+                        activation=activation,
+                        token=token,
+                        timezone=timezone,
+                        rank=default_rank,
+                        subscribe_start=settings.subscribe_start,
+                        subscribe_reply=settings.subscribe_reply,
+                        )
+
+        validate_username(username)
+        validate_password(password)
+        new_user.set_username(username)
+        new_user.set_email(email)
+        new_user.set_password(password)
+        new_user.full_clean()
+        new_user.default_avatar()
+        new_user.save(force_insert=True)
+
+        # Set user roles?
+        if not no_roles:
+            from misago.models import Role
+            new_user.roles.add(Role.objects.get(_special='registered'))
+            new_user.make_acl_key()
+            new_user.save(force_update=True)
+
+        # Update forum stats
+        with UpdatingMonitor() as cm:
+            if activation == 0:
+                monitor.increase('users')
+                monitor['last_user'] = new_user.pk
+                monitor['last_user_name'] = new_user.username
+                monitor['last_user_slug'] = new_user.username_slug
+            else:
+                monitor.increase('users_inactive')
+
+        # Return new user
+        return new_user
+
+    def get_by_email(self, email):
+        return self.get(email_hash=hashlib.md5(email).hexdigest())
+
+    def filter_stats(self, start, end):
+        return self.filter(join_date__gte=start).filter(join_date__lte=end)
+
+
+class User(models.Model):
+    """
+    Misago User model
+    """
+    username = models.CharField(max_length=255)
+    username_slug = models.SlugField(max_length=255, unique=True,
+                                     error_messages={'unique': _("This user name is already in use by another user.")})
+    email = models.EmailField(max_length=255, validators=[validate_email])
+    email_hash = models.CharField(max_length=32, unique=True,
+                                     error_messages={'unique': _("This email address is already in use by another user.")})
+    password = models.CharField(max_length=255)
+    password_date = models.DateTimeField()
+    avatar_type = models.CharField(max_length=10, null=True, blank=True)
+    avatar_image = models.CharField(max_length=255, null=True, blank=True)
+    avatar_original = models.CharField(max_length=255, null=True, blank=True)
+    avatar_temp = models.CharField(max_length=255, null=True, blank=True)
+    signature = models.TextField(null=True, blank=True)
+    signature_preparsed = models.TextField(null=True, blank=True)
+    join_date = models.DateTimeField()
+    join_ip = models.GenericIPAddressField()
+    join_agent = models.TextField(null=True, blank=True)
+    last_date = models.DateTimeField(null=True, blank=True)
+    last_ip = models.GenericIPAddressField(null=True, blank=True)
+    last_agent = models.TextField(null=True, blank=True)
+    hide_activity = models.PositiveIntegerField(default=0)
+    subscribe_start = models.PositiveIntegerField(default=0)
+    subscribe_reply = models.PositiveIntegerField(default=0)
+    receive_newsletters = models.BooleanField(default=True)
+    threads = models.PositiveIntegerField(default=0)
+    posts = models.PositiveIntegerField(default=0)
+    votes = models.PositiveIntegerField(default=0)
+    karma_given_p = models.PositiveIntegerField(default=0)
+    karma_given_n = models.PositiveIntegerField(default=0)
+    karma_p = models.PositiveIntegerField(default=0)
+    karma_n = models.PositiveIntegerField(default=0)
+    following = models.PositiveIntegerField(default=0)
+    followers = models.PositiveIntegerField(default=0)
+    score = models.IntegerField(default=0)
+    ranking = models.PositiveIntegerField(default=0)
+    rank = models.ForeignKey('Rank', null=True, blank=True, on_delete=models.SET_NULL)
+    last_sync = models.DateTimeField(null=True, blank=True)
+    follows = models.ManyToManyField('self', related_name='follows_set', symmetrical=False)
+    ignores = models.ManyToManyField('self', related_name='ignores_set', symmetrical=False)
+    title = models.CharField(max_length=255, null=True, blank=True)
+    last_post = models.DateTimeField(null=True, blank=True)
+    last_search = models.DateTimeField(null=True, blank=True)
+    alerts = models.PositiveIntegerField(default=0)
+    alerts_date = models.DateTimeField(null=True, blank=True)
+    allow_pds = models.PositiveIntegerField(default=0)
+    unread_pds = models.PositiveIntegerField(default=0)
+    sync_pds = models.BooleanField(default=False)
+    activation = models.IntegerField(default=0)
+    token = models.CharField(max_length=12, null=True, blank=True)
+    avatar_ban = models.BooleanField(default=False)
+    avatar_ban_reason_user = models.TextField(null=True, blank=True)
+    avatar_ban_reason_admin = models.TextField(null=True, blank=True)
+    signature_ban = models.BooleanField(default=False)
+    signature_ban_reason_user = models.TextField(null=True, blank=True)
+    signature_ban_reason_admin = models.TextField(null=True, blank=True)
+    timezone = models.CharField(max_length=255, default='utc')
+    roles = models.ManyToManyField('Role')
+    is_team = models.BooleanField(default=False)
+    acl_key = models.CharField(max_length=12, null=True, blank=True)
+
+    objects = UserManager()
+
+    ACTIVATION_NONE = 0
+    ACTIVATION_USER = 1
+    ACTIVATION_ADMIN = 2
+    ACTIVATION_CREDENTIALS = 3
+
+    statistics_name = _('Users Registrations')
+
+    class Meta:
+        app_label = 'misago'
+
+    def is_god(self):
+        try:
+            return self.is_god_cache
+        except AttributeError:
+            for user in settings.ADMINS:
+                if user[1].lower() == self.email:
+                    self.is_god_cache = True
+                    return True
+            self.is_god_cache = False
+            return False
+
+    def is_anonymous(self):
+        return False
+
+    def is_authenticated(self):
+        return True
+
+    def is_crawler(self):
+        return False
+
+    def is_protected(self):
+        for role in self.roles.all():
+            if role.protected:
+                return True
+        return False
+
+    def lock_avatar(self):
+        # Kill existing avatar and lock our ability to change it
+        self.delete_avatar()
+        self.avatar_ban = True
+
+        # Pick new one from _locked gallery
+        galleries = path(settings.STATICFILES_DIRS[0]).joinpath('avatars').joinpath('_locked')
+        avatars_list = galleries.files('*.gif')
+        avatars_list += galleries.files('*.jpg')
+        avatars_list += galleries.files('*.jpeg')
+        avatars_list += galleries.files('*.png')
+        self.avatar_type = 'gallery'
+        self.avatar_image = '/'.join(path(choice(avatars_list)).splitall()[-2:])
+
+    def default_avatar(self):
+        if settings.default_avatar == 'gallery':
+            try:
+                avatars_list = []
+                try:
+                    # First try, _default path
+                    galleries = path(settings.STATICFILES_DIRS[0]).joinpath('avatars').joinpath('_default')
+                    avatars_list += galleries.files('*.gif')
+                    avatars_list += galleries.files('*.jpg')
+                    avatars_list += galleries.files('*.jpeg')
+                    avatars_list += galleries.files('*.png')
+                except Exception as e:
+                    pass
+                # Second try, all paths
+                if not avatars_list:
+                    avatars_list = []
+                    for directory in path(settings.STATICFILES_DIRS[0]).joinpath('avatars').dirs():
+                        if not directory[-7:] == '_locked' and not directory[-7:] == '_thumbs':
+                            avatars_list += directory.files('*.gif')
+                            avatars_list += directory.files('*.jpg')
+                            avatars_list += directory.files('*.jpeg')
+                            avatars_list += directory.files('*.png')
+                if avatars_list:
+                    # Pick random avatar from list
+                    self.avatar_type = 'gallery'
+                    self.avatar_image = '/'.join(path(choice(avatars_list)).splitall()[-2:])
+                    return True
+            except Exception as e:
+                pass
+
+        self.avatar_type = 'gravatar'
+        self.avatar_image = None
+        return True
+
+    def delete_avatar_temp(self):
+        if self.avatar_temp:
+            try:
+                av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_temp)
+                if not av_file.isdir():
+                    av_file.remove()
+            except Exception:
+                pass
+
+        self.avatar_temp = None
+
+    def delete_avatar_original(self):
+        if self.avatar_original:
+            try:
+                av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_original)
+                if not av_file.isdir():
+                    av_file.remove()
+            except Exception:
+                pass
+
+        self.avatar_original = None
+
+    def delete_avatar_image(self):
+        if self.avatar_image:
+            for size in settings.AVATAR_SIZES[1:]:
+                try:
+                    av_file = path(settings.MEDIA_ROOT + 'avatars/' + str(size) + '_' + self.avatar_image)
+                    if not av_file.isdir():
+                        av_file.remove()
+                except Exception:
+                    pass
+            try:
+                av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_image)
+                if not av_file.isdir():
+                    av_file.remove()
+            except Exception:
+                pass
+
+        self.avatar_image = None
+
+    def delete_avatar(self):
+        self.delete_avatar_temp()
+        self.delete_avatar_original()
+        self.delete_avatar_image()
+
+    def delete_content(self):
+        delete_user_content.send(sender=self)
+
+    def delete(self, *args, **kwargs):
+        self.delete_avatar()
+        super(User, self).delete(*args, **kwargs)
+
+    def set_username(self, username):
+        self.username = username.strip()
+        self.username_slug = slugify(username)
+
+    def sync_username(self):
+        rename_user.send(sender=self)
+
+    def is_username_valid(self, e):
+        try:
+            raise ValidationError(e.message_dict['username'])
+        except KeyError:
+            pass
+        try:
+            raise ValidationError(e.message_dict['username_slug'])
+        except KeyError:
+            pass
+
+    def is_email_valid(self, e):
+        try:
+            raise ValidationError(e.message_dict['email'])
+        except KeyError:
+            pass
+        try:
+            raise ValidationError(e.message_dict['email_hash'])
+        except KeyError:
+            pass
+
+    def is_password_valid(self, e):
+        try:
+            raise ValidationError(e.message_dict['password'])
+        except KeyError:
+            pass
+
+    def set_email(self, email):
+        self.email = email.strip().lower()
+        self.email_hash = hashlib.md5(self.email).hexdigest()
+
+    def set_password(self, raw_password):
+        self.password_date = tz_util.now()
+        self.password = make_password(raw_password.strip())
+
+    def set_last_visit(self, ip, agent):
+        self.last_date = tz_util.now()
+        self.last_ip = ip
+        self.last_agent = agent
+
+    def check_password(self, raw_password, mobile=False):
+        """
+        Returns a boolean of whether the raw_password was correct. Handles
+        hashing formats behind the scenes.
+        """
+        def setter(raw_password):
+            self.set_password(raw_password)
+            self.save()
+
+        # Is standard password allright?
+        if check_password(raw_password, self.password, setter):
+            return True
+
+        # Check mobile password?
+        if mobile:
+            raw_password = raw_password[:1].lower() + raw_password[1:]
+        else:
+            password_reversed = u''
+            for c in raw_password:
+                r = c.upper()
+                if r == c:
+                    r = c.lower()
+                password_reversed += r
+            raw_password = password_reversed
+        return check_password(raw_password, self.password, setter)
+
+    def is_following(self, user):
+        try:
+            return self.follows.filter(id=user.pk).count() > 0
+        except AttributeError:
+            return self.follows.filter(id=user).count() > 0
+
+    def is_ignoring(self, user):
+        try:
+            return self.ignores.filter(id=user.pk).count() > 0
+        except AttributeError:
+            return self.ignores.filter(id=user).count() > 0
+        
+    def ignored_users(self):
+        return [item['id'] for item in self.ignores.values('id')]
+
+    def allow_pd_invite(self, user):
+        # PD's from nobody
+        if self.allow_pds == 3:
+            return False
+        # PD's from followed
+        if self.allow_pds == 2:
+            return self.is_following(user)
+        # PD's from non-ignored
+        if self.allow_pds == 1:
+            return not self.is_ignoring(user)
+        return True
+
+    def get_roles(self):
+        if self.rank:
+            return self.roles.all() | self.rank.roles.all()
+        return self.roles.all()
+
+    def make_acl_key(self, force=False):
+        if not force and self.acl_key:
+            return self.acl_key
+        roles_ids = []
+        for role in self.roles.all():
+            roles_ids.append(role.pk)
+        for role in self.rank.roles.all():
+            if not role.pk in roles_ids:
+                roles_ids.append(role.pk)
+        roles_ids.sort()
+        self.acl_key = 'acl_%s' % hashlib.md5('_'.join(str(x) for x in roles_ids)).hexdigest()[0:8]
+        self.save(update_fields=('acl_key',))
+        return self.acl_key
+
+    def acl(self, request):
+        return acl(request, self)
+
+    def get_avatar(self, size=None):
+        image_size = avatar_size(size) if size else None
+
+        # Get uploaded avatar
+        if self.avatar_type == 'upload':
+            image_prefix = '%s_' % image_size if image_size else ''
+            return settings.MEDIA_URL + 'avatars/' + image_prefix + self.avatar_image
+
+        # Get gallery avatar
+        if self.avatar_type == 'gallery':
+            image_prefix = '_thumbs/%s/' % image_size if image_size else ''
+            return settings.STATIC_URL + 'avatars/' + image_prefix + self.avatar_image
+
+        # No avatar found, get gravatar
+        if not image_size:
+            image_size = settings.AVATAR_SIZES[0]
+        return 'http://www.gravatar.com/avatar/%s?s=%s' % (hashlib.md5(self.email).hexdigest(), image_size)
+
+    def get_ranking(self):
+        if not self.ranking:
+            self.ranking = User.objects.filter(score__gt=self.score).count() + 1
+            self.save(force_update=True)
+        return self.ranking
+
+    def get_title(self):
+        if self.title:
+            return self.title
+        if self.rank:
+            return self.rank.title
+        return None
+
+    def get_style(self):
+        if self.rank:
+            return self.rank.style
+        return ''
+
+    def email_user(self, request, template, subject, context={}):
+        context = RequestContext(request, context)
+        context['author'] = context['user']
+        context['user'] = self
+
+        email_html = render_to_string('_email/%s.html' % template,
+                                      context_instance=context)
+        email_text = render_to_string('_email/%s.txt' % template,
+                                      context_instance=context)
+
+        # Set message recipient
+        if settings.DEBUG and settings.CATCH_ALL_EMAIL_ADDRESS:
+            recipient = settings.CATCH_ALL_EMAIL_ADDRESS
+        else:
+            recipient = self.email
+
+        # Build message and add it to queue
+        email = EmailMultiAlternatives(subject, email_text, settings.EMAIL_HOST_USER, [recipient])
+        email.attach_alternative(email_html, "text/html")
+        request.mails_queue.append(email)
+
+    def get_activation(self):
+        activations = ['none', 'user', 'admin', 'credentials']
+        return activations[self.activation]
+
+    def alert(self, message):
+        from misago.models import Alert
+        self.alerts += 1
+        return Alert(user=self, message=message, date=tz_util.now())
+
+    def sync_unread_pds(self, unread):
+        self.unread_pds = unread
+        self.sync_pds = False
+
+    def get_date(self):
+        return self.join_date
+
+    def sync_profile(self):
+        if (settings.PROFILES_SYNC_FREQUENCY > 0 and
+                self.last_sync <= tz_util.now() - timedelta(days=settings.PROFILES_SYNC_FREQUENCY)):
+            sync_user_profile.send(sender=self)
+            self.last_sync = tz_util.now()
+            return True
+        return False
+
+    def timeline(self, qs, length=100):
+        posts = {}
+        now = tz_util.now()
+        for item in qs.iterator():
+            diff = (now - item.timeline_date).days
+            try:
+                posts[diff] += 1
+            except KeyError:
+                posts[diff] = 1
+
+        graph = []
+        for i in reversed(range(100)):
+            try:
+                graph.append(posts[i])
+            except KeyError:
+                graph.append(0)
+        return graph
+
+
+class Guest(object):
+    """
+    Misago Guest dummy
+    """
+    id = -1
+    pk = -1
+    is_team = False
+
+    def is_anonymous(self):
+        return True
+
+    def is_authenticated(self):
+        return False
+
+    def is_crawler(self):
+        return False
+
+    def get_roles(self):
+        from misago.models import Role
+        return Role.objects.filter(_special='guest')
+
+    def make_acl_key(self):
+        return 'acl_guest'
+
+
+class Crawler(Guest):
+    """
+    Misago Crawler dummy
+    """
+    is_team = False
+
+    def __init__(self, username):
+        self.username = username
+
+    def is_anonymous(self):
+        return False
+
+    def is_authenticated(self):
+        return False
+
+    def is_crawler(self):
+        return True
+
+
+"""
+Signals handlers
+"""
+def sync_user_handler(sender, **kwargs):
+    sender.following = sender.follows.count()
+    sender.followers = sender.follows_set.count()
+
 sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_follows")
 sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_follows")

+ 8 - 8
misago/models/usernamechangemodel.py

@@ -1,9 +1,9 @@
-from django.db import models
-
-class UsernameChange(models.Model):
-    user = models.ForeignKey('User', related_name='namechanges')
-    date = models.DateTimeField()
-    old_username = models.CharField(max_length=255)
-
-    class Meta:
+from django.db import models
+
+class UsernameChange(models.Model):
+    user = models.ForeignKey('User', related_name='namechanges')
+    date = models.DateTimeField()
+    old_username = models.CharField(max_length=255)
+
+    class Meta:
         app_label = 'misago'
         app_label = 'misago'

+ 35 - 35
misago/models/watchedthreadmodel.py

@@ -1,36 +1,36 @@
-from django.db import models
-from misago.signals import merge_thread, move_forum_content, move_thread
-
-class WatchedThread(models.Model):
-    user = models.ForeignKey('User')
-    forum = models.ForeignKey('Forum')
-    thread = models.ForeignKey('Thread')
-    starter = models.ForeignKey('User', blank=True, null=True, related_name='+')
-    last_read = models.DateTimeField()
-    email = models.BooleanField(default=False)
-    deleted = False
-
-    class Meta:
-        app_label = 'misago'
-    
-    def save(self, *args, **kwargs):
-        if not self.deleted:
-            super(WatchedThread, self).save(*args, **kwargs)
-            
-
-def move_forum_content_handler(sender, **kwargs):
-    WatchedThread.objects.filter(forum=sender).update(forum=kwargs['move_to'])
-
-move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_threads_watchers")
-
-
-def move_thread_handler(sender, **kwargs):
-    WatchedThread.objects.filter(forum=sender.forum_id).update(forum=kwargs['move_to'])
-
-move_thread.connect(move_thread_handler, dispatch_uid="move_thread_watchers")
-
-
-def merge_thread_handler(sender, **kwargs):
-    WatchedThread.objects.filter(thread=sender).delete()
-
+from django.db import models
+from misago.signals import merge_thread, move_forum_content, move_thread
+
+class WatchedThread(models.Model):
+    user = models.ForeignKey('User')
+    forum = models.ForeignKey('Forum')
+    thread = models.ForeignKey('Thread')
+    starter = models.ForeignKey('User', blank=True, null=True, related_name='+')
+    last_read = models.DateTimeField()
+    email = models.BooleanField(default=False)
+    deleted = False
+
+    class Meta:
+        app_label = 'misago'
+    
+    def save(self, *args, **kwargs):
+        if not self.deleted:
+            super(WatchedThread, self).save(*args, **kwargs)
+            
+
+def move_forum_content_handler(sender, **kwargs):
+    WatchedThread.objects.filter(forum=sender).update(forum=kwargs['move_to'])
+
+move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_threads_watchers")
+
+
+def move_thread_handler(sender, **kwargs):
+    WatchedThread.objects.filter(forum=sender.forum_id).update(forum=kwargs['move_to'])
+
+move_thread.connect(move_thread_handler, dispatch_uid="move_thread_watchers")
+
+
+def merge_thread_handler(sender, **kwargs):
+    WatchedThread.objects.filter(thread=sender).delete()
+
 merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_watchers")
 merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_watchers")

+ 103 - 103
misago/monitor.py

@@ -1,103 +1,103 @@
-from datetime import timedelta
-from django.core.cache import cache
-from django.utils import timezone
-from misago.thread import local
-
-_thread_local = local()
-
-def load_monitor():
-    from misago.models import MonitorItem
-    monitor = cache.get('monitor', {})
-    if not monitor:
-        for i in MonitorItem.objects.all():
-            monitor[i.id] = [i.value, i.updated, i.type]
-        cache.set('monitor', monitor)
-    return monitor
-
-
-def refresh_monitor():
-    _thread_local.monitor = load_monitor()
-
-
-class Monitor(object):
-    def monitor(self):
-        try:
-            return _thread_local.monitor
-        except AttributeError:
-            _thread_local.monitor = load_monitor()
-            return _thread_local.monitor
-
-    def entry(self, key):
-        try:
-            return self.monitor()[key]
-        except KeyError:
-            raise Exception(u"Monitor entry \"%s\" could not be found." % key)
-
-    def __contains__(self, key):
-        return key in self.monitor()
-
-    def __getitem__(self, key):
-        return self.entry(key)[0]
-
-    def __getattr__(self, key):
-        return self.entry(key)[0]
-
-    def __setitem__(self, key, value):
-        _thread_local.monitor_update.append((key, value))
-        return value
-
-    def increase(self, key, i=1):
-        _thread_local.monitor_update.append((key, self[key] + i))
-
-    def decrease(self, key, i=1):
-        _thread_local.monitor_update.append((key, self[key] - i))
-
-    def get(self, key, default=None):
-        if not key in self.monitor():
-            return default
-        return self.entry(key)[0]
-
-    def updated(self, key):
-        if key in self.monitor():
-            return self.entry(key)[1]
-        return None
-
-    def expired(self, key, seconds=5):
-        return self.entry(key)[1] < (timezone.now() - timedelta(seconds=seconds))
-
-    def has_key(self, key):
-        return key in self.entry()
-
-    def keys(self):
-        return self.entry().keys()
-
-    def values(self):
-        return self.entry().values()
-
-    def items(self):
-        return self.entry().items()
-
-    def iterkeys(self):
-        return self.entry().iterkeys()
-
-    def itervalues(self):
-        return self.entry().itervalues()
-
-    def iteritems(self):
-        return self.entry().iteritems()
-
-
-class UpdatingMonitor(object):
-    def __enter__(self):
-        _thread_local.monitor_update = []
-
-    def __exit__(self, type, value, traceback):
-        if _thread_local.monitor_update:
-            from misago.models import MonitorItem
-            for key, value in _thread_local.monitor_update:
-                MonitorItem.objects.filter(pk=key).update(_value=value, updated=timezone.now())
-            cache.delete('monitor')
-            _thread_local.monitor_update = None
-
-
-monitor = Monitor()
+from datetime import timedelta
+from django.core.cache import cache
+from django.utils import timezone
+from misago.thread import local
+
+_thread_local = local()
+
+def load_monitor():
+    from misago.models import MonitorItem
+    monitor = cache.get('monitor', {})
+    if not monitor:
+        for i in MonitorItem.objects.all():
+            monitor[i.id] = [i.value, i.updated, i.type]
+        cache.set('monitor', monitor)
+    return monitor
+
+
+def refresh_monitor():
+    _thread_local.monitor = load_monitor()
+
+
+class Monitor(object):
+    def monitor(self):
+        try:
+            return _thread_local.monitor
+        except AttributeError:
+            _thread_local.monitor = load_monitor()
+            return _thread_local.monitor
+
+    def entry(self, key):
+        try:
+            return self.monitor()[key]
+        except KeyError:
+            raise Exception(u"Monitor entry \"%s\" could not be found." % key)
+
+    def __contains__(self, key):
+        return key in self.monitor()
+
+    def __getitem__(self, key):
+        return self.entry(key)[0]
+
+    def __getattr__(self, key):
+        return self.entry(key)[0]
+
+    def __setitem__(self, key, value):
+        _thread_local.monitor_update.append((key, value))
+        return value
+
+    def increase(self, key, i=1):
+        _thread_local.monitor_update.append((key, self[key] + i))
+
+    def decrease(self, key, i=1):
+        _thread_local.monitor_update.append((key, self[key] - i))
+
+    def get(self, key, default=None):
+        if not key in self.monitor():
+            return default
+        return self.entry(key)[0]
+
+    def updated(self, key):
+        if key in self.monitor():
+            return self.entry(key)[1]
+        return None
+
+    def expired(self, key, seconds=5):
+        return self.entry(key)[1] < (timezone.now() - timedelta(seconds=seconds))
+
+    def has_key(self, key):
+        return key in self.entry()
+
+    def keys(self):
+        return self.entry().keys()
+
+    def values(self):
+        return self.entry().values()
+
+    def items(self):
+        return self.entry().items()
+
+    def iterkeys(self):
+        return self.entry().iterkeys()
+
+    def itervalues(self):
+        return self.entry().itervalues()
+
+    def iteritems(self):
+        return self.entry().iteritems()
+
+
+class UpdatingMonitor(object):
+    def __enter__(self):
+        _thread_local.monitor_update = []
+
+    def __exit__(self, type, value, traceback):
+        if _thread_local.monitor_update:
+            from misago.models import MonitorItem
+            for key, value in _thread_local.monitor_update:
+                MonitorItem.objects.filter(pk=key).update(_value=value, updated=timezone.now())
+            cache.delete('monitor')
+            _thread_local.monitor_update = None
+
+
+monitor = Monitor()

+ 62 - 62
misago/onlines.py

@@ -1,63 +1,63 @@
-from datetime import timedelta
-from django.core.cache import cache
-from django.utils import timezone
-from misago.models import Session
-from misago.monitor import monitor, UpdatingMonitor
-
-class MembersOnline(object):
-    def __init__(self, mode, frequency=180):
-        self.frequency = frequency
-        self._mode = mode
-        self._members = monitor['online_members']
-        self._all = monitor['online_all']
-        self._om = self._members
-        self._oa = self._all
-        if (self._mode != 'no' or monitor.expired('online_all', frequency) or
-                monitor.expired('online_members', frequency)):
-            self.count_sessions()
-
-    def count_sessions(self):
-        queryset = Session.objects.filter(matched=True).filter(crawler__isnull=True).filter(last__gte=timezone.now() - timedelta(seconds=self.frequency))
-        self._all = queryset.count()
-        self._members = queryset.filter(user__isnull=False).count()
-        cache.delete_many(['team_users_online', 'ranks_online'])
-
-    def new_session(self):
-        self._all += 1
-
-    def sign_in(self):
-        self._members += 1
-
-    def sign_out(self):
-        if self._members:
-            self._members -= 1
-
-    @property
-    def all(self):
-        return self._all
-
-    @property
-    def members(self):
-        return self._members
-
-    def sync(self):
-        if self._mode == 'snap':
-            with UpdatingMonitor() as cm:
-                if self._members != self._om:
-                    monitor['online_members'] = self._members
-                if self._all != self._oa:
-                    monitor['online_all'] = self._all
-
-    def stats(self, request):
-        stat = {
-                'members': self.members,
-                'all': self.all,
-               }
-
-        if not request.user.is_crawler():
-            if not stat['members'] and request.user.is_authenticated():
-                stat['members'] += 1
-                stat['all'] += 1
-            if not stat['all']:
-                stat['all'] += 1        
+from datetime import timedelta
+from django.core.cache import cache
+from django.utils import timezone
+from misago.models import Session
+from misago.monitor import monitor, UpdatingMonitor
+
+class MembersOnline(object):
+    def __init__(self, mode, frequency=180):
+        self.frequency = frequency
+        self._mode = mode
+        self._members = monitor['online_members']
+        self._all = monitor['online_all']
+        self._om = self._members
+        self._oa = self._all
+        if (self._mode != 'no' or monitor.expired('online_all', frequency) or
+                monitor.expired('online_members', frequency)):
+            self.count_sessions()
+
+    def count_sessions(self):
+        queryset = Session.objects.filter(matched=True).filter(crawler__isnull=True).filter(last__gte=timezone.now() - timedelta(seconds=self.frequency))
+        self._all = queryset.count()
+        self._members = queryset.filter(user__isnull=False).count()
+        cache.delete_many(['team_users_online', 'ranks_online'])
+
+    def new_session(self):
+        self._all += 1
+
+    def sign_in(self):
+        self._members += 1
+
+    def sign_out(self):
+        if self._members:
+            self._members -= 1
+
+    @property
+    def all(self):
+        return self._all
+
+    @property
+    def members(self):
+        return self._members
+
+    def sync(self):
+        if self._mode == 'snap':
+            with UpdatingMonitor() as cm:
+                if self._members != self._om:
+                    monitor['online_members'] = self._members
+                if self._all != self._oa:
+                    monitor['online_all'] = self._all
+
+    def stats(self, request):
+        stat = {
+                'members': self.members,
+                'all': self.all,
+               }
+
+        if not request.user.is_crawler():
+            if not stat['members'] and request.user.is_authenticated():
+                stat['members'] += 1
+                stat['all'] += 1
+            if not stat['all']:
+                stat['all'] += 1        
         return stat
         return stat

+ 110 - 110
misago/readstrackers.py

@@ -1,111 +1,111 @@
-from datetime import timedelta
-from django.conf import settings
-from django.utils import timezone
-from misago.models import Thread, ForumRead, ThreadRead
-
-class ForumsTracker(object):
-    def __init__(self, user):
-        self.user = user
-        self.cutoff = timezone.now() - timedelta(days=settings.READS_TRACKER_LENGTH)
-        self.forums = {}
-        if user.is_authenticated() and settings.READS_TRACKER_LENGTH > 0:
-            if user.join_date > self.cutoff:
-                self.cutoff = user.join_date
-            for forum in ForumRead.objects.filter(user=user).filter(updated__gte=self.cutoff).values('id', 'forum_id', 'updated', 'cleared'):
-                 self.forums[forum['forum_id']] = forum
-
-    def is_read(self, forum):
-        if not self.user.is_authenticated() or not forum.last_thread_date:
-            return True
-        try:
-            return forum.last_thread_date <= self.cutoff or forum.last_thread_date <= self.forums[forum.pk]['cleared']
-        except KeyError:
-            return False
-
-
-class ThreadsTracker(object):
-    def __init__(self, request, forum):
-        self.need_create = None
-        self.need_update = None
-        self.request = request
-        self.forum = forum
-        self.cutoff = timezone.now() - timedelta(days=settings.READS_TRACKER_LENGTH)
-        if request.user.is_authenticated():
-            if request.user.join_date > self.cutoff:
-                self.cutoff = request.user.join_date
-            try:
-                self.record = ForumRead.objects.get(user=request.user, forum=forum)
-                if self.record.cleared > self.cutoff:
-                    self.cutoff = self.record.cleared
-            except ForumRead.DoesNotExist:
-                self.record = ForumRead(user=request.user, forum=forum, cleared=self.cutoff)
-            self.threads = self.record.get_threads()
-
-    def read_date(self, thread):
-        if not self.request.user.is_authenticated():
-            return timezone.now()
-        try:
-            if self.threads[thread.pk].updated > self.cutoff:
-                return self.threads[thread.pk].updated
-        except KeyError:
-            pass
-        return self.cutoff
-
-    def is_read(self, thread):
-        if not self.request.user.is_authenticated():
-            return True
-        try:
-            return thread.last <= self.cutoff or thread.last <= self.threads[thread.pk].updated
-        except KeyError:
-            return False
-
-    def set_read(self, thread, post):
-        if self.request.user.is_authenticated() and post.date > self.cutoff:
-            try:
-                self.threads[thread.pk].updated = post.date
-                self.need_update = self.threads[thread.pk]
-            except KeyError:
-                self.need_create = thread
-
-    def unread_count(self, queryset=None):
-        try:
-            return self.unread_threads
-        except AttributeError:
-            self.unread_threads = 0
-            if queryset == None:
-                queryset = self.default_queryset()
-            for thread in queryset.filter(last__gte=self.record.cleared):
-                if not self.is_read(thread):
-                    self.unread_threads += 1
-            return self.unread_threads
-
-    def sync(self, queryset=None):
-        now = timezone.now()
-        if queryset == None:
-            queryset = self.default_queryset()
-
-        if self.need_create:
-            new_record = ThreadRead(
-                                    user=self.request.user,
-                                    thread=self.need_create,
-                                    forum=self.forum,
-                                    updated=now
-                                    )
-            new_record.save(force_insert=True)
-            self.threads[new_record.thread_id] = new_record
-
-        if self.need_update:
-            self.need_update.updated = now
-            self.need_update.save(force_update=True)
-
-        if self.need_create or self.need_update:
-            if not self.unread_count(queryset):
-                self.record.cleared = now
-            self.record.updated = now
-            if self.record.pk:
-                self.record.save(force_update=True)
-            else:
-                self.record.save(force_insert=True)
-
-    def default_queryset(self):
+from datetime import timedelta
+from django.conf import settings
+from django.utils import timezone
+from misago.models import Thread, ForumRead, ThreadRead
+
+class ForumsTracker(object):
+    def __init__(self, user):
+        self.user = user
+        self.cutoff = timezone.now() - timedelta(days=settings.READS_TRACKER_LENGTH)
+        self.forums = {}
+        if user.is_authenticated() and settings.READS_TRACKER_LENGTH > 0:
+            if user.join_date > self.cutoff:
+                self.cutoff = user.join_date
+            for forum in ForumRead.objects.filter(user=user).filter(updated__gte=self.cutoff).values('id', 'forum_id', 'updated', 'cleared'):
+                 self.forums[forum['forum_id']] = forum
+
+    def is_read(self, forum):
+        if not self.user.is_authenticated() or not forum.last_thread_date:
+            return True
+        try:
+            return forum.last_thread_date <= self.cutoff or forum.last_thread_date <= self.forums[forum.pk]['cleared']
+        except KeyError:
+            return False
+
+
+class ThreadsTracker(object):
+    def __init__(self, request, forum):
+        self.need_create = None
+        self.need_update = None
+        self.request = request
+        self.forum = forum
+        self.cutoff = timezone.now() - timedelta(days=settings.READS_TRACKER_LENGTH)
+        if request.user.is_authenticated():
+            if request.user.join_date > self.cutoff:
+                self.cutoff = request.user.join_date
+            try:
+                self.record = ForumRead.objects.get(user=request.user, forum=forum)
+                if self.record.cleared > self.cutoff:
+                    self.cutoff = self.record.cleared
+            except ForumRead.DoesNotExist:
+                self.record = ForumRead(user=request.user, forum=forum, cleared=self.cutoff)
+            self.threads = self.record.get_threads()
+
+    def read_date(self, thread):
+        if not self.request.user.is_authenticated():
+            return timezone.now()
+        try:
+            if self.threads[thread.pk].updated > self.cutoff:
+                return self.threads[thread.pk].updated
+        except KeyError:
+            pass
+        return self.cutoff
+
+    def is_read(self, thread):
+        if not self.request.user.is_authenticated():
+            return True
+        try:
+            return thread.last <= self.cutoff or thread.last <= self.threads[thread.pk].updated
+        except KeyError:
+            return False
+
+    def set_read(self, thread, post):
+        if self.request.user.is_authenticated() and post.date > self.cutoff:
+            try:
+                self.threads[thread.pk].updated = post.date
+                self.need_update = self.threads[thread.pk]
+            except KeyError:
+                self.need_create = thread
+
+    def unread_count(self, queryset=None):
+        try:
+            return self.unread_threads
+        except AttributeError:
+            self.unread_threads = 0
+            if queryset == None:
+                queryset = self.default_queryset()
+            for thread in queryset.filter(last__gte=self.record.cleared):
+                if not self.is_read(thread):
+                    self.unread_threads += 1
+            return self.unread_threads
+
+    def sync(self, queryset=None):
+        now = timezone.now()
+        if queryset == None:
+            queryset = self.default_queryset()
+
+        if self.need_create:
+            new_record = ThreadRead(
+                                    user=self.request.user,
+                                    thread=self.need_create,
+                                    forum=self.forum,
+                                    updated=now
+                                    )
+            new_record.save(force_insert=True)
+            self.threads[new_record.thread_id] = new_record
+
+        if self.need_update:
+            self.need_update.updated = now
+            self.need_update.save(force_update=True)
+
+        if self.need_create or self.need_update:
+            if not self.unread_count(queryset):
+                self.record.cleared = now
+            self.record.updated = now
+            if self.record.pk:
+                self.record.save(force_update=True)
+            else:
+                self.record.save(force_insert=True)
+
+    def default_queryset(self):
         return self.request.acl.threads.filter_threads(self.request, self.forum, self.forum.thread_set)
         return self.request.acl.threads.filter_threads(self.request, self.forum, self.forum.thread_set)

+ 88 - 88
misago/search.py

@@ -1,89 +1,89 @@
-from django.utils.translation import ugettext_lazy as _
-
-class SearchException(Exception):
-    def __init__(self, message=None, suggestion=None):
-        self.message = message
-        self.suggestion = suggestion
-
-    def __unicode__(self):
-         return self.message
-
-
-class SearchQuery(object):
-    def __init__(self, raw_query=None):
-        """
-        Build search query object
-        """
-        if raw_query:
-            self.parse_query(raw_query)
-        
-    def parse_query(self, raw_query):
-        """
-        Parse raw search query into dict of lists of words that should be found and cant be found in string
-        """
-        self.criteria = {'+': [], '-': []}
-        for word in unicode(raw_query).split():
-            # Trim word and skip it if its empty
-            word = unicode(word).strip().lower()
-            if len(word) == 0:
-                pass
-            
-            # Find word mode
-            mode = '+'
-            if word[0] == '-':
-                mode = '-'
-                word = unicode(word[1:]).strip()
-                
-            # Strip extra crap
-            word = ''.join(e for e in word if e.isalnum())
-            
-            # Slice word?
-            if len(word) <= 3:
-                raise SearchException(_("One or more search phrases are shorter than four characters."))
-            if mode == '+':
-                if len(word) == 5:
-                    word = word[0:-1]
-                if len(word) == 6:
-                    word = word[0:-2]
-                if len(word) > 6:
-                    word = word[0:-3]
-            self.criteria[mode].append(word)
-            
-        # Complain that there are no positive matches
-        if not self.criteria['+'] and not self.criteria['-']:
-            raise SearchException(_("Search query is invalid."))
-    
-    def search(self, value):
-        """
-        See if value meets search criteria, return True for success and False otherwhise
-        """
-        try:
-            value = unicode(value).strip().lower()
-            # Search for only
-            if self.criteria['+'] and not self.criteria['-']:
-               return self.search_for(value)
-            # Search against only
-            if self.criteria['-'] and not self.criteria['+']:
-               return self.search_against(value)
-            # Search if contains for values but not against values
-            return self.search_for(value) and not self.search_against(value)
-        except AttributeError:
-            raise SearchException(_("You have to define search query before you will be able to search."))
-        
-    def search_for(self, value):
-        """
-        See if value is required
-        """
-        for word in self.criteria['+']:
-            if value.find(word) != -1:
-                return True
-        return False
-        
-    def search_against(self, value):
-        """
-        See if value is forbidden
-        """
-        for word in self.criteria['-']:
-            if value.find(word) != -1:
-                return True
+from django.utils.translation import ugettext_lazy as _
+
+class SearchException(Exception):
+    def __init__(self, message=None, suggestion=None):
+        self.message = message
+        self.suggestion = suggestion
+
+    def __unicode__(self):
+         return self.message
+
+
+class SearchQuery(object):
+    def __init__(self, raw_query=None):
+        """
+        Build search query object
+        """
+        if raw_query:
+            self.parse_query(raw_query)
+        
+    def parse_query(self, raw_query):
+        """
+        Parse raw search query into dict of lists of words that should be found and cant be found in string
+        """
+        self.criteria = {'+': [], '-': []}
+        for word in unicode(raw_query).split():
+            # Trim word and skip it if its empty
+            word = unicode(word).strip().lower()
+            if len(word) == 0:
+                pass
+            
+            # Find word mode
+            mode = '+'
+            if word[0] == '-':
+                mode = '-'
+                word = unicode(word[1:]).strip()
+                
+            # Strip extra crap
+            word = ''.join(e for e in word if e.isalnum())
+            
+            # Slice word?
+            if len(word) <= 3:
+                raise SearchException(_("One or more search phrases are shorter than four characters."))
+            if mode == '+':
+                if len(word) == 5:
+                    word = word[0:-1]
+                if len(word) == 6:
+                    word = word[0:-2]
+                if len(word) > 6:
+                    word = word[0:-3]
+            self.criteria[mode].append(word)
+            
+        # Complain that there are no positive matches
+        if not self.criteria['+'] and not self.criteria['-']:
+            raise SearchException(_("Search query is invalid."))
+    
+    def search(self, value):
+        """
+        See if value meets search criteria, return True for success and False otherwhise
+        """
+        try:
+            value = unicode(value).strip().lower()
+            # Search for only
+            if self.criteria['+'] and not self.criteria['-']:
+               return self.search_for(value)
+            # Search against only
+            if self.criteria['-'] and not self.criteria['+']:
+               return self.search_against(value)
+            # Search if contains for values but not against values
+            return self.search_for(value) and not self.search_against(value)
+        except AttributeError:
+            raise SearchException(_("You have to define search query before you will be able to search."))
+        
+    def search_for(self, value):
+        """
+        See if value is required
+        """
+        for word in self.criteria['+']:
+            if value.find(word) != -1:
+                return True
+        return False
+        
+    def search_against(self, value):
+        """
+        See if value is forbidden
+        """
+        for word in self.criteria['-']:
+            if value.find(word) != -1:
+                return True
         return False
         return False

+ 40 - 40
misago/search_indexes.py

@@ -1,40 +1,40 @@
-from haystack import indexes
-from misago.models import Post
-
-class PostIndex(indexes.SearchIndex, indexes.Indexable):
-    text = indexes.CharField(document=True, use_template=True)
-    forum = indexes.IntegerField(model_attr='forum_id')
-    thread = indexes.IntegerField(model_attr='thread_id')
-    thread_name = indexes.CharField()
-    start_post = indexes.IntegerField()
-    thread_starter = indexes.IntegerField(default=0)
-    username = indexes.CharField(model_attr='user_name')
-    date = indexes.DateTimeField(model_attr='date')
-
-    def get_model(self):
-        return Post
-
-    def prepare_thread_name(self, obj):
-        return obj.thread.name
-
-    def prepare_start_post(self, obj):
-        return 1 if obj.thread.start_post_id == obj.pk else 0
-
-    def prepare_thread_starter(self, obj):
-        return obj.thread.start_poster_id or 0
-
-    def get_updated_field(self):
-        return 'current_date'
-
-    def should_update(self, instance, **kwargs):
-        if (instance.deleted or instance.moderated
-                or instance.thread.deleted or instance.thread.moderated):
-            self.remove_object(instance, **kwargs)
-            return False
-        return True
-
-    def read_queryset(self, using=None):
-        return Post.objects.all().select_related('forum', 'thread', 'user')
-
-    def index_queryset(self, using=None):
-        return self.get_model().objects.all().select_related('thread')
+from haystack import indexes
+from misago.models import Post
+
+class PostIndex(indexes.SearchIndex, indexes.Indexable):
+    text = indexes.CharField(document=True, use_template=True)
+    forum = indexes.IntegerField(model_attr='forum_id')
+    thread = indexes.IntegerField(model_attr='thread_id')
+    thread_name = indexes.CharField()
+    start_post = indexes.IntegerField()
+    thread_starter = indexes.IntegerField(default=0)
+    username = indexes.CharField(model_attr='user_name')
+    date = indexes.DateTimeField(model_attr='date')
+
+    def get_model(self):
+        return Post
+
+    def prepare_thread_name(self, obj):
+        return obj.thread.name
+
+    def prepare_start_post(self, obj):
+        return 1 if obj.thread.start_post_id == obj.pk else 0
+
+    def prepare_thread_starter(self, obj):
+        return obj.thread.start_poster_id or 0
+
+    def get_updated_field(self):
+        return 'current_date'
+
+    def should_update(self, instance, **kwargs):
+        if (instance.deleted or instance.moderated
+                or instance.thread.deleted or instance.thread.moderated):
+            self.remove_object(instance, **kwargs)
+            return False
+        return True
+
+    def read_queryset(self, using=None):
+        return Post.objects.all().select_related('forum', 'thread', 'user')
+
+    def index_queryset(self, using=None):
+        return self.get_model().objects.all().select_related('thread')

+ 261 - 261
misago/sessions.py

@@ -1,262 +1,262 @@
-from hashlib import md5
-from datetime import timedelta
-from django.contrib.sessions.backends.base import SessionBase, CreateError
-from django.db import IntegrityError
-from django.db.models.loading import cache as model_cache
-from django.utils import timezone
-from django.utils.crypto import salted_hmac
-from django.utils.encoding import force_unicode
-from misago.auth import auth_remember, AuthException
-from misago.conf import settings
-from misago.models import Session, Token, Guest, User
-from misago.utils.strings import random_string
-
-# Assert models are loaded
-if not model_cache.loaded:
-    model_cache.get_models()
-
-
-class IncorrectSessionException(Exception):
-    pass
-
-
-class MisagoSession(SessionBase):
-    """
-    Abstract class for sessions to inherit and extend
-    """
-    def _get_new_session_key(self):
-        return random_string(42)
-
-    def _get_session(self):
-        try:
-            return self._session_cache
-        except AttributeError:
-            self._session_cache = self.load()
-        return self._session_cache
-
-    def _hash(self, value):
-        key_salt = "misago.sessions" + self.__class__.__name__
-        return salted_hmac(key_salt, value).hexdigest()
-
-    def delete(self):
-        """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
-        pass
-
-    def created(self):
-        try:
-            return self.started
-        except AttributeError:
-            return False
-
-    def flush(self):
-        """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
-        pass
-
-    def load(self):
-        return self.decode(force_unicode(self._session_rk.data))
-
-    def session_expired(self):
-        return False
-
-    def get_ip(self, request):
-        return request.META.get('HTTP_X_FORWARDED_FOR', '') or request.META.get('REMOTE_ADDR')
-
-    def set_user(self, user=None):
-        pass
-
-    def get_ban(self):
-        return False
-
-    def set_ban(self, ban):
-        return False
-
-    def save(self):
-        self._session_rk.data = self.encode(self._get_session())
-        self._session_rk.last = timezone.now()
-        if self._session_rk.pk:
-            self._session_rk.save(force_update=True)
-        else:
-            self._session_rk.save(force_insert=True)
-
-    def match(self):
-        self._session_rk.matched = True
-
-
-class CrawlerSession(MisagoSession):
-    """
-    Crawler Session controller
-    """
-    def __init__(self, request):
-        self.matched = True
-        self.started = False
-        self.team = False
-        self._ip = self.get_ip(request)
-        self._session_key = md5('%s-%s' % (request.user.username, self._ip)).hexdigest()
-        try:
-            self._session_rk = Session.objects.get(id=self._session_key)
-            self._session_key = self._session_rk.id
-        except Session.DoesNotExist:
-            self.create(request)
-
-    def create(self, request):
-        self._session_rk = Session(
-                                   id=self._session_key,
-                                   data=self.encode({}),
-                                   crawler=request.user.username,
-                                   ip=self._ip,
-                                   agent=request.META.get('HTTP_USER_AGENT', ''),
-                                   start=timezone.now(),
-                                   last=timezone.now(),
-                                   matched=True
-                                   )
-        while True:
-            try:
-                self._session_rk.save(force_insert=True)
-                break
-            except CreateError:
-                continue
-            except IntegrityError:
-                try:
-                    self._session_rk =  Session.objects.get(id=self._session_key)                    
-                except Session.DoesNotExist:
-                    continue
-
-    def human_session(self):
-        return False
-
-
-class HumanSession(MisagoSession):
-    """
-    Human Session controller
-    """
-    def __init__(self, request):
-        self.started = False
-        self.matched = False
-        self.expired = False
-        self.team = False
-        self.rank = None
-        self.remember_me = None
-        self._user = None
-        self._ip = self.get_ip(request)
-        self._session_token = None
-        if request.firewall.admin:
-            self._cookie_sid = settings.COOKIES_PREFIX + 'ASID'
-        else:
-            self._cookie_sid = settings.COOKIES_PREFIX + 'SID'
-        try:
-            # Do we have correct session ID?
-            if self._cookie_sid not in request.COOKIES or len(request.COOKIES[self._cookie_sid]) != 42:
-                raise IncorrectSessionException()
-            self._session_key = request.COOKIES[self._cookie_sid]
-            self._session_rk = Session.objects.select_related('user', 'rank')
-            if settings.USER_EXTENSIONS_PRELOAD:
-                self._session_rk = self._session_rk.select_related(*settings.USER_EXTENSIONS_PRELOAD)
-            self._session_rk = self._session_rk.get(
-                                                    pk=self._session_key,
-                                                    admin=request.firewall.admin
-                                                    )
-            # IP invalid
-            if settings.sessions_validate_ip and self._session_rk.ip != self._ip:
-                raise IncorrectSessionException()
-            
-            # Session expired
-            if timezone.now() - self._session_rk.last > timedelta(seconds=settings.SESSION_LIFETIME):
-                self.expired = True
-                raise IncorrectSessionException()
-            
-            # Change session to matched and extract session user
-            if self._session_rk.matched:
-                self.matched = True
-            else:
-                self.started = True
-            self._user = self._session_rk.user
-            self.team = self._session_rk.team
-        except (Session.DoesNotExist, IncorrectSessionException):
-            # Attempt autolog
-            try:
-                self.remember_me = auth_remember(request, self.get_ip(request))
-                self.create(request, user=self.remember_me.user)
-                self.started = True
-                self._session_rk.matched = True
-            except AuthException as e:
-                # Autolog failed
-                self.create(request)
-        self.id = self._session_rk.id
-
-        # Make cookie live longer
-        if request.firewall.admin:
-            request.cookiejar.set('ASID', self._session_rk.id)
-        else:
-            request.cookiejar.set('SID', self._session_rk.id)
-
-    def create(self, request, user=None):
-        self._user = user
-        while True:
-            try:
-                self._session_key = self._get_new_session_key()
-                self._session_rk = Session(
-                                         id=self._session_key,
-                                         data=self.encode({}),
-                                         user=self._user,
-                                         ip=self._ip,
-                                         agent=request.META.get('HTTP_USER_AGENT', ''),
-                                         start=timezone.now(),
-                                         last=timezone.now(),
-                                         admin=request.firewall.admin,
-                                         )
-                self._session_rk.save(force_insert=True)
-                if settings.USER_EXTENSIONS_PRELOAD:
-                    self._session_rk = self._session_rk.select_related(*settings.USER_EXTENSIONS_PRELOAD)
-                if user:
-                    # Update user data
-                    user.set_last_visit(
-                                        self.get_ip(request),
-                                        request.META.get('HTTP_USER_AGENT', '')
-                                        )
-                    user.save(force_update=True)
-                break
-            except CreateError:
-                # Key wasn't unique. Try again.
-                continue
-
-    def save(self):
-        self._session_rk.user = self._user
-        self._session_rk.team = self.team
-        self._session_rk.rank_id = self.rank
-        super(HumanSession, self).save()
-
-    def human_session(self):
-        return True
-
-    def session_expired(self):
-        return self.expired
-
-    def get_user(self):
-        if self._user == None:
-            return Guest()
-        return self._user
-
-    def set_user(self, user=None):
-        self._user = user
-
-    def sign_out(self, request):
-        try:
-            if self._user.is_authenticated():
-                if not request.firewall.admin:
-                    cookie_token = settings.COOKIES_PREFIX + 'TOKEN'
-                    if cookie_token in request.COOKIES:
-                        if len(request.COOKIES[cookie_token]) > 0:
-                            Token.objects.filter(id=request.COOKIES[cookie_token]).delete()
-                        request.cookiejar.delete('TOKEN')
-                self._user = None
-                request.user = Guest()
-        except AttributeError:
-            pass
-
-
-class SessionMock(object):
-    def get_ip(self, request):
-        try:
-            return self.ip
-        except AttributeError:
+from hashlib import md5
+from datetime import timedelta
+from django.contrib.sessions.backends.base import SessionBase, CreateError
+from django.db import IntegrityError
+from django.db.models.loading import cache as model_cache
+from django.utils import timezone
+from django.utils.crypto import salted_hmac
+from django.utils.encoding import force_unicode
+from misago.auth import auth_remember, AuthException
+from misago.conf import settings
+from misago.models import Session, Token, Guest, User
+from misago.utils.strings import random_string
+
+# Assert models are loaded
+if not model_cache.loaded:
+    model_cache.get_models()
+
+
+class IncorrectSessionException(Exception):
+    pass
+
+
+class MisagoSession(SessionBase):
+    """
+    Abstract class for sessions to inherit and extend
+    """
+    def _get_new_session_key(self):
+        return random_string(42)
+
+    def _get_session(self):
+        try:
+            return self._session_cache
+        except AttributeError:
+            self._session_cache = self.load()
+        return self._session_cache
+
+    def _hash(self, value):
+        key_salt = "misago.sessions" + self.__class__.__name__
+        return salted_hmac(key_salt, value).hexdigest()
+
+    def delete(self):
+        """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
+        pass
+
+    def created(self):
+        try:
+            return self.started
+        except AttributeError:
+            return False
+
+    def flush(self):
+        """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
+        pass
+
+    def load(self):
+        return self.decode(force_unicode(self._session_rk.data))
+
+    def session_expired(self):
+        return False
+
+    def get_ip(self, request):
+        return request.META.get('HTTP_X_FORWARDED_FOR', '') or request.META.get('REMOTE_ADDR')
+
+    def set_user(self, user=None):
+        pass
+
+    def get_ban(self):
+        return False
+
+    def set_ban(self, ban):
+        return False
+
+    def save(self):
+        self._session_rk.data = self.encode(self._get_session())
+        self._session_rk.last = timezone.now()
+        if self._session_rk.pk:
+            self._session_rk.save(force_update=True)
+        else:
+            self._session_rk.save(force_insert=True)
+
+    def match(self):
+        self._session_rk.matched = True
+
+
+class CrawlerSession(MisagoSession):
+    """
+    Crawler Session controller
+    """
+    def __init__(self, request):
+        self.matched = True
+        self.started = False
+        self.team = False
+        self._ip = self.get_ip(request)
+        self._session_key = md5('%s-%s' % (request.user.username, self._ip)).hexdigest()
+        try:
+            self._session_rk = Session.objects.get(id=self._session_key)
+            self._session_key = self._session_rk.id
+        except Session.DoesNotExist:
+            self.create(request)
+
+    def create(self, request):
+        self._session_rk = Session(
+                                   id=self._session_key,
+                                   data=self.encode({}),
+                                   crawler=request.user.username,
+                                   ip=self._ip,
+                                   agent=request.META.get('HTTP_USER_AGENT', ''),
+                                   start=timezone.now(),
+                                   last=timezone.now(),
+                                   matched=True
+                                   )
+        while True:
+            try:
+                self._session_rk.save(force_insert=True)
+                break
+            except CreateError:
+                continue
+            except IntegrityError:
+                try:
+                    self._session_rk =  Session.objects.get(id=self._session_key)                    
+                except Session.DoesNotExist:
+                    continue
+
+    def human_session(self):
+        return False
+
+
+class HumanSession(MisagoSession):
+    """
+    Human Session controller
+    """
+    def __init__(self, request):
+        self.started = False
+        self.matched = False
+        self.expired = False
+        self.team = False
+        self.rank = None
+        self.remember_me = None
+        self._user = None
+        self._ip = self.get_ip(request)
+        self._session_token = None
+        if request.firewall.admin:
+            self._cookie_sid = settings.COOKIES_PREFIX + 'ASID'
+        else:
+            self._cookie_sid = settings.COOKIES_PREFIX + 'SID'
+        try:
+            # Do we have correct session ID?
+            if self._cookie_sid not in request.COOKIES or len(request.COOKIES[self._cookie_sid]) != 42:
+                raise IncorrectSessionException()
+            self._session_key = request.COOKIES[self._cookie_sid]
+            self._session_rk = Session.objects.select_related('user', 'rank')
+            if settings.USER_EXTENSIONS_PRELOAD:
+                self._session_rk = self._session_rk.select_related(*settings.USER_EXTENSIONS_PRELOAD)
+            self._session_rk = self._session_rk.get(
+                                                    pk=self._session_key,
+                                                    admin=request.firewall.admin
+                                                    )
+            # IP invalid
+            if settings.sessions_validate_ip and self._session_rk.ip != self._ip:
+                raise IncorrectSessionException()
+            
+            # Session expired
+            if timezone.now() - self._session_rk.last > timedelta(seconds=settings.SESSION_LIFETIME):
+                self.expired = True
+                raise IncorrectSessionException()
+            
+            # Change session to matched and extract session user
+            if self._session_rk.matched:
+                self.matched = True
+            else:
+                self.started = True
+            self._user = self._session_rk.user
+            self.team = self._session_rk.team
+        except (Session.DoesNotExist, IncorrectSessionException):
+            # Attempt autolog
+            try:
+                self.remember_me = auth_remember(request, self.get_ip(request))
+                self.create(request, user=self.remember_me.user)
+                self.started = True
+                self._session_rk.matched = True
+            except AuthException as e:
+                # Autolog failed
+                self.create(request)
+        self.id = self._session_rk.id
+
+        # Make cookie live longer
+        if request.firewall.admin:
+            request.cookiejar.set('ASID', self._session_rk.id)
+        else:
+            request.cookiejar.set('SID', self._session_rk.id)
+
+    def create(self, request, user=None):
+        self._user = user
+        while True:
+            try:
+                self._session_key = self._get_new_session_key()
+                self._session_rk = Session(
+                                         id=self._session_key,
+                                         data=self.encode({}),
+                                         user=self._user,
+                                         ip=self._ip,
+                                         agent=request.META.get('HTTP_USER_AGENT', ''),
+                                         start=timezone.now(),
+                                         last=timezone.now(),
+                                         admin=request.firewall.admin,
+                                         )
+                self._session_rk.save(force_insert=True)
+                if settings.USER_EXTENSIONS_PRELOAD:
+                    self._session_rk = self._session_rk.select_related(*settings.USER_EXTENSIONS_PRELOAD)
+                if user:
+                    # Update user data
+                    user.set_last_visit(
+                                        self.get_ip(request),
+                                        request.META.get('HTTP_USER_AGENT', '')
+                                        )
+                    user.save(force_update=True)
+                break
+            except CreateError:
+                # Key wasn't unique. Try again.
+                continue
+
+    def save(self):
+        self._session_rk.user = self._user
+        self._session_rk.team = self.team
+        self._session_rk.rank_id = self.rank
+        super(HumanSession, self).save()
+
+    def human_session(self):
+        return True
+
+    def session_expired(self):
+        return self.expired
+
+    def get_user(self):
+        if self._user == None:
+            return Guest()
+        return self._user
+
+    def set_user(self, user=None):
+        self._user = user
+
+    def sign_out(self, request):
+        try:
+            if self._user.is_authenticated():
+                if not request.firewall.admin:
+                    cookie_token = settings.COOKIES_PREFIX + 'TOKEN'
+                    if cookie_token in request.COOKIES:
+                        if len(request.COOKIES[cookie_token]) > 0:
+                            Token.objects.filter(id=request.COOKIES[cookie_token]).delete()
+                        request.cookiejar.delete('TOKEN')
+                self._user = None
+                request.user = Guest()
+        except AttributeError:
+            pass
+
+
+class SessionMock(object):
+    def get_ip(self, request):
+        try:
+            return self.ip
+        except AttributeError:
             return '127.0.0.1'
             return '127.0.0.1'

+ 261 - 261
misago/settings_base.py

@@ -1,261 +1,261 @@
-import os
-
-# Board address
-BOARD_ADDRESS = 'http://127.0.0.1:8000/'
-
-# Allowed hosts
-ALLOWED_HOSTS = ['*']
-
-# Admin control panel path
-# Leave this setting empty
-ADMIN_PATH = ''
-
-# Enable mobile subdomain for mobile stuff
-MOBILE_SUBDOMAIN = ''
-
-# Templates used by mobile version
-MOBILE_TEMPLATES = ''
-
-# Default format of Misago generated HTML
-OUTPUT_FORMAT = 'html5'
-
-# Default avatar sizes
-# Those are avatar sizes Misago generates images for
-# Remember to run "genavatars" command when you change this setting!
-AVATAR_SIZES = (125, 100, 80, 60, 40, 24)
-
-# Allow usernames to contain diacritics
-UNICODE_USERNAMES = True
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# If you set this to False, Django will not format dates, numbers and
-# calendars according to the current locale.
-USE_L10N = True
-
-# If you set this to False, Django will not use timezone-aware datetimes.
-USE_TZ = True
-
-# List of directories that contain Misago locale files
-# Defautly set Django to look for Misago translations in misago/locale directory
-LOCALE_PATHS = (
-    ('%slocale%s' % (os.path.dirname(__file__) + os.sep, os.sep)),
-)
-
-# Catch-all e-mail address
-# If DEBUG_MODE is on, all emails will be sent to this address instead of real recipient.
-CATCH_ALL_EMAIL_ADDRESS = ''
-
-# Forums and threads read tracker length (days)
-# Enter 0 to turn tracking off
-# The bigger the number, then longer tracker keeps threads reads
-# information and the more costful it is to track reads
-READS_TRACKER_LENGTH = 7
-
-# Min. number of days between synchronisating member profiles
-# Allows you to keep your member profiles up to date, enter 0 to never sync
-PROFILES_SYNC_FREQUENCY = 7
-
-# Heartbeat Path for crons
-# Use this path if you wish to keep Misago alive using separate cron
-# By quering this path from your cron you'll keep Misago's base clean
-# Leave empty if you don't use Heartbeat cron
-HEARTBEAT_PATH = ''
-
-# List of finder classes that know how to find static files in
-# various locations.
-STATICFILES_FINDERS = (
-    'django.contrib.staticfiles.finders.FileSystemFinder',
-    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
-)
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django_jinja.loaders.AppLoader',
-    'django_jinja.loaders.FileSystemLoader',
-)
-
-# Template extensions that will cause Jinja2 to be used
-DEFAULT_JINJA2_TEMPLATE_EXTENSION = ('.html', '.txt')
-
-# Context processors
-TEMPLATE_CONTEXT_PROCESSORS = (
-    'django.core.context_processors.debug',
-    'django.core.context_processors.i18n',
-    'django.core.context_processors.media',
-    'django.core.context_processors.static',
-    'django.core.context_processors.tz',
-    'django.contrib.messages.context_processors.messages',
-    'misago.context_processors.common',
-    'misago.context_processors.admin',
-)
-
-# Template middlewares
-TEMPLATE_MIDDLEWARES = ()
-
-# Jinja2 Template Extensions
-JINJA2_EXTENSIONS = (
-    'jinja2.ext.do',
-)
-
-# List of application middlewares
-MIDDLEWARE_CLASSES = (
-    'misago.middleware.thread.ThreadMiddleware',
-    'misago.middleware.stopwatch.StopwatchMiddleware',
-    'misago.middleware.heartbeat.HeartbeatMiddleware',
-    'debug_toolbar.middleware.DebugToolbarMiddleware',
-    'misago.middleware.cookiejar.CookieJarMiddleware',
-    'misago.middleware.theme.ThemeMiddleware',
-    'misago.middleware.firewalls.FirewallMiddleware',
-    'misago.middleware.crawlers.DetectCrawlerMiddleware',
-    'misago.middleware.session.SessionMiddleware',
-    'misago.middleware.bruteforce.JamMiddleware',
-    'misago.middleware.csrf.CSRFMiddleware',
-    'misago.middleware.banning.BanningMiddleware',
-    'misago.middleware.messages.MessagesMiddleware',
-    'misago.middleware.user.UserMiddleware',
-    'misago.middleware.mailsqueue.MailsQueueMiddleware',
-    'misago.middleware.acl.ACLMiddleware',
-    'misago.middleware.privatethreads.PrivateThreadsMiddleware',
-    'django.middleware.common.CommonMiddleware',
-)
-
-# List of application permission providers
-PERMISSION_PROVIDERS = (
-    'misago.acl.permissions.usercp',
-    'misago.acl.permissions.search',
-    'misago.acl.permissions.users',
-    'misago.acl.permissions.forums',
-    'misago.acl.permissions.threads',
-    'misago.acl.permissions.privatethreads',
-    'misago.acl.permissions.reports',
-    'misago.acl.permissions.special',
-)
-
-# List of UserCP extensions
-USERCP_EXTENSIONS = (
-    'misago.apps.usercp.options',
-    'misago.apps.usercp.avatar',
-    'misago.apps.usercp.signature',
-    'misago.apps.usercp.credentials',
-    'misago.apps.usercp.username',
-)
-
-# List of User Profile extensions
-PROFILE_EXTENSIONS = (
-    'misago.apps.profiles.posts',
-    'misago.apps.profiles.threads',
-    'misago.apps.profiles.follows',
-    'misago.apps.profiles.followers',
-    'misago.apps.profiles.details',
-)
-
-# List of User Model relations that should be loaded by session handler
-USER_EXTENSIONS_PRELOAD = ()
-
-# List of User Model relations that should be loaded when displaying users profiles
-PROFILE_EXTENSIONS_PRELOAD = ()
-
-# List of Markdown Extensions
-MARKDOWN_EXTENSIONS = (
-    'misago.markdown.extensions.strikethrough.StrikethroughExtension',
-    'misago.markdown.extensions.quotes.QuoteTitlesExtension',
-    'misago.markdown.extensions.mentions.MentionsExtension',
-    'misago.markdown.extensions.magiclinks.MagicLinksExtension',
-    'misago.markdown.extensions.cleanlinks.CleanLinksExtension',
-    'misago.markdown.extensions.shorthandimgs.ShorthandImagesExtension',
-    # Uncomment for EXPERIMENTAL BBCode support
-    #'misago.markdown.extensions.bbcodes.BBCodesExtension',
-    # Uncomment for emoji support, requires emoji directory in static dir.
-    #'misago.markdown.extensions.emoji.EmojiExtension',
-)
-
-# Name of root urls configuration
-ROOT_URLCONF = 'misago.urls'
-
-#Installed applications
-INSTALLED_APPS = (
-    # Applications that have no dependencies first!
-    'south', # Database schema building and updating
-    'django.contrib.staticfiles',
-    'django.contrib.humanize',
-    'django_jinja', # Jinja2 integration
-    'django_jinja.contrib.humanize', # Some Django filters    
-    'floppyforms', # Better forms
-    'mptt', # Modified Pre-order Tree Transversal - allows us to nest forums 
-    'haystack', # Search engines bridge
-    'debug_toolbar', # Debug toolbar'
-    'misago', # Misago Forum App
-)
-
-# Stopwatch target file
-STOPWATCH_LOG = ''
-
-# IP's that can see debug toolbar
-INTERNAL_IPS = ('127.0.0.1', '::1')
-
-# Debug toolbar config
-DEBUG_TOOLBAR_CONFIG = {
-    'INTERCEPT_REDIRECTS': False
-}
-
-# List panels displayed by toolbar
-DEBUG_TOOLBAR_PANELS = (
-    'debug_toolbar.panels.timer.TimerDebugPanel',
-    'debug_toolbar.panels.sql.SQLDebugPanel',
-    'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
-    'misago.acl.panels.MisagoACLDebugPanel',
-    'debug_toolbar.panels.headers.HeaderDebugPanel',
-    'debug_toolbar.panels.template.TemplateDebugPanel',
-    'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
-    'debug_toolbar.panels.signals.SignalDebugPanel',
-    'debug_toolbar.panels.logger.LoggingPanel',
-    'debug_toolbar.panels.version.VersionDebugPanel',
-)
-
-# Turn off caching
-CACHES = {
-    'default': {
-        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
-    }
-}
-
-# A sample logging configuration. The only tangible logging
-# performed by this configuration is to send an email to
-# the site admins on every HTTP 500 error when DEBUG=False.
-# See http://docs.djangoproject.com/en/dev/topics/logging for
-# more details on how to customize your logging configuration.
-LOGGING = {
-    'version': 1,
-    'disable_existing_loggers': False,
-    'filters': {
-        'require_debug_false': {
-            '()': 'django.utils.log.RequireDebugFalse'
-        }
-    },
-    'handlers': {
-        'mail_admins': {
-            'level': 'ERROR',
-            'filters': ['require_debug_false'],
-            'class': 'django.utils.log.AdminEmailHandler'
-        }
-    },
-    'loggers': {
-        'django.request': {
-            'handlers': ['mail_admins'],
-            'level': 'ERROR',
-            'propagate': True,
-        },
-    }
-}
-
-# Create copy of installed apps list
-# South overrides INSTALLED_APPS list with custom one
-# that doesn't contain apps without models when it
-# runs its own syncdb
-# Misago's loaddata command requires complete list of
-# installed apps in order to work correctly
-import copy
-INSTALLED_APPS_COMPLETE = copy.copy(INSTALLED_APPS)
+import os
+
+# Board address
+BOARD_ADDRESS = 'http://127.0.0.1:8000/'
+
+# Allowed hosts
+ALLOWED_HOSTS = ['*']
+
+# Admin control panel path
+# Leave this setting empty
+ADMIN_PATH = ''
+
+# Enable mobile subdomain for mobile stuff
+MOBILE_SUBDOMAIN = ''
+
+# Templates used by mobile version
+MOBILE_TEMPLATES = ''
+
+# Default format of Misago generated HTML
+OUTPUT_FORMAT = 'html5'
+
+# Default avatar sizes
+# Those are avatar sizes Misago generates images for
+# Remember to run "genavatars" command when you change this setting!
+AVATAR_SIZES = (125, 100, 80, 60, 40, 24)
+
+# Allow usernames to contain diacritics
+UNICODE_USERNAMES = True
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale.
+USE_L10N = True
+
+# If you set this to False, Django will not use timezone-aware datetimes.
+USE_TZ = True
+
+# List of directories that contain Misago locale files
+# Defautly set Django to look for Misago translations in misago/locale directory
+LOCALE_PATHS = (
+    ('%slocale%s' % (os.path.dirname(__file__) + os.sep, os.sep)),
+)
+
+# Catch-all e-mail address
+# If DEBUG_MODE is on, all emails will be sent to this address instead of real recipient.
+CATCH_ALL_EMAIL_ADDRESS = ''
+
+# Forums and threads read tracker length (days)
+# Enter 0 to turn tracking off
+# The bigger the number, then longer tracker keeps threads reads
+# information and the more costful it is to track reads
+READS_TRACKER_LENGTH = 7
+
+# Min. number of days between synchronisating member profiles
+# Allows you to keep your member profiles up to date, enter 0 to never sync
+PROFILES_SYNC_FREQUENCY = 7
+
+# Heartbeat Path for crons
+# Use this path if you wish to keep Misago alive using separate cron
+# By quering this path from your cron you'll keep Misago's base clean
+# Leave empty if you don't use Heartbeat cron
+HEARTBEAT_PATH = ''
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+    'django.contrib.staticfiles.finders.FileSystemFinder',
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+)
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django_jinja.loaders.AppLoader',
+    'django_jinja.loaders.FileSystemLoader',
+)
+
+# Template extensions that will cause Jinja2 to be used
+DEFAULT_JINJA2_TEMPLATE_EXTENSION = ('.html', '.txt')
+
+# Context processors
+TEMPLATE_CONTEXT_PROCESSORS = (
+    'django.core.context_processors.debug',
+    'django.core.context_processors.i18n',
+    'django.core.context_processors.media',
+    'django.core.context_processors.static',
+    'django.core.context_processors.tz',
+    'django.contrib.messages.context_processors.messages',
+    'misago.context_processors.common',
+    'misago.context_processors.admin',
+)
+
+# Template middlewares
+TEMPLATE_MIDDLEWARES = ()
+
+# Jinja2 Template Extensions
+JINJA2_EXTENSIONS = (
+    'jinja2.ext.do',
+)
+
+# List of application middlewares
+MIDDLEWARE_CLASSES = (
+    'misago.middleware.thread.ThreadMiddleware',
+    'misago.middleware.stopwatch.StopwatchMiddleware',
+    'misago.middleware.heartbeat.HeartbeatMiddleware',
+    'debug_toolbar.middleware.DebugToolbarMiddleware',
+    'misago.middleware.cookiejar.CookieJarMiddleware',
+    'misago.middleware.theme.ThemeMiddleware',
+    'misago.middleware.firewalls.FirewallMiddleware',
+    'misago.middleware.crawlers.DetectCrawlerMiddleware',
+    'misago.middleware.session.SessionMiddleware',
+    'misago.middleware.bruteforce.JamMiddleware',
+    'misago.middleware.csrf.CSRFMiddleware',
+    'misago.middleware.banning.BanningMiddleware',
+    'misago.middleware.messages.MessagesMiddleware',
+    'misago.middleware.user.UserMiddleware',
+    'misago.middleware.mailsqueue.MailsQueueMiddleware',
+    'misago.middleware.acl.ACLMiddleware',
+    'misago.middleware.privatethreads.PrivateThreadsMiddleware',
+    'django.middleware.common.CommonMiddleware',
+)
+
+# List of application permission providers
+PERMISSION_PROVIDERS = (
+    'misago.acl.permissions.usercp',
+    'misago.acl.permissions.search',
+    'misago.acl.permissions.users',
+    'misago.acl.permissions.forums',
+    'misago.acl.permissions.threads',
+    'misago.acl.permissions.privatethreads',
+    'misago.acl.permissions.reports',
+    'misago.acl.permissions.special',
+)
+
+# List of UserCP extensions
+USERCP_EXTENSIONS = (
+    'misago.apps.usercp.options',
+    'misago.apps.usercp.avatar',
+    'misago.apps.usercp.signature',
+    'misago.apps.usercp.credentials',
+    'misago.apps.usercp.username',
+)
+
+# List of User Profile extensions
+PROFILE_EXTENSIONS = (
+    'misago.apps.profiles.posts',
+    'misago.apps.profiles.threads',
+    'misago.apps.profiles.follows',
+    'misago.apps.profiles.followers',
+    'misago.apps.profiles.details',
+)
+
+# List of User Model relations that should be loaded by session handler
+USER_EXTENSIONS_PRELOAD = ()
+
+# List of User Model relations that should be loaded when displaying users profiles
+PROFILE_EXTENSIONS_PRELOAD = ()
+
+# List of Markdown Extensions
+MARKDOWN_EXTENSIONS = (
+    'misago.markdown.extensions.strikethrough.StrikethroughExtension',
+    'misago.markdown.extensions.quotes.QuoteTitlesExtension',
+    'misago.markdown.extensions.mentions.MentionsExtension',
+    'misago.markdown.extensions.magiclinks.MagicLinksExtension',
+    'misago.markdown.extensions.cleanlinks.CleanLinksExtension',
+    'misago.markdown.extensions.shorthandimgs.ShorthandImagesExtension',
+    # Uncomment for EXPERIMENTAL BBCode support
+    #'misago.markdown.extensions.bbcodes.BBCodesExtension',
+    # Uncomment for emoji support, requires emoji directory in static dir.
+    #'misago.markdown.extensions.emoji.EmojiExtension',
+)
+
+# Name of root urls configuration
+ROOT_URLCONF = 'misago.urls'
+
+#Installed applications
+INSTALLED_APPS = (
+    # Applications that have no dependencies first!
+    'south', # Database schema building and updating
+    'django.contrib.staticfiles',
+    'django.contrib.humanize',
+    'django_jinja', # Jinja2 integration
+    'django_jinja.contrib.humanize', # Some Django filters    
+    'floppyforms', # Better forms
+    'mptt', # Modified Pre-order Tree Transversal - allows us to nest forums 
+    'haystack', # Search engines bridge
+    'debug_toolbar', # Debug toolbar'
+    'misago', # Misago Forum App
+)
+
+# Stopwatch target file
+STOPWATCH_LOG = ''
+
+# IP's that can see debug toolbar
+INTERNAL_IPS = ('127.0.0.1', '::1')
+
+# Debug toolbar config
+DEBUG_TOOLBAR_CONFIG = {
+    'INTERCEPT_REDIRECTS': False
+}
+
+# List panels displayed by toolbar
+DEBUG_TOOLBAR_PANELS = (
+    'debug_toolbar.panels.timer.TimerDebugPanel',
+    'debug_toolbar.panels.sql.SQLDebugPanel',
+    'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
+    'misago.acl.panels.MisagoACLDebugPanel',
+    'debug_toolbar.panels.headers.HeaderDebugPanel',
+    'debug_toolbar.panels.template.TemplateDebugPanel',
+    'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
+    'debug_toolbar.panels.signals.SignalDebugPanel',
+    'debug_toolbar.panels.logger.LoggingPanel',
+    'debug_toolbar.panels.version.VersionDebugPanel',
+)
+
+# Turn off caching
+CACHES = {
+    'default': {
+        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
+    }
+}
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error when DEBUG=False.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'filters': {
+        'require_debug_false': {
+            '()': 'django.utils.log.RequireDebugFalse'
+        }
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'filters': ['require_debug_false'],
+            'class': 'django.utils.log.AdminEmailHandler'
+        }
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['mail_admins'],
+            'level': 'ERROR',
+            'propagate': True,
+        },
+    }
+}
+
+# Create copy of installed apps list
+# South overrides INSTALLED_APPS list with custom one
+# that doesn't contain apps without models when it
+# runs its own syncdb
+# Misago's loaddata command requires complete list of
+# installed apps in order to work correctly
+import copy
+INSTALLED_APPS_COMPLETE = copy.copy(INSTALLED_APPS)

+ 16 - 16
misago/shortcuts.py

@@ -1,16 +1,16 @@
-from django.shortcuts import (redirect, render as django_render,
-                              render_to_response as django_render_to_response)
-from misago.template.middlewares import process_context
-from misago.template.theme import prefix_templates
-from misago.utils.views import redirect_message, json_response
-
-def render(request, template_name, dictionary=None, **kwargs):
-    dictionary = process_context(template_name, dictionary, kwargs.pop('context_instance', None))
-    template_name = prefix_templates(template_name, dictionary)
-    return django_render(request, template_name, dictionary, **kwargs)
-
-
-def render_to_response(template_name, dictionary=None, **kwargs):
-    dictionary = process_context(template_name, dictionary, kwargs.get('context_instance'))
-    template_name = prefix_templates(template_name, dictionary)
-    return django_render_to_response(template_name, dictionary, content_type=kwargs.get('content_type'))
+from django.shortcuts import (redirect, render as django_render,
+                              render_to_response as django_render_to_response)
+from misago.template.middlewares import process_context
+from misago.template.theme import prefix_templates
+from misago.utils.views import redirect_message, json_response
+
+def render(request, template_name, dictionary=None, **kwargs):
+    dictionary = process_context(template_name, dictionary, kwargs.pop('context_instance', None))
+    template_name = prefix_templates(template_name, dictionary)
+    return django_render(request, template_name, dictionary, **kwargs)
+
+
+def render_to_response(template_name, dictionary=None, **kwargs):
+    dictionary = process_context(template_name, dictionary, kwargs.get('context_instance'))
+    template_name = prefix_templates(template_name, dictionary)
+    return django_render_to_response(template_name, dictionary, content_type=kwargs.get('content_type'))

+ 11 - 11
misago/signals.py

@@ -1,12 +1,12 @@
-import django.dispatch
-
-delete_forum_content = django.dispatch.Signal()
-delete_user_content = django.dispatch.Signal()
-merge_post = django.dispatch.Signal(providing_args=["new_post"])
-merge_thread = django.dispatch.Signal(providing_args=["new_thread"])
-move_forum_content = django.dispatch.Signal(providing_args=["move_to"])
-move_post = django.dispatch.Signal(providing_args=["move_to"])
-move_thread = django.dispatch.Signal(providing_args=["move_to"])
-rename_forum = django.dispatch.Signal()
-rename_user = django.dispatch.Signal()
+import django.dispatch
+
+delete_forum_content = django.dispatch.Signal()
+delete_user_content = django.dispatch.Signal()
+merge_post = django.dispatch.Signal(providing_args=["new_post"])
+merge_thread = django.dispatch.Signal(providing_args=["new_thread"])
+move_forum_content = django.dispatch.Signal(providing_args=["move_to"])
+move_post = django.dispatch.Signal(providing_args=["move_to"])
+move_thread = django.dispatch.Signal(providing_args=["move_to"])
+rename_forum = django.dispatch.Signal()
+rename_user = django.dispatch.Signal()
 sync_user_profile = django.dispatch.Signal()
 sync_user_profile = django.dispatch.Signal()

+ 6 - 6
misago/stopwatch.py

@@ -1,7 +1,7 @@
-import time
-
-class Stopwatch(object):
-    def __init__(self):
-        self.start_time = time.time()
-    def time(self):
+import time
+
+class Stopwatch(object):
+    def __init__(self):
+        self.start_time = time.time()
+    def time(self):
         return time.time() - self.start_time 
         return time.time() - self.start_time 

+ 8 - 8
misago/template/loader.py

@@ -1,8 +1,8 @@
-from django.template.loader import render_to_string as django_render_to_string
-from misago.template.theme import prefix_templates
-from misago.template.middlewares import process_context
-
-def render_to_string(template_name, dictionary=None, context_instance=None):
-    dictionary = process_context(template_name, dictionary, context_instance)
-    template_name = prefix_templates(template_name)
-    return django_render_to_string(template_name, dictionary)
+from django.template.loader import render_to_string as django_render_to_string
+from misago.template.theme import prefix_templates
+from misago.template.middlewares import process_context
+
+def render_to_string(template_name, dictionary=None, context_instance=None):
+    dictionary = process_context(template_name, dictionary, context_instance)
+    template_name = prefix_templates(template_name)
+    return django_render_to_string(template_name, dictionary)

+ 62 - 62
misago/template/middlewares.py

@@ -1,62 +1,62 @@
-from django.conf import settings
-from django.utils.importlib import import_module
-from misago.thread import local
-
-__all__ = ('merge_contexts', 'process_context', 'process_templates')
-
-def load_middlewares():
-    """
-    Populate _middlewares with list of template middlewares instances
-    """
-    middlewares = []
-    for extension in settings.TEMPLATE_MIDDLEWARES:
-        module = '.'.join(extension.split('.')[:-1])
-        extension = extension.split('.')[-1]
-        module = import_module(module)
-        middleware = getattr(module, extension)
-        middlewares += (middleware(), )
-    return tuple(middlewares)
-
-_middlewares = load_middlewares()
-
-def merge_contexts(dictionary=None, context_instance=None):
-    dictionary = dictionary or {}
-    if not context_instance:
-        return dictionary
-    context_instance.update(dictionary)
-    return context_instance
-
-
-_thread_local = local()
-
-
-def process_context(templates, dictionary=None, context_instance=None):
-    context = merge_contexts(dictionary, context_instance)
-    """
-    Put template context trough template middlewares
-    """
-    if _thread_local.template_mutex:
-        return context
-    _thread_local.template_mutex = True
-
-    for middleware in _middlewares:
-        try:
-            new_context = middleware.process_context(templates, context)
-            if new_context:
-                context = new_context
-        except AttributeError:
-            pass
-
-    _thread_local.template_mutex = None
-    return context
-
-
-def process_templates(templates, context):
-    for middleware in _middlewares:
-        try:
-            new_templates = middleware.process_template(templates, context)
-            if new_templates:
-                return new_templates
-        except AttributeError:
-            pass
-    return templates
+from django.conf import settings
+from django.utils.importlib import import_module
+from misago.thread import local
+
+__all__ = ('merge_contexts', 'process_context', 'process_templates')
+
+def load_middlewares():
+    """
+    Populate _middlewares with list of template middlewares instances
+    """
+    middlewares = []
+    for extension in settings.TEMPLATE_MIDDLEWARES:
+        module = '.'.join(extension.split('.')[:-1])
+        extension = extension.split('.')[-1]
+        module = import_module(module)
+        middleware = getattr(module, extension)
+        middlewares += (middleware(), )
+    return tuple(middlewares)
+
+_middlewares = load_middlewares()
+
+def merge_contexts(dictionary=None, context_instance=None):
+    dictionary = dictionary or {}
+    if not context_instance:
+        return dictionary
+    context_instance.update(dictionary)
+    return context_instance
+
+
+_thread_local = local()
+
+
+def process_context(templates, dictionary=None, context_instance=None):
+    context = merge_contexts(dictionary, context_instance)
+    """
+    Put template context trough template middlewares
+    """
+    if _thread_local.template_mutex:
+        return context
+    _thread_local.template_mutex = True
+
+    for middleware in _middlewares:
+        try:
+            new_context = middleware.process_context(templates, context)
+            if new_context:
+                context = new_context
+        except AttributeError:
+            pass
+
+    _thread_local.template_mutex = None
+    return context
+
+
+def process_templates(templates, context):
+    for middleware in _middlewares:
+        try:
+            new_templates = middleware.process_template(templates, context)
+            if new_templates:
+                return new_templates
+        except AttributeError:
+            pass
+    return templates

+ 42 - 42
misago/template/theme.py

@@ -1,42 +1,42 @@
-from django.conf import settings
-from misago.template.middlewares import process_templates
-from misago.thread import local
-
-__all__ = ('activate_theme', 'active_theme', 'prefix_templates')
-
-_thread_local = local()
-
-def activate_theme(theme):
-    """
-    Activate theme in current thread
-    """
-    if theme not in settings.INSTALLED_THEMES:
-        raise ValueError('"%s" is not correct theme name.' % theme)
-    if theme[0] == '_':
-        raise ValueError('"%s" is a template package, not a theme.' % theme[1:])
-    
-    _thread_local.theme = theme;
-    _thread_local.template_mutex = False
-
-
-def reset_theme():
-    _thread_local.theme = settings.INSTALLED_THEMES[0];
-
-
-def active_theme():
-    try:
-        return _thread_local.theme
-    except AttributeError:
-        return None
-
-
-def prefix_templates(templates, dictionary=None):
-    templates = process_templates(templates, dictionary)
-    if isinstance(templates, str):
-        return ('%s/%s' % (_thread_local.theme, templates), templates)
-    else:
-        prefixed = []
-        for template in templates:
-            prefixed.append('%s/%s' % (_thread_local.theme, template))
-        prefixed += templates
-        return tuple(prefixed)
+from django.conf import settings
+from misago.template.middlewares import process_templates
+from misago.thread import local
+
+__all__ = ('activate_theme', 'active_theme', 'prefix_templates')
+
+_thread_local = local()
+
+def activate_theme(theme):
+    """
+    Activate theme in current thread
+    """
+    if theme not in settings.INSTALLED_THEMES:
+        raise ValueError('"%s" is not correct theme name.' % theme)
+    if theme[0] == '_':
+        raise ValueError('"%s" is a template package, not a theme.' % theme[1:])
+    
+    _thread_local.theme = theme;
+    _thread_local.template_mutex = False
+
+
+def reset_theme():
+    _thread_local.theme = settings.INSTALLED_THEMES[0];
+
+
+def active_theme():
+    try:
+        return _thread_local.theme
+    except AttributeError:
+        return None
+
+
+def prefix_templates(templates, dictionary=None):
+    templates = process_templates(templates, dictionary)
+    if isinstance(templates, str):
+        return ('%s/%s' % (_thread_local.theme, templates), templates)
+    else:
+        prefixed = []
+        for template in templates:
+            prefixed.append('%s/%s' % (_thread_local.theme, template))
+        prefixed += templates
+        return tuple(prefixed)

+ 28 - 28
misago/templatetags/datetime.py

@@ -1,29 +1,29 @@
-from django_jinja.library import Library
-from misago.utils.datesformats import date, reldate, reltimesince, compact, relcompact
-
-register = Library()
-
-
-@register.filter(name='date')
-def date_filter(val, arg=""):
-    return date(val, arg)
-
-
-@register.filter(name='reldate')
-def reldate_filter(val, arg=""):
-    return reldate(val, arg)
-
-
-@register.filter(name='reltimesince')
-def reltimesince_filter(val, arg=""):
-    return reltimesince(val, arg)
-
-
-@register.filter(name='compact')
-def compact_filter(val):
-    return compact(val)
-
-
-@register.filter(name='relcompact')
-def relcompact_filter(val):
+from django_jinja.library import Library
+from misago.utils.datesformats import date, reldate, reltimesince, compact, relcompact
+
+register = Library()
+
+
+@register.filter(name='date')
+def date_filter(val, arg=""):
+    return date(val, arg)
+
+
+@register.filter(name='reldate')
+def reldate_filter(val, arg=""):
+    return reldate(val, arg)
+
+
+@register.filter(name='reltimesince')
+def reltimesince_filter(val, arg=""):
+    return reltimesince(val, arg)
+
+
+@register.filter(name='compact')
+def compact_filter(val):
+    return compact(val)
+
+
+@register.filter(name='relcompact')
+def relcompact_filter(val):
     return relcompact(val)
     return relcompact(val)

+ 33 - 33
misago/templatetags/django2jinja.py

@@ -1,33 +1,33 @@
-import math
-import urllib
-from django_jinja.library import Library
-from misago.utils.strings import slugify
-
-register = Library()
-
-
-@register.global_function(name='widthratio')
-def widthratio(min=0, max=100, range=100):
-    return int(math.ceil(float(float(min) / float(max) * int(range))))
-
-
-@register.global_function(name='query')
-def query_string(**kwargs):
-    query = urllib.urlencode(kwargs)
-    return '?%s' % (query if kwargs else '')
-
-
-@register.filter(name='low')
-def low(value):
-    if not value:
-        return u''
-    try:
-        rest = value[1:]
-    except IndexError:
-        rest = ''
-    return '%s%s' % (unicode(value[0]).lower(), rest)
-
-
-@register.filter(name="slugify")
-def slugify_function(format_string):
-    return slugify(format_string)
+import math
+import urllib
+from django_jinja.library import Library
+from misago.utils.strings import slugify
+
+register = Library()
+
+
+@register.global_function(name='widthratio')
+def widthratio(min=0, max=100, range=100):
+    return int(math.ceil(float(float(min) / float(max) * int(range))))
+
+
+@register.global_function(name='query')
+def query_string(**kwargs):
+    query = urllib.urlencode(kwargs)
+    return '?%s' % (query if kwargs else '')
+
+
+@register.filter(name='low')
+def low(value):
+    if not value:
+        return u''
+    try:
+        rest = value[1:]
+    except IndexError:
+        rest = ''
+    return '%s%s' % (unicode(value[0]).lower(), rest)
+
+
+@register.filter(name="slugify")
+def slugify_function(format_string):
+    return slugify(format_string)

+ 40 - 40
misago/templatetags/md.py

@@ -1,41 +1,41 @@
-from markdown import markdown
-from django_jinja.library import Library
-from django.conf import settings
-import misago.markdown
-
-register = Library()
-
-
-@register.filter(name='markdown')
-def parse_markdown(value, format=None):
-    if not format:
-        format = settings.OUTPUT_FORMAT
-    return markdown(value,
-                    safe_mode='escape',
-                    output_format=format,
-                    extensions=['nl2br', 'fenced_code']).strip()
-
-
-@register.filter(name='markdown_short')
-def short_markdown(value, length=300):
-    value = misago.markdown.clear_markdown(value)
-
-    if len(value) <= length:
-        return ' '.join(value.splitlines())
-
-    value = ' '.join(value.splitlines())
-    value = value[0:length]
-
-    while value[-1] != ' ':
-        value = value[0:-1]
-
-    value = value.strip()
-    if value[-3:3] != '...':
-        value = '%s...' % value
-
-    return value
-
-
-@register.filter(name='markdown_final')
-def finalize_markdown(value):
+from markdown import markdown
+from django_jinja.library import Library
+from django.conf import settings
+import misago.markdown
+
+register = Library()
+
+
+@register.filter(name='markdown')
+def parse_markdown(value, format=None):
+    if not format:
+        format = settings.OUTPUT_FORMAT
+    return markdown(value,
+                    safe_mode='escape',
+                    output_format=format,
+                    extensions=['nl2br', 'fenced_code']).strip()
+
+
+@register.filter(name='markdown_short')
+def short_markdown(value, length=300):
+    value = misago.markdown.clear_markdown(value)
+
+    if len(value) <= length:
+        return ' '.join(value.splitlines())
+
+    value = ' '.join(value.splitlines())
+    value = value[0:length]
+
+    while value[-1] != ' ':
+        value = value[0:-1]
+
+    value = value.strip()
+    if value[-3:3] != '...':
+        value = '%s...' % value
+
+    return value
+
+
+@register.filter(name='markdown_final')
+def finalize_markdown(value):
     return misago.markdown.finalize_markdown(value)
     return misago.markdown.finalize_markdown(value)

+ 25 - 25
misago/templatetags/utils.py

@@ -1,26 +1,26 @@
-from django_jinja.library import Library
-from haystack.utils import Highlighter
-from misago.utils.strings import short_string
-
-register = Library()
-
-
-@register.global_function(name='intersect')
-def intersect(list_a, list_b):
-    for i in list_a:
-        if i in list_b:
-            return True
-    return False
-
-
-@register.filter(name='short_string')
-def make_short(string, length=16):
-    return short_string(string, length)
-
-
-@register.filter(name='highlight')
-def highlight_result(text, query, length=500):
-    hl = Highlighter(query, html_tag='strong', max_length=length)
-    hl = hl.highlight(text)
-
+from django_jinja.library import Library
+from haystack.utils import Highlighter
+from misago.utils.strings import short_string
+
+register = Library()
+
+
+@register.global_function(name='intersect')
+def intersect(list_a, list_b):
+    for i in list_a:
+        if i in list_b:
+            return True
+    return False
+
+
+@register.filter(name='short_string')
+def make_short(string, length=16):
+    return short_string(string, length)
+
+
+@register.filter(name='highlight')
+def highlight_result(text, query, length=500):
+    hl = Highlighter(query, html_tag='strong', max_length=length)
+    hl = hl.highlight(text)
+
     return hl
     return hl

+ 70 - 70
misago/tests/user_manager_create_user.py

@@ -1,70 +1,70 @@
-from django.core.exceptions import ValidationError
-from django.core.management import call_command
-from django.test import TestCase
-from misago.models import User
-from misago.monitor import monitor, refresh_monitor, UpdatingMonitor
-
-class UserManagerCreateUserTestCase(TestCase):
-    def setUp(self):
-        call_command('startmisago', quiet=True)
-
-    def test_create_user(self):
-        """Test User.objects.create_user"""
-        with UpdatingMonitor() as cm:
-            user_a = User.objects.create_user('Lemmiwinks', 'lemm@sp.com', '123pass')
-            try:
-                user_from_db = User.objects.get(username=user_a.username)
-                user_from_db = User.objects.get(email=user_a.email)
-            except User.DoesNotExist:
-                raise AssertionError("User A was not saved in database!")
-
-        refresh_monitor()
-        self.assertEqual(int(monitor['users']), 1)
-        self.assertEqual(int(monitor['users_inactive']), 0)
-        self.assertEqual(int(monitor['last_user']), user_a.pk)
-        self.assertEqual(monitor['last_user_name'], user_a.username)
-        self.assertEqual(monitor['last_user_slug'], user_a.username_slug)
-
-        with UpdatingMonitor() as cm:
-            user_b = User.objects.create_user('InactiveTest', 'lemsm@sp.com', '123pass', activation=User.ACTIVATION_USER)
-            try:
-                user_from_db = User.objects.get(username=user_b.username)
-                user_from_db = User.objects.get(email=user_b.email)
-                self.assertEqual(user_from_db.activation, User.ACTIVATION_USER)
-            except User.DoesNotExist:
-                raise AssertionError("User B was not saved in database!")
-
-        refresh_monitor()
-        self.assertEqual(int(monitor['users']), 1)
-        self.assertEqual(int(monitor['users_inactive']), 1)
-        self.assertEqual(int(monitor['last_user']), user_a.pk)
-        self.assertEqual(monitor['last_user_name'], user_a.username)
-        self.assertEqual(monitor['last_user_slug'], user_a.username_slug)
-
-        with UpdatingMonitor() as cm:
-            try:
-                user_c = User.objects.create_user('UsedMail', 'lemsm@sp.com', '123pass')
-                raise AssertionError("Created user account with taken e-mail address!")
-            except ValidationError:
-                pass
-
-        refresh_monitor()
-        self.assertEqual(int(monitor['users']), 1)
-        self.assertEqual(int(monitor['users_inactive']), 1)
-        self.assertEqual(int(monitor['last_user']), user_a.pk)
-        self.assertEqual(monitor['last_user_name'], user_a.username)
-        self.assertEqual(monitor['last_user_slug'], user_a.username_slug)
-
-        with UpdatingMonitor() as cm:
-            try:
-                user_d = User.objects.create_user('InactiveTest', 'user@name.com', '123pass')
-                raise AssertionError("Created user account with taken username!")
-            except ValidationError:
-                pass
-
-        refresh_monitor()
-        self.assertEqual(int(monitor['users']), 1)
-        self.assertEqual(int(monitor['users_inactive']), 1)
-        self.assertEqual(int(monitor['last_user']), user_a.pk)
-        self.assertEqual(monitor['last_user_name'], user_a.username)
-        self.assertEqual(monitor['last_user_slug'], user_a.username_slug)
+from django.core.exceptions import ValidationError
+from django.core.management import call_command
+from django.test import TestCase
+from misago.models import User
+from misago.monitor import monitor, refresh_monitor, UpdatingMonitor
+
+class UserManagerCreateUserTestCase(TestCase):
+    def setUp(self):
+        call_command('startmisago', quiet=True)
+
+    def test_create_user(self):
+        """Test User.objects.create_user"""
+        with UpdatingMonitor() as cm:
+            user_a = User.objects.create_user('Lemmiwinks', 'lemm@sp.com', '123pass')
+            try:
+                user_from_db = User.objects.get(username=user_a.username)
+                user_from_db = User.objects.get(email=user_a.email)
+            except User.DoesNotExist:
+                raise AssertionError("User A was not saved in database!")
+
+        refresh_monitor()
+        self.assertEqual(int(monitor['users']), 1)
+        self.assertEqual(int(monitor['users_inactive']), 0)
+        self.assertEqual(int(monitor['last_user']), user_a.pk)
+        self.assertEqual(monitor['last_user_name'], user_a.username)
+        self.assertEqual(monitor['last_user_slug'], user_a.username_slug)
+
+        with UpdatingMonitor() as cm:
+            user_b = User.objects.create_user('InactiveTest', 'lemsm@sp.com', '123pass', activation=User.ACTIVATION_USER)
+            try:
+                user_from_db = User.objects.get(username=user_b.username)
+                user_from_db = User.objects.get(email=user_b.email)
+                self.assertEqual(user_from_db.activation, User.ACTIVATION_USER)
+            except User.DoesNotExist:
+                raise AssertionError("User B was not saved in database!")
+
+        refresh_monitor()
+        self.assertEqual(int(monitor['users']), 1)
+        self.assertEqual(int(monitor['users_inactive']), 1)
+        self.assertEqual(int(monitor['last_user']), user_a.pk)
+        self.assertEqual(monitor['last_user_name'], user_a.username)
+        self.assertEqual(monitor['last_user_slug'], user_a.username_slug)
+
+        with UpdatingMonitor() as cm:
+            try:
+                user_c = User.objects.create_user('UsedMail', 'lemsm@sp.com', '123pass')
+                raise AssertionError("Created user account with taken e-mail address!")
+            except ValidationError:
+                pass
+
+        refresh_monitor()
+        self.assertEqual(int(monitor['users']), 1)
+        self.assertEqual(int(monitor['users_inactive']), 1)
+        self.assertEqual(int(monitor['last_user']), user_a.pk)
+        self.assertEqual(monitor['last_user_name'], user_a.username)
+        self.assertEqual(monitor['last_user_slug'], user_a.username_slug)
+
+        with UpdatingMonitor() as cm:
+            try:
+                user_d = User.objects.create_user('InactiveTest', 'user@name.com', '123pass')
+                raise AssertionError("Created user account with taken username!")
+            except ValidationError:
+                pass
+
+        refresh_monitor()
+        self.assertEqual(int(monitor['users']), 1)
+        self.assertEqual(int(monitor['users_inactive']), 1)
+        self.assertEqual(int(monitor['last_user']), user_a.pk)
+        self.assertEqual(monitor['last_user_name'], user_a.username)
+        self.assertEqual(monitor['last_user_slug'], user_a.username_slug)

+ 11 - 11
misago/thread.py

@@ -1,12 +1,12 @@
-import threading
-
-_thread_local = threading.local()
-
-def local():
-    return _thread_local
-
-
-def clear():
-    for attr in _thread_local.__dict__.keys():
-        if attr[0] != '_':
+import threading
+
+_thread_local = threading.local()
+
+def local():
+    return _thread_local
+
+
+def clear():
+    for attr in _thread_local.__dict__.keys():
+        if attr[0] != '_':
             del _thread_local.__dict__[attr]
             del _thread_local.__dict__[attr]

+ 56 - 56
misago/urls.py

@@ -1,57 +1,57 @@
-from django.conf import settings
-from django.conf.urls import patterns, include, url
-from django.contrib.staticfiles.urls import staticfiles_urlpatterns
-from misago.admin import ADMIN_PATH, site
-
-# Include frontend patterns
-urlpatterns = patterns('misago.apps',
-    url(r'^$', 'index.index', name="index"),
-    url(r'^read-all/$', 'readall.read_all', name="read_all"),
-    url(r'^register/$', 'register.views.form', name="register"),
-    url(r'^category/(?P<slug>(\w|-)+)-(?P<forum>\d+)/$', 'category.category', name="category"),
-    url(r'^redirect/(?P<slug>(\w|-)+)-(?P<forum>\d+)/$', 'redirect.redirect', name="redirect"),
-    url(r'^alerts/$', 'alerts.alerts', name="alerts"),
-    url(r'^news/$', 'newsfeed.newsfeed', name="newsfeed"),
-    url(r'^tos/$', 'tos.tos', name="tos"),
-    url(r'^forum-map/$', 'forummap.forum_map', name="forum_map"),
-    url(r'^popular/$', 'popularthreads.popular_threads', name="popular_threads"),
-    url(r'^popular/(?P<page>[1-9]([0-9]+)?)/$', 'popularthreads.popular_threads', name="popular_threads"),
-    url(r'^new/$', 'newthreads.new_threads', name="new_threads"),
-    url(r'^new/(?P<page>[1-9]([0-9]+)?)/$', 'newthreads.new_threads', name="new_threads"),
-)
-
-urlpatterns += patterns('',
-    (r'^', include('misago.apps.signin.urls')),
-    (r'^users/', include('misago.apps.profiles.urls')),
-    (r'^usercp/', include('misago.apps.usercp.urls')),
-    (r'^activate/', include('misago.apps.activation.urls')),
-    (r'^watched-threads/', include('misago.apps.watchedthreads.urls')),
-    (r'^reset-password/', include('misago.apps.resetpswd.urls')),
-    (r'^private-threads/', include('misago.apps.privatethreads.urls')),
-    (r'^reports/', include('misago.apps.reports.urls')),
-    (r'^search/', include('misago.apps.search.urls')),
-    (r'^', include('misago.apps.threads.urls')),
-)
-
-
-# Include admin patterns
-if ADMIN_PATH:
-    urlpatterns += patterns('',
-        url(r'^' + ADMIN_PATH, include(site.discover())),
-    )
-
-# Include static and media patterns in DEBUG
-if settings.DEBUG:
-    urlpatterns += patterns('django.views.static',
-        (r'media/(?P<path>.*)', 'serve', {'document_root': settings.MEDIA_ROOT}),
-    )
-
-# Set error handlers
-handler403 = 'misago.apps.errors.error403'
-handler404 = 'misago.apps.errors.error404'
-
-# Make sure people are not keeping uploads and app under same domain
-import warnings
-from urlparse import urlparse
-if not settings.DEBUG and not urlparse(settings.MEDIA_URL).netloc:
+from django.conf import settings
+from django.conf.urls import patterns, include, url
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from misago.admin import ADMIN_PATH, site
+
+# Include frontend patterns
+urlpatterns = patterns('misago.apps',
+    url(r'^$', 'index.index', name="index"),
+    url(r'^read-all/$', 'readall.read_all', name="read_all"),
+    url(r'^register/$', 'register.views.form', name="register"),
+    url(r'^category/(?P<slug>(\w|-)+)-(?P<forum>\d+)/$', 'category.category', name="category"),
+    url(r'^redirect/(?P<slug>(\w|-)+)-(?P<forum>\d+)/$', 'redirect.redirect', name="redirect"),
+    url(r'^alerts/$', 'alerts.alerts', name="alerts"),
+    url(r'^news/$', 'newsfeed.newsfeed', name="newsfeed"),
+    url(r'^tos/$', 'tos.tos', name="tos"),
+    url(r'^forum-map/$', 'forummap.forum_map', name="forum_map"),
+    url(r'^popular/$', 'popularthreads.popular_threads', name="popular_threads"),
+    url(r'^popular/(?P<page>[1-9]([0-9]+)?)/$', 'popularthreads.popular_threads', name="popular_threads"),
+    url(r'^new/$', 'newthreads.new_threads', name="new_threads"),
+    url(r'^new/(?P<page>[1-9]([0-9]+)?)/$', 'newthreads.new_threads', name="new_threads"),
+)
+
+urlpatterns += patterns('',
+    (r'^', include('misago.apps.signin.urls')),
+    (r'^users/', include('misago.apps.profiles.urls')),
+    (r'^usercp/', include('misago.apps.usercp.urls')),
+    (r'^activate/', include('misago.apps.activation.urls')),
+    (r'^watched-threads/', include('misago.apps.watchedthreads.urls')),
+    (r'^reset-password/', include('misago.apps.resetpswd.urls')),
+    (r'^private-threads/', include('misago.apps.privatethreads.urls')),
+    (r'^reports/', include('misago.apps.reports.urls')),
+    (r'^search/', include('misago.apps.search.urls')),
+    (r'^', include('misago.apps.threads.urls')),
+)
+
+
+# Include admin patterns
+if ADMIN_PATH:
+    urlpatterns += patterns('',
+        url(r'^' + ADMIN_PATH, include(site.discover())),
+    )
+
+# Include static and media patterns in DEBUG
+if settings.DEBUG:
+    urlpatterns += patterns('django.views.static',
+        (r'media/(?P<path>.*)', 'serve', {'document_root': settings.MEDIA_ROOT}),
+    )
+
+# Set error handlers
+handler403 = 'misago.apps.errors.error403'
+handler404 = 'misago.apps.errors.error404'
+
+# Make sure people are not keeping uploads and app under same domain
+import warnings
+from urlparse import urlparse
+if not settings.DEBUG and not urlparse(settings.MEDIA_URL).netloc:
     warnings.warn('Sharing same domain name between application and user uploaded media is a security risk. Create a subdomain pointing to your media directory (eg. "uploads.myforum.com") and change your MEDIA_URL.', RuntimeWarning)
     warnings.warn('Sharing same domain name between application and user uploaded media is a security risk. Create a subdomain pointing to your media directory (eg. "uploads.myforum.com") and change your MEDIA_URL.', RuntimeWarning)

+ 46 - 46
misago/utils/avatars.py

@@ -1,46 +1,46 @@
-from django.conf import settings
-try:
-    from PIL import Image
-    has_pil = True
-except ImportError:
-    has_pil = False
-
-avatar_sizes = {}
-def avatar_size(size):
-    if not has_pil:
-        return None
-    try:
-        return avatar_sizes[size]
-    except KeyError:
-        avatar_sizes[size] = None
-        for i in settings.AVATAR_SIZES[1:]:
-            if size <= i:
-                avatar_sizes[size] = i
-    return avatar_sizes[size]
-
-
-def resizeimage(image, size, target, info=None, format=None):
-    if isinstance(image, basestring):
-        image = Image.open(image)
-    if not info:
-        info = image.info
-    if not format:
-        format = image.format
-    if format == "GIF":
-        if 'transparency' in info:
-            image = image.resize((size, size), Image.ANTIALIAS)
-            image.save(target, image.format, transparency=info['transparency'])
-        else:
-            image = image.convert("RGB")
-            image = image.resize((size, size), Image.ANTIALIAS)
-            image = image.convert('P', palette=Image.ADAPTIVE)
-            image.save(target, image.format)
-    if format == "PNG":
-        image = image.resize((size, size), Image.ANTIALIAS)
-        image.save(target, quality=95)
-    if format == "JPEG":
-        image = image.convert("RGB")
-        image = image.resize((size, size), Image.ANTIALIAS)
-        image = image.convert('P', palette=Image.ADAPTIVE)
-        image = image.convert("RGB", dither=None)
-        image.save(target, image.format, quality=95)
+from django.conf import settings
+try:
+    from PIL import Image
+    has_pil = True
+except ImportError:
+    has_pil = False
+
+avatar_sizes = {}
+def avatar_size(size):
+    if not has_pil:
+        return None
+    try:
+        return avatar_sizes[size]
+    except KeyError:
+        avatar_sizes[size] = None
+        for i in settings.AVATAR_SIZES[1:]:
+            if size <= i:
+                avatar_sizes[size] = i
+    return avatar_sizes[size]
+
+
+def resizeimage(image, size, target, info=None, format=None):
+    if isinstance(image, basestring):
+        image = Image.open(image)
+    if not info:
+        info = image.info
+    if not format:
+        format = image.format
+    if format == "GIF":
+        if 'transparency' in info:
+            image = image.resize((size, size), Image.ANTIALIAS)
+            image.save(target, image.format, transparency=info['transparency'])
+        else:
+            image = image.convert("RGB")
+            image = image.resize((size, size), Image.ANTIALIAS)
+            image = image.convert('P', palette=Image.ADAPTIVE)
+            image.save(target, image.format)
+    if format == "PNG":
+        image = image.resize((size, size), Image.ANTIALIAS)
+        image.save(target, quality=95)
+    if format == "JPEG":
+        image = image.convert("RGB")
+        image = image.resize((size, size), Image.ANTIALIAS)
+        image = image.convert('P', palette=Image.ADAPTIVE)
+        image = image.convert("RGB", dither=None)
+        image.save(target, image.format, quality=95)

+ 142 - 142
misago/utils/datesformats.py

@@ -1,143 +1,143 @@
-import math
-from datetime import datetime, timedelta
-from django.utils.dateformat import format, time_format
-from django.utils.formats import get_format
-from django.utils.timezone import is_aware, localtime, utc
-from django.utils.translation import pgettext, ungettext, ugettext as _
-from misago.utils.strings import slugify
-
-# Build date formats
-formats = {
-    'DATE_FORMAT': '',
-    'DATETIME_FORMAT': '',
-    'TIME_FORMAT': '',
-    'YEAR_MONTH_FORMAT': '',
-    'MONTH_DAY_FORMAT': '',
-    'SHORT_DATE_FORMAT': '',
-    'SHORT_DATETIME_FORMAT': '',
-}
-
-for key in formats:
-    formats[key] = get_format(key).replace('P', 'g:i a')
-    
-def date(val, arg=""):
-    if not val:
-        return _("Never")
-    if not arg:
-        arg = formats['DATE_FORMAT']
-    elif arg in formats:
-        arg = formats[arg]
-    return format(localtime(val), arg)
-
-
-def reldate(val, arg=""):
-    if not val:
-        return _("Never")
-    now = datetime.now(utc if is_aware(val) else None)
-    local_now = localtime(now)
-    diff = now - val
-    local = localtime(val)
-
-    # Today check
-    if format(local, 'Y-z') == format(local_now, 'Y-z'):
-        return _("Today, %(hour)s") % {'hour': time_format(local, formats['TIME_FORMAT'])}
-        
-    # Yesteday check
-    yesterday = localtime(now - timedelta(days=1))
-    if format(local, 'Y-z') == format(yesterday, 'Y-z'):
-        return _("Yesterday, %(hour)s") % {'hour': time_format(local, formats['TIME_FORMAT'])}
-        
-    # Tomorrow Check
-    tomorrow = localtime(now + timedelta(days=1))
-    if format(local, 'Y-z') == format(tomorrow, 'Y-z'):
-        return _("Tomorrow, %(hour)s") % {'hour': time_format(local, formats['TIME_FORMAT'])}
-        
-    # Day of Week check
-    if format(local, 'D') != format(local_now, 'D') and diff.days > -7 and diff.days < 7:
-        return _("%(day)s, %(hour)s") % {'day': format(local, 'l'), 'hour': time_format(local, formats['TIME_FORMAT'])}
-
-    # Fallback to date      
-    return date(val, arg)
-
-
-def reltimesince(val, arg=""):
-    if not val:
-        return _("Never")
-    now = datetime.now(utc if is_aware(val) else None)
-    diff = now - val
-    local = localtime(val)
-
-    # Difference is greater than day for sure
-    if diff.days != 0:
-        return reldate(val, arg)
-
-    # Display specific time
-    if diff.seconds >= 0:
-        if diff.seconds <= 5:
-            return _("Just now")
-                        
-        if diff.seconds < 3540:
-            minutes = int(math.ceil(diff.seconds / 60.0))
-            return ungettext(
-                    "Minute ago",
-                    "%(minutes)s minutes ago",
-                minutes) % {'minutes': minutes}
-
-        if diff.seconds < 3660:
-            return _("Hour ago")
-        
-        if diff.seconds < 10800:
-            hours = int(math.floor(diff.seconds / 3600.0))
-            minutes = (diff.seconds - (hours * 3600)) / 60
-            
-            if minutes > 0:
-                return ungettext(
-                    "Hour and %(minutes)s ago",
-                    "%(hours)s hours and %(minutes)s ago",
-                hours) % {'hours': hours, 'minutes': ungettext(
-                        "%(minutes)s minute",
-                        "%(minutes)s minutes",
-                    minutes) % {'minutes': minutes}}
-                
-            return ungettext(
-                    "Hour ago",
-                    "%(hours)s hours ago",
-                hours) % {'hours': hours}
-
-    # Fallback to reldate
-    return reldate(val, arg)
-
-
-def compact(val):
-    if not val:
-        return _("Never")
-    now = datetime.now(utc if is_aware(val) else None)
-    local = localtime(val)
-
-    if now.year == local.year:        
-        return format(localtime(val), _('j M'))
-    return format(localtime(val), _('j M y'))
-
-
-def relcompact(val):
-    if not val:
-        return _("Never")
-    now = datetime.now(utc if is_aware(val) else None)
-    diff = now - val
-    local = localtime(val)
-
-    # Difference is greater than day for sure
-    if diff.days != 0:
-        return compact(val)
-
-    if diff.seconds >= 0:
-        if diff.seconds <= 60:
-            return _("Now")
-        if diff.seconds < 3600:
-            minutes = int(math.ceil(diff.seconds / 60.0))
-            return pgettext("number of minutes", "%(minute)sm") % {'minute': minutes}
-        if diff.seconds < 86400:
-            hours = int(math.ceil(diff.seconds / 3600.0))
-            return pgettext("number of hours", "%(hour)sh") % {'hour': hours}
-
+import math
+from datetime import datetime, timedelta
+from django.utils.dateformat import format, time_format
+from django.utils.formats import get_format
+from django.utils.timezone import is_aware, localtime, utc
+from django.utils.translation import pgettext, ungettext, ugettext as _
+from misago.utils.strings import slugify
+
+# Build date formats
+formats = {
+    'DATE_FORMAT': '',
+    'DATETIME_FORMAT': '',
+    'TIME_FORMAT': '',
+    'YEAR_MONTH_FORMAT': '',
+    'MONTH_DAY_FORMAT': '',
+    'SHORT_DATE_FORMAT': '',
+    'SHORT_DATETIME_FORMAT': '',
+}
+
+for key in formats:
+    formats[key] = get_format(key).replace('P', 'g:i a')
+    
+def date(val, arg=""):
+    if not val:
+        return _("Never")
+    if not arg:
+        arg = formats['DATE_FORMAT']
+    elif arg in formats:
+        arg = formats[arg]
+    return format(localtime(val), arg)
+
+
+def reldate(val, arg=""):
+    if not val:
+        return _("Never")
+    now = datetime.now(utc if is_aware(val) else None)
+    local_now = localtime(now)
+    diff = now - val
+    local = localtime(val)
+
+    # Today check
+    if format(local, 'Y-z') == format(local_now, 'Y-z'):
+        return _("Today, %(hour)s") % {'hour': time_format(local, formats['TIME_FORMAT'])}
+        
+    # Yesteday check
+    yesterday = localtime(now - timedelta(days=1))
+    if format(local, 'Y-z') == format(yesterday, 'Y-z'):
+        return _("Yesterday, %(hour)s") % {'hour': time_format(local, formats['TIME_FORMAT'])}
+        
+    # Tomorrow Check
+    tomorrow = localtime(now + timedelta(days=1))
+    if format(local, 'Y-z') == format(tomorrow, 'Y-z'):
+        return _("Tomorrow, %(hour)s") % {'hour': time_format(local, formats['TIME_FORMAT'])}
+        
+    # Day of Week check
+    if format(local, 'D') != format(local_now, 'D') and diff.days > -7 and diff.days < 7:
+        return _("%(day)s, %(hour)s") % {'day': format(local, 'l'), 'hour': time_format(local, formats['TIME_FORMAT'])}
+
+    # Fallback to date      
+    return date(val, arg)
+
+
+def reltimesince(val, arg=""):
+    if not val:
+        return _("Never")
+    now = datetime.now(utc if is_aware(val) else None)
+    diff = now - val
+    local = localtime(val)
+
+    # Difference is greater than day for sure
+    if diff.days != 0:
+        return reldate(val, arg)
+
+    # Display specific time
+    if diff.seconds >= 0:
+        if diff.seconds <= 5:
+            return _("Just now")
+                        
+        if diff.seconds < 3540:
+            minutes = int(math.ceil(diff.seconds / 60.0))
+            return ungettext(
+                    "Minute ago",
+                    "%(minutes)s minutes ago",
+                minutes) % {'minutes': minutes}
+
+        if diff.seconds < 3660:
+            return _("Hour ago")
+        
+        if diff.seconds < 10800:
+            hours = int(math.floor(diff.seconds / 3600.0))
+            minutes = (diff.seconds - (hours * 3600)) / 60
+            
+            if minutes > 0:
+                return ungettext(
+                    "Hour and %(minutes)s ago",
+                    "%(hours)s hours and %(minutes)s ago",
+                hours) % {'hours': hours, 'minutes': ungettext(
+                        "%(minutes)s minute",
+                        "%(minutes)s minutes",
+                    minutes) % {'minutes': minutes}}
+                
+            return ungettext(
+                    "Hour ago",
+                    "%(hours)s hours ago",
+                hours) % {'hours': hours}
+
+    # Fallback to reldate
+    return reldate(val, arg)
+
+
+def compact(val):
+    if not val:
+        return _("Never")
+    now = datetime.now(utc if is_aware(val) else None)
+    local = localtime(val)
+
+    if now.year == local.year:        
+        return format(localtime(val), _('j M'))
+    return format(localtime(val), _('j M y'))
+
+
+def relcompact(val):
+    if not val:
+        return _("Never")
+    now = datetime.now(utc if is_aware(val) else None)
+    diff = now - val
+    local = localtime(val)
+
+    # Difference is greater than day for sure
+    if diff.days != 0:
+        return compact(val)
+
+    if diff.seconds >= 0:
+        if diff.seconds <= 60:
+            return _("Now")
+        if diff.seconds < 3600:
+            minutes = int(math.ceil(diff.seconds / 60.0))
+            return pgettext("number of minutes", "%(minute)sm") % {'minute': minutes}
+        if diff.seconds < 86400:
+            hours = int(math.ceil(diff.seconds / 3600.0))
+            return pgettext("number of hours", "%(hour)sh") % {'hour': hours}
+
     return compact(val)
     return compact(val)

+ 126 - 126
misago/utils/fixtures.py

@@ -1,126 +1,126 @@
-import base64
-from django.utils import timezone
-from django.utils.importlib import import_module
-from misago.models import MonitorItem, SettingsGroup, Setting
-from misago.utils.translation import get_msgid
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-
-def load_fixture(name):
-    """
-    Load fixture
-    """
-    try:
-        fixture = import_module(name)
-        fixture.load()
-        return True
-    except (ImportError, AttributeError):
-        return False
-
-
-def update_fixture(name):
-    """
-    If fixture module contains update function, use it to update fixture
-    """
-    try:
-        fixture = import_module(name)
-        fixture.update()
-        return True
-    except (ImportError, AttributeError):
-        return False
-
-
-def load_settings_group_fixture(group, fixture):
-    model_group = SettingsGroup(
-                                key=group,
-                                name=get_msgid(fixture['name']),
-                                description=get_msgid(fixture.get('description'))
-                                )
-    model_group.save(force_insert=True)
-    fixture = fixture.get('settings', ())
-    position = 0
-    for setting in fixture:
-        value = setting[1].get('value')
-        value_default = setting[1].get('default')
-        # Convert boolean True and False to 1 and 0, otherwhise it wont work
-        if setting[1].get('type') == 'boolean':
-            value = 1 if value else 0
-            value_default = 1 if value_default else 0
-        # Convert array value to string
-        if setting[1].get('type') == 'array':
-            value = ','.join(value) if value else ''
-            value_default = ','.join(value_default) if value_default else ''
-        # Store setting in database
-        model_setting = Setting(
-                                setting=setting[0],
-                                group=model_group,
-                                value=value,
-                                value_default=value_default,
-                                normalize_to=setting[1].get('type'),
-                                field=setting[1].get('input'),
-                                extra=base64.encodestring(pickle.dumps(setting[1].get('extra', {}), pickle.HIGHEST_PROTOCOL)),
-                                position=position,
-                                separator=get_msgid(setting[1].get('separator')),
-                                name=get_msgid(setting[1].get('name')),
-                                description=get_msgid(setting[1].get('description')),
-                                )
-        model_setting.save(force_insert=True)
-        position += 1
-
-
-def update_settings_group_fixture(group, fixture):
-    try:
-        model_group = SettingsGroup.objects.get(key=group)
-        settings = {}
-        for setting in model_group.setting_set.all():
-            settings[setting.pk] = setting.value
-        model_group.delete()
-        load_settings_group_fixture(group, fixture)
-
-        for setting in settings:
-            try:
-                new_setting = Setting.objects.get(pk=setting)
-                new_setting.value = settings[setting]
-                new_setting.save(force_update=True)
-            except Setting.DoesNotExist:
-                pass
-    except SettingsGroup.DoesNotExist:
-        load_settings_group_fixture(group, fixture)
-
-
-def load_settings_fixture(fixture):
-    for group in fixture:
-        load_settings_group_fixture(group[0], group[1])
-
-
-def update_settings_fixture(fixture):
-    for group in fixture:
-        update_settings_group_fixture(group[0], group[1])
-
-
-def load_monitor_fixture(fixture):
-    for id in fixture.keys():
-        item = MonitorItem.objects.create(
-                                          id=id,
-                                          value=fixture[id][0],
-                                          type=fixture[id][1],
-                                          updated=timezone.now()
-                                          )
-
-
-def update_monitor_fixture(fixture):
-    for id in fixture.keys():
-        try:
-            item = MonitorItem.objects.get(id=id)
-            item.type = fixture[id][1]
-            item.updated = timezone.now()
-            item.save(force_update=True)
-        except MonitorItem.DoesNotExist:
-            MonitorItem.objects.create(
-                                       id=id,
-                                       value=fixture[id][0],
-                                       type=fixture[id][1],
-                                       updated=timezone.now()
-                                       )
+import base64
+from django.utils import timezone
+from django.utils.importlib import import_module
+from misago.models import MonitorItem, SettingsGroup, Setting
+from misago.utils.translation import get_msgid
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+def load_fixture(name):
+    """
+    Load fixture
+    """
+    try:
+        fixture = import_module(name)
+        fixture.load()
+        return True
+    except (ImportError, AttributeError):
+        return False
+
+
+def update_fixture(name):
+    """
+    If fixture module contains update function, use it to update fixture
+    """
+    try:
+        fixture = import_module(name)
+        fixture.update()
+        return True
+    except (ImportError, AttributeError):
+        return False
+
+
+def load_settings_group_fixture(group, fixture):
+    model_group = SettingsGroup(
+                                key=group,
+                                name=get_msgid(fixture['name']),
+                                description=get_msgid(fixture.get('description'))
+                                )
+    model_group.save(force_insert=True)
+    fixture = fixture.get('settings', ())
+    position = 0
+    for setting in fixture:
+        value = setting[1].get('value')
+        value_default = setting[1].get('default')
+        # Convert boolean True and False to 1 and 0, otherwhise it wont work
+        if setting[1].get('type') == 'boolean':
+            value = 1 if value else 0
+            value_default = 1 if value_default else 0
+        # Convert array value to string
+        if setting[1].get('type') == 'array':
+            value = ','.join(value) if value else ''
+            value_default = ','.join(value_default) if value_default else ''
+        # Store setting in database
+        model_setting = Setting(
+                                setting=setting[0],
+                                group=model_group,
+                                value=value,
+                                value_default=value_default,
+                                normalize_to=setting[1].get('type'),
+                                field=setting[1].get('input'),
+                                extra=base64.encodestring(pickle.dumps(setting[1].get('extra', {}), pickle.HIGHEST_PROTOCOL)),
+                                position=position,
+                                separator=get_msgid(setting[1].get('separator')),
+                                name=get_msgid(setting[1].get('name')),
+                                description=get_msgid(setting[1].get('description')),
+                                )
+        model_setting.save(force_insert=True)
+        position += 1
+
+
+def update_settings_group_fixture(group, fixture):
+    try:
+        model_group = SettingsGroup.objects.get(key=group)
+        settings = {}
+        for setting in model_group.setting_set.all():
+            settings[setting.pk] = setting.value
+        model_group.delete()
+        load_settings_group_fixture(group, fixture)
+
+        for setting in settings:
+            try:
+                new_setting = Setting.objects.get(pk=setting)
+                new_setting.value = settings[setting]
+                new_setting.save(force_update=True)
+            except Setting.DoesNotExist:
+                pass
+    except SettingsGroup.DoesNotExist:
+        load_settings_group_fixture(group, fixture)
+
+
+def load_settings_fixture(fixture):
+    for group in fixture:
+        load_settings_group_fixture(group[0], group[1])
+
+
+def update_settings_fixture(fixture):
+    for group in fixture:
+        update_settings_group_fixture(group[0], group[1])
+
+
+def load_monitor_fixture(fixture):
+    for id in fixture.keys():
+        item = MonitorItem.objects.create(
+                                          id=id,
+                                          value=fixture[id][0],
+                                          type=fixture[id][1],
+                                          updated=timezone.now()
+                                          )
+
+
+def update_monitor_fixture(fixture):
+    for id in fixture.keys():
+        try:
+            item = MonitorItem.objects.get(id=id)
+            item.type = fixture[id][1]
+            item.updated = timezone.now()
+            item.save(force_update=True)
+        except MonitorItem.DoesNotExist:
+            MonitorItem.objects.create(
+                                       id=id,
+                                       value=fixture[id][0],
+                                       type=fixture[id][1],
+                                       updated=timezone.now()
+                                       )

+ 64 - 64
misago/utils/pagination.py

@@ -1,65 +1,65 @@
-import math
-from django.http import Http404
-
-def make_pagination(page, total, per):
-    pagination = {'start': 0, 'stop': 0, 'prev':-1, 'next':-1}
-    page = int(page)
-
-    if page == 1:
-        # This is fugly abuse of "wrong page" handling
-        # It's done to combat "duplicate content" errors
-        # If page is 1 instead of 0, that suggests user came
-        # to page from somewhere/1/ instead of somewhere/
-        # when this happens We raise 404 to drop /1/ part from url
-        raise Http404()
-
-    if page > 0:
-        pagination['start'] = (page - 1) * per
-
-    # Set page and total stat
-    pagination['page'] = int(pagination['start'] / per) + 1
-    pagination['total'] = int(math.ceil(total / float(per)))
-
-    # Fix too large offset
-    if pagination['start'] > total:
-        pagination['start'] = 0
-
-    # Allow prev/next?
-    if total > per:
-        if pagination['page'] > 1:
-            pagination['prev'] = pagination['page'] - 1
-        if pagination['page'] < pagination['total']:
-            pagination['next'] = pagination['page'] + 1
-
-    # Fix empty pagers
-    if not pagination['total']:
-        pagination['total'] = 1
-
-    # Set stop offset
-    pagination['stop'] = pagination['start'] + per
-
-    # Put 1/5 of last page on current page...
-    if pagination['page'] + 1 == pagination['total']:
-        last_page = per + total - (pagination['total'] * per)
-        cutoff = int(per / 5)
-        if cutoff > 1 and last_page < cutoff:
-            pagination['stop'] += last_page
-            pagination['total'] -= 1
-            pagination['next'] = -1
-
-    # Raise 404 if page is out of range
-    if pagination['page'] > pagination['total']:
-        raise Http404()
-
-    # Return complete pager
-    return pagination
-
-
-def page_number(item, total, per):
-    page_item = int(math.ceil(item / float(per)))
-    pages_total = int(math.ceil(total / float(per)))
-    last_page = total - ((pages_total - 1) * per)
-    cutoff = int(per / 5)
-    if cutoff > 1 and cutoff > last_page and pages_total == page_item and pages_total > 1:
-        page_item -= 1
+import math
+from django.http import Http404
+
+def make_pagination(page, total, per):
+    pagination = {'start': 0, 'stop': 0, 'prev':-1, 'next':-1}
+    page = int(page)
+
+    if page == 1:
+        # This is fugly abuse of "wrong page" handling
+        # It's done to combat "duplicate content" errors
+        # If page is 1 instead of 0, that suggests user came
+        # to page from somewhere/1/ instead of somewhere/
+        # when this happens We raise 404 to drop /1/ part from url
+        raise Http404()
+
+    if page > 0:
+        pagination['start'] = (page - 1) * per
+
+    # Set page and total stat
+    pagination['page'] = int(pagination['start'] / per) + 1
+    pagination['total'] = int(math.ceil(total / float(per)))
+
+    # Fix too large offset
+    if pagination['start'] > total:
+        pagination['start'] = 0
+
+    # Allow prev/next?
+    if total > per:
+        if pagination['page'] > 1:
+            pagination['prev'] = pagination['page'] - 1
+        if pagination['page'] < pagination['total']:
+            pagination['next'] = pagination['page'] + 1
+
+    # Fix empty pagers
+    if not pagination['total']:
+        pagination['total'] = 1
+
+    # Set stop offset
+    pagination['stop'] = pagination['start'] + per
+
+    # Put 1/5 of last page on current page...
+    if pagination['page'] + 1 == pagination['total']:
+        last_page = per + total - (pagination['total'] * per)
+        cutoff = int(per / 5)
+        if cutoff > 1 and last_page < cutoff:
+            pagination['stop'] += last_page
+            pagination['total'] -= 1
+            pagination['next'] = -1
+
+    # Raise 404 if page is out of range
+    if pagination['page'] > pagination['total']:
+        raise Http404()
+
+    # Return complete pager
+    return pagination
+
+
+def page_number(item, total, per):
+    page_item = int(math.ceil(item / float(per)))
+    pages_total = int(math.ceil(total / float(per)))
+    last_page = total - ((pages_total - 1) * per)
+    cutoff = int(per / 5)
+    if cutoff > 1 and cutoff > last_page and pages_total == page_item and pages_total > 1:
+        page_item -= 1
     return page_item
     return page_item

+ 8 - 8
misago/utils/plugins.py

@@ -1,8 +1,8 @@
-from django.template.loader import render_to_string as django_render_to_string
-
-def render_to_string(template_name, dictionary=None, context_instance=None):
-    from misago.template.theme import prefix_templates
-    template_name = prefix_templates(template_name)
-    return django_render_to_string(template_name,
-                                   dictionary,
-                                   context_instance=context_instance)
+from django.template.loader import render_to_string as django_render_to_string
+
+def render_to_string(template_name, dictionary=None, context_instance=None):
+    from misago.template.theme import prefix_templates
+    template_name = prefix_templates(template_name)
+    return django_render_to_string(template_name,
+                                   dictionary,
+                                   context_instance=context_instance)

+ 32 - 32
misago/utils/strings.py

@@ -1,33 +1,33 @@
-from django.template.defaultfilters import slugify as django_slugify
-from django.utils import crypto
-try:
-    from unidecode import unidecode
-    use_unidecode = True
-except ImportError:
-    use_unidecode = False
-
-def slugify(string):
-    string = unicode(string)
-    if use_unidecode:
-        string = unidecode(string)
-    return django_slugify(string)
-
-
-def random_string(length):
-    return crypto.get_random_string(length, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")
-
-
-def short_string(string, length=16):
-    if len(string) <= length:
-        return string;
-    string = string[0:length - 3]
-    bits = string.split()
-    if len(bits[-1]) < 3:
-        bits.pop()
-    return '%s...' % (' '.join(bits))
-
-def html_escape(html):
-    html = html.replace('&', '&amp;')
-    html = html.replace('<', '&lt;')
-    html = html.replace('>', '&gt;')
+from django.template.defaultfilters import slugify as django_slugify
+from django.utils import crypto
+try:
+    from unidecode import unidecode
+    use_unidecode = True
+except ImportError:
+    use_unidecode = False
+
+def slugify(string):
+    string = unicode(string)
+    if use_unidecode:
+        string = unidecode(string)
+    return django_slugify(string)
+
+
+def random_string(length):
+    return crypto.get_random_string(length, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")
+
+
+def short_string(string, length=16):
+    if len(string) <= length:
+        return string;
+    string = string[0:length - 3]
+    bits = string.split()
+    if len(bits[-1]) < 3:
+        bits.pop()
+    return '%s...' % (' '.join(bits))
+
+def html_escape(html):
+    html = html.replace('&', '&amp;')
+    html = html.replace('<', '&lt;')
+    html = html.replace('>', '&gt;')
     return html.replace('"', '&quot;')
     return html.replace('"', '&quot;')

+ 87 - 87
misago/utils/timezones.py

@@ -1,87 +1,87 @@
-import datetime
-from django.utils.translation import ugettext_lazy as _
-from django.utils import timezone
-import pytz
-
-raw_tz = (
-    ('Pacific/Apia', _('(UTC-13:00) Samoa'), _('(UTC-14:00) Samoa')),
-    ('Pacific/Midway', _('(UTC-11:00) Midway Islands, American Samoa')),
-    ('Pacific/Honolulu', _('(UTC-10:00) Cook Islands, Hawaii, Society Islands')),
-    ('America/Adak', _('(UTC-10:00) Aleutian Islands'), _('(UTC-09:00) Aleutian Islands')),
-    ('Pacific/Marquesas', _('(UTC-09:30) Marquesas Islands')),
-    ('Pacific/Gambier', _('(UTC-09:00) Gambier Islands')),
-    ('America/Anchorage', _('(UTC-09:00) Alaska Standard Time'), _('(UTC-08:00) Alaska Daylight Time')),
-    ('Pacific/Pitcairn', _('(UTC-08:00) Pitcairn Islands')),
-    ('America/Los_Angeles', _('(UTC-08:00) Pacific Time (Canada and US)'), _('(UTC-07:00) Pacific Time (Canada and US)')),
-    ('America/Santa_Isabel', _('(UTC-08:00) Baja California'), _('(UTC-07:00) Baja California')),
-    ('America/Phoenix', _('(UTC-07:00) Mountain Standard Time (No DST)')),
-    ('America/Hermosillo', _('(UTC-07:00) Sonora')),
-    ('America/Denver', _('(UTC-07:00) Mountain Standard Time'), _('(UTC-06:00) Mountain Summer Time')),
-    ('America/Chihuahua', _('(UTC-07:00) Baja California Sur, Chihuahua, Nayarit, Sinaloa'), _('(UTC-06:00) Baja California Sur, Chihuahua, Nayarit, Sinaloa')),
-    ('America/Costa_Rica', _('(UTC-06:00) Costa Rica, El Salvador, Galapagos, Guatemala, Managua')),
-    ('America/Chicago', _('(UTC-06:00) Central Standard Time'), _('(UTC-05:00) Central Daylight Time')),
-    ('America/Mexico_City', _('(UTC-06:00) Mexican Central Zone'), _('(UTC-05:00) Mexican Central Zone')),
-    ('America/Panama', _('(UTC-05:00) Bogota, Cayman, Guayaquil, Jamaica, Lima, Panama')),
-    ('America/New_York', _('(UTC-05:00) Eastern Standard Time'), _('(UTC-04:00) Eastern Daylight Time')),
-    ('America/Caracas', _('(UTC-04:30) Caracas')),
-    ('America/Puerto_Rico', _('(UTC-04:00) Barbados, Dominica, Puerto Rico, Santo Domingo')),
-    ('America/Santiago', _('(UTC-04:00) Bermuda, Campo Grande, Goose Bay, Santiago, Thule'), _('(UTC-03:00) Bermuda, Campo Grande, Goose Bay, Santiago, Thule')),
-    ('America/St_Johns', _('(UTC-03:30) Newfoundland Time')),
-    ('America/Argentina/La_Rioja', _('(UTC-03:00) San Juan, San Luis, Santa Cruz')),
-    ('America/Sao_Paulo', _('(UTC-03:00) Buenos Aires, Godthab, Sao Paulo, Montevideo'), _('(UTC-02:00) Buenos Aires, Godthab, Sao Paulo, Montevideo')),
-    ('America/Noronha', _('(UTC-02:00) Atlantic islands')),
-    ('Atlantic/Cape_Verde', _('(UTC-01:00) Cape Verde Time')),
-    ('Atlantic/Azores', _('(UTC-01:00) Azores, Scoresbysund'), _('(UTC) Azores, Scoresbysund')),
-    ('utc', _('(UTC) Coordinated Universal Time')),
-    ('Africa/Dakar', _('(UTC) Dakar, Rabat')),
-    ('Europe/Lisbon', _('(UTC) Western European Time'), _('(UTC+01:00) Western European Summer Time')),
-    ('Africa/Algiers', _('(UTC+01:00) West Africa Time')),
-    ('Europe/Zurich', _('(UTC+01:00) Central European Time'), _('(UTC+02:00) Central European Summer Time')),
-    ('Africa/Cairo', _('(UTC+02:00) Central Africa Time')),
-    ('Europe/Athens', _('(UTC+02:00) Eastern European Time'), _('(UTC+03:00) Eastern European Summer Time')),
-    ('Asia/Qatar', _('(UTC+03:00) East Africa Time')),
-    ('Europe/Minsk', _('(UTC+03:00) Further-eastern European Time')),
-    ('Asia/Tehran', _('(UTC+3:30) Iran Time'), _('(UTC+4:30) Iran Time')),
-    ('Europe/Moscow', _('(UTC+4:00) Moscow Standard Time, Georgian Time')),
-    ('Asia/Dubai', _('(UTC+04:00) United Arab Emirates Standard Time')),
-    ('Asia/Baku', _('(UTC+05:00) Baku, Yerevan'), _('(UTC+06:00) Baku, Yerevan')),
-    ('Asia/Kabul', _('(UTC+04:30) Afghanistan Standard Time')),
-    ('Asia/Karachi', _('(UTC+05:00) Ashgabat, Dushanbe, Karachi, Maldives, Tashkent')),
-    ('Asia/Kolkata', _('(UTC+05:30) Colombo, Kolkata')),
-    ('Asia/Kathmandu', _('(UTC+05:45) Kathmandu')),
-    ('Asia/Almaty', _('(UTC+06:00) Astana, Bishkek, Dhaka, Thimphu, Yekaterinburg')),
-    ('Asia/Rangoon', _('(UTC+06:30) Yangon, Cocos Islands')),
-    ('Asia/Bangkok', _('(UTC+07:00) Bangkok, Ho Chi Minh, Jakarta, Novosibirsk')),
-    ('Asia/Taipei', _('(UTC+08:00) Beijing, Hong Kong, Kuala Lumpur, Singapore, Taipei')),
-    ('Australia/Perth', _('(UTC+08:00) Australian Western Standard Time')),
-    ('Australia/Eucla', _('(UTC+08:45) Eucla Area')),
-    ('Asia/Tokyo', _('(UTC+09:00) Tokyo, Seoul, Irkutsk, Pyongyang')),
-    ('Australia/Darwin', _('(UTC+09:30) Australian Central Standard Time')),
-    ('Australia/Adelaide', _('(UTC+09:30) Australian Central Standard Time')),
-    ('Australia/Melbourne', _('(UTC+10:00) Australian Eastern Standard Time'), _('(UTC+11:00) Australian Eastern Summer Time')),
-    ('Australia/Lord_Howe', _('(UTC+10:30) Lord Howe Island'), _('(UTC+11:00) Lord Howe Island')),
-    ('Pacific/Guadalcanal', _('(UTC+11:00) Guadalcanal, Honiara, Noumea, Vladivostok')),
-    ('Pacific/Norfolk', _('(UTC+11:30) Norfolk Island')),
-    ('Pacific/Wake', _('(UTC+12:00) Kamchatka, Marshall Islands')),
-    ('Pacific/Auckland', _('(UTC+12:00) Auckland, Fiji'), _('(UTC+13:00) Auckland, Fiji')),
-    ('Pacific/Chatham', _('(UTC+12:45) Chatham Islands'), _('(UTC+13:45) Chatham Islands')),
-    ('Pacific/Enderbury', _('(UTC+13:00) Phoenix Islands')),
-    ('Pacific/Kiritimati', _('(UTC+14:00) Nuku\'alofa')),
-)
-
-def tzlist():
-    """
-    Generate user-friendly timezone list for selects
-    """
-    ready_list = []
-    utc_dt = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
-    for tz in raw_tz:
-        if len(tz) == 3:
-            tzinfo = pytz.timezone(tz[0])
-            if utc_dt.astimezone(tzinfo).dst().seconds > 0:
-                ready_list.append((tz[0], tz[2]))
-            else:
-                ready_list.append((tz[0], tz[1]))
-        else:
-           ready_list.append((tz[0], tz[1]))
-    return tuple(ready_list)
+import datetime
+from django.utils.translation import ugettext_lazy as _
+from django.utils import timezone
+import pytz
+
+raw_tz = (
+    ('Pacific/Apia', _('(UTC-13:00) Samoa'), _('(UTC-14:00) Samoa')),
+    ('Pacific/Midway', _('(UTC-11:00) Midway Islands, American Samoa')),
+    ('Pacific/Honolulu', _('(UTC-10:00) Cook Islands, Hawaii, Society Islands')),
+    ('America/Adak', _('(UTC-10:00) Aleutian Islands'), _('(UTC-09:00) Aleutian Islands')),
+    ('Pacific/Marquesas', _('(UTC-09:30) Marquesas Islands')),
+    ('Pacific/Gambier', _('(UTC-09:00) Gambier Islands')),
+    ('America/Anchorage', _('(UTC-09:00) Alaska Standard Time'), _('(UTC-08:00) Alaska Daylight Time')),
+    ('Pacific/Pitcairn', _('(UTC-08:00) Pitcairn Islands')),
+    ('America/Los_Angeles', _('(UTC-08:00) Pacific Time (Canada and US)'), _('(UTC-07:00) Pacific Time (Canada and US)')),
+    ('America/Santa_Isabel', _('(UTC-08:00) Baja California'), _('(UTC-07:00) Baja California')),
+    ('America/Phoenix', _('(UTC-07:00) Mountain Standard Time (No DST)')),
+    ('America/Hermosillo', _('(UTC-07:00) Sonora')),
+    ('America/Denver', _('(UTC-07:00) Mountain Standard Time'), _('(UTC-06:00) Mountain Summer Time')),
+    ('America/Chihuahua', _('(UTC-07:00) Baja California Sur, Chihuahua, Nayarit, Sinaloa'), _('(UTC-06:00) Baja California Sur, Chihuahua, Nayarit, Sinaloa')),
+    ('America/Costa_Rica', _('(UTC-06:00) Costa Rica, El Salvador, Galapagos, Guatemala, Managua')),
+    ('America/Chicago', _('(UTC-06:00) Central Standard Time'), _('(UTC-05:00) Central Daylight Time')),
+    ('America/Mexico_City', _('(UTC-06:00) Mexican Central Zone'), _('(UTC-05:00) Mexican Central Zone')),
+    ('America/Panama', _('(UTC-05:00) Bogota, Cayman, Guayaquil, Jamaica, Lima, Panama')),
+    ('America/New_York', _('(UTC-05:00) Eastern Standard Time'), _('(UTC-04:00) Eastern Daylight Time')),
+    ('America/Caracas', _('(UTC-04:30) Caracas')),
+    ('America/Puerto_Rico', _('(UTC-04:00) Barbados, Dominica, Puerto Rico, Santo Domingo')),
+    ('America/Santiago', _('(UTC-04:00) Bermuda, Campo Grande, Goose Bay, Santiago, Thule'), _('(UTC-03:00) Bermuda, Campo Grande, Goose Bay, Santiago, Thule')),
+    ('America/St_Johns', _('(UTC-03:30) Newfoundland Time')),
+    ('America/Argentina/La_Rioja', _('(UTC-03:00) San Juan, San Luis, Santa Cruz')),
+    ('America/Sao_Paulo', _('(UTC-03:00) Buenos Aires, Godthab, Sao Paulo, Montevideo'), _('(UTC-02:00) Buenos Aires, Godthab, Sao Paulo, Montevideo')),
+    ('America/Noronha', _('(UTC-02:00) Atlantic islands')),
+    ('Atlantic/Cape_Verde', _('(UTC-01:00) Cape Verde Time')),
+    ('Atlantic/Azores', _('(UTC-01:00) Azores, Scoresbysund'), _('(UTC) Azores, Scoresbysund')),
+    ('utc', _('(UTC) Coordinated Universal Time')),
+    ('Africa/Dakar', _('(UTC) Dakar, Rabat')),
+    ('Europe/Lisbon', _('(UTC) Western European Time'), _('(UTC+01:00) Western European Summer Time')),
+    ('Africa/Algiers', _('(UTC+01:00) West Africa Time')),
+    ('Europe/Zurich', _('(UTC+01:00) Central European Time'), _('(UTC+02:00) Central European Summer Time')),
+    ('Africa/Cairo', _('(UTC+02:00) Central Africa Time')),
+    ('Europe/Athens', _('(UTC+02:00) Eastern European Time'), _('(UTC+03:00) Eastern European Summer Time')),
+    ('Asia/Qatar', _('(UTC+03:00) East Africa Time')),
+    ('Europe/Minsk', _('(UTC+03:00) Further-eastern European Time')),
+    ('Asia/Tehran', _('(UTC+3:30) Iran Time'), _('(UTC+4:30) Iran Time')),
+    ('Europe/Moscow', _('(UTC+4:00) Moscow Standard Time, Georgian Time')),
+    ('Asia/Dubai', _('(UTC+04:00) United Arab Emirates Standard Time')),
+    ('Asia/Baku', _('(UTC+05:00) Baku, Yerevan'), _('(UTC+06:00) Baku, Yerevan')),
+    ('Asia/Kabul', _('(UTC+04:30) Afghanistan Standard Time')),
+    ('Asia/Karachi', _('(UTC+05:00) Ashgabat, Dushanbe, Karachi, Maldives, Tashkent')),
+    ('Asia/Kolkata', _('(UTC+05:30) Colombo, Kolkata')),
+    ('Asia/Kathmandu', _('(UTC+05:45) Kathmandu')),
+    ('Asia/Almaty', _('(UTC+06:00) Astana, Bishkek, Dhaka, Thimphu, Yekaterinburg')),
+    ('Asia/Rangoon', _('(UTC+06:30) Yangon, Cocos Islands')),
+    ('Asia/Bangkok', _('(UTC+07:00) Bangkok, Ho Chi Minh, Jakarta, Novosibirsk')),
+    ('Asia/Taipei', _('(UTC+08:00) Beijing, Hong Kong, Kuala Lumpur, Singapore, Taipei')),
+    ('Australia/Perth', _('(UTC+08:00) Australian Western Standard Time')),
+    ('Australia/Eucla', _('(UTC+08:45) Eucla Area')),
+    ('Asia/Tokyo', _('(UTC+09:00) Tokyo, Seoul, Irkutsk, Pyongyang')),
+    ('Australia/Darwin', _('(UTC+09:30) Australian Central Standard Time')),
+    ('Australia/Adelaide', _('(UTC+09:30) Australian Central Standard Time')),
+    ('Australia/Melbourne', _('(UTC+10:00) Australian Eastern Standard Time'), _('(UTC+11:00) Australian Eastern Summer Time')),
+    ('Australia/Lord_Howe', _('(UTC+10:30) Lord Howe Island'), _('(UTC+11:00) Lord Howe Island')),
+    ('Pacific/Guadalcanal', _('(UTC+11:00) Guadalcanal, Honiara, Noumea, Vladivostok')),
+    ('Pacific/Norfolk', _('(UTC+11:30) Norfolk Island')),
+    ('Pacific/Wake', _('(UTC+12:00) Kamchatka, Marshall Islands')),
+    ('Pacific/Auckland', _('(UTC+12:00) Auckland, Fiji'), _('(UTC+13:00) Auckland, Fiji')),
+    ('Pacific/Chatham', _('(UTC+12:45) Chatham Islands'), _('(UTC+13:45) Chatham Islands')),
+    ('Pacific/Enderbury', _('(UTC+13:00) Phoenix Islands')),
+    ('Pacific/Kiritimati', _('(UTC+14:00) Nuku\'alofa')),
+)
+
+def tzlist():
+    """
+    Generate user-friendly timezone list for selects
+    """
+    ready_list = []
+    utc_dt = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
+    for tz in raw_tz:
+        if len(tz) == 3:
+            tzinfo = pytz.timezone(tz[0])
+            if utc_dt.astimezone(tzinfo).dst().seconds > 0:
+                ready_list.append((tz[0], tz[2]))
+            else:
+                ready_list.append((tz[0], tz[1]))
+        else:
+           ready_list.append((tz[0], tz[1]))
+    return tuple(ready_list)

+ 19 - 19
misago/utils/translation.py

@@ -1,20 +1,20 @@
-from django.utils import translation
-
-def ugettext_lazy(string):
-    """
-    Custom wrapper that preserves untranslated message on lazy translation string object
-    """
-    t = translation.ugettext_lazy(string)
-    t.message = string
-    return t
-
-
-def get_msgid(gettext):
-    """
-    Function for extracting untranslated message from lazy translation string object
-    made trough ugettext_lazy
-    """
-    try:
-        return gettext.message
-    except AttributeError:
+from django.utils import translation
+
+def ugettext_lazy(string):
+    """
+    Custom wrapper that preserves untranslated message on lazy translation string object
+    """
+    t = translation.ugettext_lazy(string)
+    t.message = string
+    return t
+
+
+def get_msgid(gettext):
+    """
+    Function for extracting untranslated message from lazy translation string object
+    made trough ugettext_lazy
+    """
+    try:
+        return gettext.message
+    except AttributeError:
         return None
         return None

+ 23 - 23
misago/utils/urls.py

@@ -1,24 +1,24 @@
-#-*- coding: utf-8 -*-
-import re
-from urlparse import urlparse
-from django.conf import settings
-from misago.utils.strings import html_escape
-
-URL_RE = re.compile(r'^(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))$', re.UNICODE)
-
-def is_url(string):
-    return URL_RE.search(string.strip()) != None
-
-
-def is_inner(string):
-    return urlparse(string.strip()).netloc.lower() == urlparse(settings.BOARD_ADDRESS.lower()).netloc
-
-
-def clean_inner(string):
-    parsed = urlparse(string.strip())
-    href = parsed.path
-    if parsed.query:
-        href += '?%s' % parsed.query
-    if parsed.fragment:
-        href += '#%s' % parsed.fragment
+#-*- coding: utf-8 -*-
+import re
+from urlparse import urlparse
+from django.conf import settings
+from misago.utils.strings import html_escape
+
+URL_RE = re.compile(r'^(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))$', re.UNICODE)
+
+def is_url(string):
+    return URL_RE.search(string.strip()) != None
+
+
+def is_inner(string):
+    return urlparse(string.strip()).netloc.lower() == urlparse(settings.BOARD_ADDRESS.lower()).netloc
+
+
+def clean_inner(string):
+    parsed = urlparse(string.strip())
+    href = parsed.path
+    if parsed.query:
+        href += '?%s' % parsed.query
+    if parsed.fragment:
+        href += '#%s' % parsed.fragment
     return html_escape(href)
     return html_escape(href)

+ 16 - 16
misago/utils/views.py

@@ -1,16 +1,16 @@
-from json import dumps as json_dumps
-from django.core.urlresolvers import reverse
-from django.http import HttpResponse
-from django.shortcuts import redirect
-from django.template import RequestContext
-
-def redirect_message(request, message, type='info', owner=None):
-    request.messages.set_flash(message, type, owner)
-    return redirect(reverse('index'))
-
-
-def json_response(request, json=None, status=200, message=None):
-    json = json or {}
-    json.update({'code': status, 'message': unicode(message)})
-    response = json_dumps(json, sort_keys=True,  ensure_ascii=False)
-    return HttpResponse(response, content_type='application/json', status=status)
+from json import dumps as json_dumps
+from django.core.urlresolvers import reverse
+from django.http import HttpResponse
+from django.shortcuts import redirect
+from django.template import RequestContext
+
+def redirect_message(request, message, type='info', owner=None):
+    request.messages.set_flash(message, type, owner)
+    return redirect(reverse('index'))
+
+
+def json_response(request, json=None, status=200, message=None):
+    json = json or {}
+    json.update({'code': status, 'message': unicode(message)})
+    response = json_dumps(json, sort_keys=True,  ensure_ascii=False)
+    return HttpResponse(response, content_type='application/json', status=status)

+ 83 - 83
misago/validators.py

@@ -1,83 +1,83 @@
-import re
-from django.core.exceptions import ValidationError
-from django.utils.translation import ungettext, ugettext_lazy as _
-from misago.conf import settings
-from misago.models import Ban
-from misago.utils.strings import slugify
-
-class validate_sluggable(object):
-    def __init__(self, error_short=None, error_long=None):
-        self.error_short = error_short if error_short else _("Value has to contain alpha-numerical characters.")
-        self.error_long = error_long if error_long else _("Value is too long.")
-
-    def __call__(self, value):
-        slug = slugify(value)
-        if not slug:
-            raise ValidationError(self.error_short)
-        if len(slug) > 255:
-            raise ValidationError(self.error_long)
-
-
-def validate_username(value):
-    value = unicode(value).strip()
-
-    if len(value) < settings.username_length_min:
-        raise ValidationError(ungettext(
-            'Username must be at least one character long.',
-            'Username must be at least %(count)d characters long.',
-            settings.username_length_min
-        ) % {
-            'count': settings.username_length_min,
-        })
-
-    if len(value) > settings.username_length_max:
-        raise ValidationError(ungettext(
-            'Username cannot be longer than one characters.',
-            'Username cannot be longer than %(count)d characters.',
-            settings.username_length_max
-        ) % {
-            'count': settings.username_length_max,
-        })
-
-    if settings.UNICODE_USERNAMES:
-        if not re.search('^[^\W_]+$', value, re.UNICODE):
-            raise ValidationError(_("Username can only contain letters and digits."))
-    else:
-        if not re.search('^[^\W_]+$', value):
-            raise ValidationError(_("Username can only contain latin alphabet letters and digits."))
-    
-    if Ban.objects.check_ban(username=value):
-        raise ValidationError(_("This username is forbidden."))
-
-
-def validate_password(value):
-    value = unicode(value).strip()
-
-    if len(value) < settings.password_length:
-        raise ValidationError(ungettext(
-            'Correct password has to be at least one character long.',
-            'Correct password has to be at least %(count)d characters long.',
-            settings.password_length
-        ) % {
-            'count': settings.password_length,
-        })
-
-    for test in settings.password_complexity:
-        if test in ('case', 'digits', 'special'):
-            if not re.search('[a-zA-Z]', value):
-                raise ValidationError(_("Password must contain alphabetical characters."))
-            if test == 'case':
-                if not (re.search('[a-z]', value) and re.search('[A-Z]', value)):
-                    raise ValidationError(_("Password must contain characters that have different case."))
-            if test == 'digits':
-                if not re.search('[0-9]', value):
-                    raise ValidationError(_("Password must contain digits in addition to characters."))
-            if test == 'special':
-                if not re.search('[^0-9a-zA-Z]', value):
-                    raise ValidationError(_("Password must contain special (non alphanumerical) characters."))
-
-
-def validate_email(value):
-    value = unicode(value).strip()
-    if Ban.objects.check_ban(email=value):
-        raise ValidationError(_("This board forbids registrations using this e-mail address."))
+import re
+from django.core.exceptions import ValidationError
+from django.utils.translation import ungettext, ugettext_lazy as _
+from misago.conf import settings
+from misago.models import Ban
+from misago.utils.strings import slugify
+
+class validate_sluggable(object):
+    def __init__(self, error_short=None, error_long=None):
+        self.error_short = error_short if error_short else _("Value has to contain alpha-numerical characters.")
+        self.error_long = error_long if error_long else _("Value is too long.")
+
+    def __call__(self, value):
+        slug = slugify(value)
+        if not slug:
+            raise ValidationError(self.error_short)
+        if len(slug) > 255:
+            raise ValidationError(self.error_long)
+
+
+def validate_username(value):
+    value = unicode(value).strip()
+
+    if len(value) < settings.username_length_min:
+        raise ValidationError(ungettext(
+            'Username must be at least one character long.',
+            'Username must be at least %(count)d characters long.',
+            settings.username_length_min
+        ) % {
+            'count': settings.username_length_min,
+        })
+
+    if len(value) > settings.username_length_max:
+        raise ValidationError(ungettext(
+            'Username cannot be longer than one characters.',
+            'Username cannot be longer than %(count)d characters.',
+            settings.username_length_max
+        ) % {
+            'count': settings.username_length_max,
+        })
+
+    if settings.UNICODE_USERNAMES:
+        if not re.search('^[^\W_]+$', value, re.UNICODE):
+            raise ValidationError(_("Username can only contain letters and digits."))
+    else:
+        if not re.search('^[^\W_]+$', value):
+            raise ValidationError(_("Username can only contain latin alphabet letters and digits."))
+    
+    if Ban.objects.check_ban(username=value):
+        raise ValidationError(_("This username is forbidden."))
+
+
+def validate_password(value):
+    value = unicode(value).strip()
+
+    if len(value) < settings.password_length:
+        raise ValidationError(ungettext(
+            'Correct password has to be at least one character long.',
+            'Correct password has to be at least %(count)d characters long.',
+            settings.password_length
+        ) % {
+            'count': settings.password_length,
+        })
+
+    for test in settings.password_complexity:
+        if test in ('case', 'digits', 'special'):
+            if not re.search('[a-zA-Z]', value):
+                raise ValidationError(_("Password must contain alphabetical characters."))
+            if test == 'case':
+                if not (re.search('[a-z]', value) and re.search('[A-Z]', value)):
+                    raise ValidationError(_("Password must contain characters that have different case."))
+            if test == 'digits':
+                if not re.search('[0-9]', value):
+                    raise ValidationError(_("Password must contain digits in addition to characters."))
+            if test == 'special':
+                if not re.search('[^0-9a-zA-Z]', value):
+                    raise ValidationError(_("Password must contain special (non alphanumerical) characters."))
+
+
+def validate_email(value):
+    value = unicode(value).strip()
+    if Ban.objects.check_ban(email=value):
+        raise ValidationError(_("This board forbids registrations using this e-mail address."))

+ 14 - 14
requirements.txt

@@ -1,15 +1,15 @@
-django
-django-debug-toolbar
-django-floppyforms
-django-haystack
-django-jinja
-django-mptt
-jinja2
-markdown
-path.py
-Pillow
-pytz
-recaptcha-client
-South
-Unidecode
+django
+django-debug-toolbar
+django-floppyforms
+django-haystack
+django-jinja
+django-mptt
+jinja2
+markdown
+path.py
+Pillow
+pytz
+recaptcha-client
+South
+Unidecode
 whoosh<2.5
 whoosh<2.5

+ 91 - 91
static/admin/css/admin.less

@@ -1,92 +1,92 @@
-/*!
- * Bootstrap v2.1.0
- *
- * Copyright 2012 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
- */
-
-// CSS Reset
-@import "bootstrap/reset.less";
-
-// Core variables and mixins
-@import "variables.less";
-@import "bootstrap/mixins.less";
-
-// Grid system and page structure
-@import "bootstrap/scaffolding.less";
-@import "bootstrap/grid.less";
-@import "bootstrap/layouts.less";
-
-// Base CSS
-@import "bootstrap/type.less";
-@import "bootstrap/code.less";
-@import "bootstrap/forms.less";
-@import "bootstrap/tables.less";
-
-// Components: common
-@import "bootstrap/sprites.less";
-@import "bootstrap/dropdowns.less";
-@import "bootstrap/wells.less";
-@import "bootstrap/component-animations.less";
-@import "bootstrap/close.less";
-
-// Components: Buttons & Alerts
-@import "bootstrap/buttons.less";
-@import "bootstrap/button-groups.less";
-@import "bootstrap/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
-
-// Components: Nav
-@import "bootstrap/navs.less";
-@import "bootstrap/navbar.less";
-@import "bootstrap/breadcrumbs.less";
-@import "bootstrap/pagination.less";
-@import "bootstrap/pager.less";
-
-// Components: Popovers
-@import "bootstrap/modals.less";
-@import "bootstrap/tooltip.less";
-@import "bootstrap/popovers.less";
-
-// Components: Misc
-@import "bootstrap/thumbnails.less";
-@import "bootstrap/labels-badges.less";
-@import "bootstrap/progress-bars.less";
-@import "bootstrap/accordion.less";
-@import "bootstrap/carousel.less";
-@import "bootstrap/hero-unit.less";
-
-// Utility classes
-@import "bootstrap/utilities.less"; // Has to be last to override when necessary
-
-// Responsiveness
-@import "bootstrap/responsive-768px-979px.less";
-@import "bootstrap/responsive-1200px-min.less";
-
-
-// Yes-no Switch
-@import "bootstrap-toggle-buttons.css";
-
-
-// Admin theme
-@import "admin/scaffolding.less";
-
-@import "admin/forms.less";
-@import "admin/tables.less";
-
-@import "admin/wells.less";
-@import "admin/users-lists.less";
-
-@import "admin/buttons.less";
-@import "admin/alerts.less";
-
-@import "admin/avatars.less";
-@import "admin/navs.less";
-@import "admin/navbar.less";
-
-@import "admin/popovers.less";
-@import "admin/graphs.less";
-
+/*!
+ * Bootstrap v2.1.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+// CSS Reset
+@import "bootstrap/reset.less";
+
+// Core variables and mixins
+@import "variables.less";
+@import "bootstrap/mixins.less";
+
+// Grid system and page structure
+@import "bootstrap/scaffolding.less";
+@import "bootstrap/grid.less";
+@import "bootstrap/layouts.less";
+
+// Base CSS
+@import "bootstrap/type.less";
+@import "bootstrap/code.less";
+@import "bootstrap/forms.less";
+@import "bootstrap/tables.less";
+
+// Components: common
+@import "bootstrap/sprites.less";
+@import "bootstrap/dropdowns.less";
+@import "bootstrap/wells.less";
+@import "bootstrap/component-animations.less";
+@import "bootstrap/close.less";
+
+// Components: Buttons & Alerts
+@import "bootstrap/buttons.less";
+@import "bootstrap/button-groups.less";
+@import "bootstrap/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
+
+// Components: Nav
+@import "bootstrap/navs.less";
+@import "bootstrap/navbar.less";
+@import "bootstrap/breadcrumbs.less";
+@import "bootstrap/pagination.less";
+@import "bootstrap/pager.less";
+
+// Components: Popovers
+@import "bootstrap/modals.less";
+@import "bootstrap/tooltip.less";
+@import "bootstrap/popovers.less";
+
+// Components: Misc
+@import "bootstrap/thumbnails.less";
+@import "bootstrap/labels-badges.less";
+@import "bootstrap/progress-bars.less";
+@import "bootstrap/accordion.less";
+@import "bootstrap/carousel.less";
+@import "bootstrap/hero-unit.less";
+
+// Utility classes
+@import "bootstrap/utilities.less"; // Has to be last to override when necessary
+
+// Responsiveness
+@import "bootstrap/responsive-768px-979px.less";
+@import "bootstrap/responsive-1200px-min.less";
+
+
+// Yes-no Switch
+@import "bootstrap-toggle-buttons.css";
+
+
+// Admin theme
+@import "admin/scaffolding.less";
+
+@import "admin/forms.less";
+@import "admin/tables.less";
+
+@import "admin/wells.less";
+@import "admin/users-lists.less";
+
+@import "admin/buttons.less";
+@import "admin/alerts.less";
+
+@import "admin/avatars.less";
+@import "admin/navs.less";
+@import "admin/navbar.less";
+
+@import "admin/popovers.less";
+@import "admin/graphs.less";
+
 @import "admin/utilities.less";
 @import "admin/utilities.less";

+ 62 - 62
static/admin/css/admin/alerts.less

@@ -1,62 +1,62 @@
-// Misago alerts
-// -------------------------
-.alerts-global {
-  margin-top: 16px;
-}
-
-.alert-form {
-  margin: 0px;
-  margin-bottom: 16px;
-  
-  font-weight: bold;
-}
-
-.alert-inline {
-  margin: 0px;
-  padding: 0px;
-}
-
-// Alerts icons
-// -------------------------
-.alert-icon {
-  float: left;
-  
-  span {
-    .border-radius(2px);
-    padding: 1px 2px; 
-  }
-}
-
-.alert .alert-icon span {
-  background: @orange;
-}
-
-.alert-error .alert-icon span {
-  background: @red;  
-}
-
-.alert-success .alert-icon span {
-  background: @green;  
-}
-
-.alert-info .alert-icon span {
-  background: @blue;  
-}
-
-// Alerts paddings
-// -------------------------
-.alert {
-  padding: 10px 11px;
-  
-  p {
-    margin-left: 28px;
-  }
-  
-  p:last-child {
-    margin-bottom: 0px;
-  }
-  
-  p.protip {
-    font-size: 100%;
-  }
-}
+// Misago alerts
+// -------------------------
+.alerts-global {
+  margin-top: 16px;
+}
+
+.alert-form {
+  margin: 0px;
+  margin-bottom: 16px;
+  
+  font-weight: bold;
+}
+
+.alert-inline {
+  margin: 0px;
+  padding: 0px;
+}
+
+// Alerts icons
+// -------------------------
+.alert-icon {
+  float: left;
+  
+  span {
+    .border-radius(2px);
+    padding: 1px 2px; 
+  }
+}
+
+.alert .alert-icon span {
+  background: @orange;
+}
+
+.alert-error .alert-icon span {
+  background: @red;  
+}
+
+.alert-success .alert-icon span {
+  background: @green;  
+}
+
+.alert-info .alert-icon span {
+  background: @blue;  
+}
+
+// Alerts paddings
+// -------------------------
+.alert {
+  padding: 10px 11px;
+  
+  p {
+    margin-left: 28px;
+  }
+  
+  p:last-child {
+    margin-bottom: 0px;
+  }
+  
+  p.protip {
+    font-size: 100%;
+  }
+}

+ 65 - 65
static/admin/css/admin/avatars.less

@@ -1,65 +1,65 @@
-// Misago avatars sizes
-// -------------------------
-img{
-  &.avatar-big {
-    border-radius: 3px;
-    width: 180px;
-    height: 180px;
-  }
-  
-  &.avatar-normal {
-    border-radius: 3px;
-    width: 64px;
-    height: 64px;
-  }
-  
-  &.avatar-small {
-    border-radius: 3px;
-    width: 28px;
-    height: 28px;
-  }
-  
-  &.avatar-tiny {
-    border-radius: 3px;
-    width: 16px;
-    height: 16px;
-  }
-}
-
-// Columns widths
-td, th {
-  &.avatar-big {width: 180px;}
-  &.avatar-normal {width: 72px;}
-  &.avatar-small {width: 44px;}
-  &.avatar-tiny {width: 32px;}
-  
-  &.avatar-big,
-  &.avatar-normal,
-  &.avatar-small,
-  &.avatar-tiny {
-    padding-left: 0px;
-    padding-right: 0px;
-    
-    text-align: center;
-  }
-}
-
-// Handle small avatar within different places
-// -------------------------
-.navbar {
-  img.avatar-small {
-    margin: -(@baseLineHeight / 2) 0px;
-    position: relative;
-    bottom: 1px;
-  }
-}
-
-.page-header {
-  .avatar-normal {
-    width: 42px;
-    height: 42px;
-    margin: -(@baseLineHeight / 2) 0px;
-    position: relative;
-    top: (@baseLineHeight / 2) - 13;
-  }
-}
+// Misago avatars sizes
+// -------------------------
+img{
+  &.avatar-big {
+    border-radius: 3px;
+    width: 180px;
+    height: 180px;
+  }
+  
+  &.avatar-normal {
+    border-radius: 3px;
+    width: 64px;
+    height: 64px;
+  }
+  
+  &.avatar-small {
+    border-radius: 3px;
+    width: 28px;
+    height: 28px;
+  }
+  
+  &.avatar-tiny {
+    border-radius: 3px;
+    width: 16px;
+    height: 16px;
+  }
+}
+
+// Columns widths
+td, th {
+  &.avatar-big {width: 180px;}
+  &.avatar-normal {width: 72px;}
+  &.avatar-small {width: 44px;}
+  &.avatar-tiny {width: 32px;}
+  
+  &.avatar-big,
+  &.avatar-normal,
+  &.avatar-small,
+  &.avatar-tiny {
+    padding-left: 0px;
+    padding-right: 0px;
+    
+    text-align: center;
+  }
+}
+
+// Handle small avatar within different places
+// -------------------------
+.navbar {
+  img.avatar-small {
+    margin: -(@baseLineHeight / 2) 0px;
+    position: relative;
+    bottom: 1px;
+  }
+}
+
+.page-header {
+  .avatar-normal {
+    width: 42px;
+    height: 42px;
+    margin: -(@baseLineHeight / 2) 0px;
+    position: relative;
+    top: (@baseLineHeight / 2) - 13;
+  }
+}

+ 102 - 102
static/admin/css/admin/buttons.less

@@ -1,103 +1,103 @@
-.button-colors (@btnColor) {
-  background: @btnColor;
-  border: 1px solid @btnColor;
-  *border: 0;
-}
-
-.button-icon (@default, @hover: 100) {
-  i {
-    .opacity(@default);
-  }
-  
-  &:hover, &:active {
-    i {
-      .opacity(@hover);
-    }
-  }
-}
-
-.button-style (@background, @highlight) {
-  .button-colors(@background);
-
-  color: darken(@white, 3%);
-  text-shadow: 0px 1px 0px darken(@background, 5%);
-
-  &:hover, &:active {
-    .button-colors(@highlight);
-  }
-}
-
-.btn {
-  .button-colors(@btnBackground);
-  .box-shadow(none);
-  padding: 4px 10px;
-  
-  color: lighten(@gray, 10%);
-  font-weight: bold;
-  text-shadow: none;
-  
-  &:hover, &:active {
-    .button-colors(@btnBackgroundHighlight);
-    box-shadow: none;
-  
-    color: @linkColor;
-  }
-  
-  .button-icon(70);
-  
-  &.btn-primary,
-  &.btn-info,
-  &.btn-success,
-  &.btn-warning,
-  &.btn-danger,
-  &.btn-inverse {
-    .button-icon(90);
-  
-    color: darken(@white, 5%);
-  
-    &:hover, &:active {  
-      color: @white;
-    }
-  }
-  
-  &.btn-primary {
-    .button-colors(@btnPrimaryBackground);
-    .button-style(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight);
-  }
-  
-  &.btn-info {
-    .button-colors(@btnInfoBackground);
-    .button-style(@btnInfoBackground, @btnInfoBackgroundHighlight);
-  }
-  
-  &.btn-success {
-    .button-colors(@btnSuccessBackground);
-    .button-style(@btnSuccessBackground, @btnSuccessBackgroundHighlight);
-  }
-  
-  &.btn-warning {
-    .button-colors(@btnWarningBackground);
-    .button-style(@btnWarningBackground, @btnWarningBackgroundHighlight);
-  }
-  
-  &.btn-danger {
-    .button-colors(@btnDangerBackground);
-    .button-style(@btnDangerBackground, @btnDangerBackgroundHighlight);
-  }
-  
-  &.btn-inverse {
-    .button-colors(@btnInverseBackground);
-    .button-style(@btnInverseBackground, @btnInverseBackgroundHighlight);
-  }
-  
-  &.btn-link {
-    background: none;
-    border: none;
-  
-    &:hover, &:active {
-      color: @linkColorHover;
-      
-      text-decoration: none;
-    }
-  }
+.button-colors (@btnColor) {
+  background: @btnColor;
+  border: 1px solid @btnColor;
+  *border: 0;
+}
+
+.button-icon (@default, @hover: 100) {
+  i {
+    .opacity(@default);
+  }
+  
+  &:hover, &:active {
+    i {
+      .opacity(@hover);
+    }
+  }
+}
+
+.button-style (@background, @highlight) {
+  .button-colors(@background);
+
+  color: darken(@white, 3%);
+  text-shadow: 0px 1px 0px darken(@background, 5%);
+
+  &:hover, &:active {
+    .button-colors(@highlight);
+  }
+}
+
+.btn {
+  .button-colors(@btnBackground);
+  .box-shadow(none);
+  padding: 4px 10px;
+  
+  color: lighten(@gray, 10%);
+  font-weight: bold;
+  text-shadow: none;
+  
+  &:hover, &:active {
+    .button-colors(@btnBackgroundHighlight);
+    box-shadow: none;
+  
+    color: @linkColor;
+  }
+  
+  .button-icon(70);
+  
+  &.btn-primary,
+  &.btn-info,
+  &.btn-success,
+  &.btn-warning,
+  &.btn-danger,
+  &.btn-inverse {
+    .button-icon(90);
+  
+    color: darken(@white, 5%);
+  
+    &:hover, &:active {  
+      color: @white;
+    }
+  }
+  
+  &.btn-primary {
+    .button-colors(@btnPrimaryBackground);
+    .button-style(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight);
+  }
+  
+  &.btn-info {
+    .button-colors(@btnInfoBackground);
+    .button-style(@btnInfoBackground, @btnInfoBackgroundHighlight);
+  }
+  
+  &.btn-success {
+    .button-colors(@btnSuccessBackground);
+    .button-style(@btnSuccessBackground, @btnSuccessBackgroundHighlight);
+  }
+  
+  &.btn-warning {
+    .button-colors(@btnWarningBackground);
+    .button-style(@btnWarningBackground, @btnWarningBackgroundHighlight);
+  }
+  
+  &.btn-danger {
+    .button-colors(@btnDangerBackground);
+    .button-style(@btnDangerBackground, @btnDangerBackgroundHighlight);
+  }
+  
+  &.btn-inverse {
+    .button-colors(@btnInverseBackground);
+    .button-style(@btnInverseBackground, @btnInverseBackgroundHighlight);
+  }
+  
+  &.btn-link {
+    background: none;
+    border: none;
+  
+    &:hover, &:active {
+      color: @linkColorHover;
+      
+      text-decoration: none;
+    }
+  }
 }
 }

+ 105 - 105
static/admin/css/admin/forms.less

@@ -1,105 +1,105 @@
-// Misago forms
-// -------------------------
-form {
-  label {
-    color: @gray;
-    font-weight: bold;
-    cursor: pointer;
-  }
-  
-  fieldset {
-    border-top: 1px solid darken(@bodyBackground, 8%);
-    margin: 0px;
-    padding: 0px;
-    padding-top: 16px;
-    padding-bottom: 8px;
-  
-    legend {
-      margin: 0px;
-      margin-bottom: -8px;
-      padding: 0px;
-      padding-top: 8px;
-    }
-        
-    .control-group {
-      padding-bottom: 4px;
-    }
-    
-    .control-group:last-child {
-      padding-bottom: 0px;
-    }
-  }
-  
-  fieldset.first {
-    border-top: none;
-    padding-top: 0px;
-  }
-  
-  fieldset.last {
-    padding-bottom: 0px;
-    margin-bottom: 0px;
-  }
-  
-  .form-actions {
-    margin-top: -4px;
-  }
-}
-
-// Make textarea resizeable vertically only
-// -------------------------
-textarea {
-  resize: vertical;
-}
-
-
-// Lists styling
-// -------------------------
-.radio-group, .select-multiple, .yes-no-switch {
-  label {
-    color: @black;
-    font-weight: normal;
-  }
-  
-  margin-bottom: 8px;
-}
-
-.checkbox {
-  color: @black;
-  font-weight: normal;
-}
-
-// Side-search form
-// -------------------------
-.side-search {
-  background: @white;
-  border: 1px solid darken(@bodyBackground, 15%);
-  .border-radius(4px);
-  .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 5%));
-  padding: 8px;
-  margin-left: 14px;
-  margin-right: -14px;
-  
-  h4 {
-    border-bottom: 1px solid darken(@bodyBackground, 15%);
-    padding-top: 0px;
-    padding-bottom: 8px;
-    margin-top: 0px;
-  }
-  
-  hr {
-    border-top: 1px solid darken(@bodyBackground, 7%);
-    margin-bottom: 16px;
-  }
-    
-  label.checkbox, label.radio {
-    font-weight: normal;
-  }
-    
-  .form-actions {
-    background: darken(@bodyBackground, 3%);
-    .border-radius(0px 0px 3px 3px);
-    padding: 12px 8px;
-    margin: -8px;
-    margin-top: 0px;
-  }
-}
+// Misago forms
+// -------------------------
+form {
+  label {
+    color: @gray;
+    font-weight: bold;
+    cursor: pointer;
+  }
+  
+  fieldset {
+    border-top: 1px solid darken(@bodyBackground, 8%);
+    margin: 0px;
+    padding: 0px;
+    padding-top: 16px;
+    padding-bottom: 8px;
+  
+    legend {
+      margin: 0px;
+      margin-bottom: -8px;
+      padding: 0px;
+      padding-top: 8px;
+    }
+        
+    .control-group {
+      padding-bottom: 4px;
+    }
+    
+    .control-group:last-child {
+      padding-bottom: 0px;
+    }
+  }
+  
+  fieldset.first {
+    border-top: none;
+    padding-top: 0px;
+  }
+  
+  fieldset.last {
+    padding-bottom: 0px;
+    margin-bottom: 0px;
+  }
+  
+  .form-actions {
+    margin-top: -4px;
+  }
+}
+
+// Make textarea resizeable vertically only
+// -------------------------
+textarea {
+  resize: vertical;
+}
+
+
+// Lists styling
+// -------------------------
+.radio-group, .select-multiple, .yes-no-switch {
+  label {
+    color: @black;
+    font-weight: normal;
+  }
+  
+  margin-bottom: 8px;
+}
+
+.checkbox {
+  color: @black;
+  font-weight: normal;
+}
+
+// Side-search form
+// -------------------------
+.side-search {
+  background: @white;
+  border: 1px solid darken(@bodyBackground, 15%);
+  .border-radius(4px);
+  .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 5%));
+  padding: 8px;
+  margin-left: 14px;
+  margin-right: -14px;
+  
+  h4 {
+    border-bottom: 1px solid darken(@bodyBackground, 15%);
+    padding-top: 0px;
+    padding-bottom: 8px;
+    margin-top: 0px;
+  }
+  
+  hr {
+    border-top: 1px solid darken(@bodyBackground, 7%);
+    margin-bottom: 16px;
+  }
+    
+  label.checkbox, label.radio {
+    font-weight: normal;
+  }
+    
+  .form-actions {
+    background: darken(@bodyBackground, 3%);
+    .border-radius(0px 0px 3px 3px);
+    padding: 12px 8px;
+    margin: -8px;
+    margin-top: 0px;
+  }
+}

+ 28 - 28
static/admin/css/admin/graphs.less

@@ -1,28 +1,28 @@
-// Graphs
-// --------------------------------------------------
-.graph {
-  background: #FFF;
-  border: 1px solid #DDD;
-  .border-radius(3px);
-  padding: 5px;
-
-  .timeline {
-    overflow: auto;
-    margin-top: -12px;
-    padding: 0px 12px;
-  }
-
-  .peak {
-    background: #FFF;
-    border: 4px solid #F00;
-    .border-radius(12px);
-	position: absolute;
-    cursor: pointer;
-    width: 7px;
-    height: 7px;
-  }
-}
-
-.sub-graph {
-  margin-top: 8px;
-}
+// Graphs
+// --------------------------------------------------
+.graph {
+  background: #FFF;
+  border: 1px solid #DDD;
+  .border-radius(3px);
+  padding: 5px;
+
+  .timeline {
+    overflow: auto;
+    margin-top: -12px;
+    padding: 0px 12px;
+  }
+
+  .peak {
+    background: #FFF;
+    border: 4px solid #F00;
+    .border-radius(12px);
+	position: absolute;
+    cursor: pointer;
+    width: 7px;
+    height: 7px;
+  }
+}
+
+.sub-graph {
+  margin-top: 8px;
+}

+ 171 - 171
static/admin/css/admin/navbar.less

@@ -1,172 +1,172 @@
-// Admin sections and user navbar
-// -------------------------
-.navbar-sections {    
-  // Style brand
-  .brand {
-    margin-right: -10px;
-    
-    color: @grayDark;
-    font-size: 200%;
-    
-    span {
-      color: @grayLight;
-      font-size: 50%;
-      line-height: 50%;
-    }
-  }
-  
-  // Signed-in administrator profile 
-  .user-profile {
-    padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px 3px;
-    margin: 15px 5px;
-    
-    color: @grayDarker;
-    font-weight: bold;
-  }
-      
-  // Make links look better
-  .nav {
-    // Drop side margins so we fit 1024px clients
-    margin-left: 0px;
-    margin-right: 0px;
-    
-    li {
-      a, a:link, a:visited, a:active {
-        .opacity(50);
-        padding: ((@navbarHeight - @baseLineHeight) / 2) 10px ((@navbarHeight - @baseLineHeight) / 2);
-        margin: 0px 5px;
-        
-        font-weight: bold;
-        text-shadow: 0px 1px 0px @white;
-      }
-
-      a:focus, a:hover {
-        background-color: @navbarLinkBackgroundHover;
-        .border-radius(4px);
-        padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px;
-        margin: 15px 5px;
-        .opacity(100);
-      }
-      
-      form {
-        margin: 0px;
-        padding: 0px;
-      }
-    }
-    
-    li.active {
-      a, a:link, a:active, a:visited, a:hover {
-        background-color: @navbarLinkBackgroundActive;
-        .border-radius(4px);
-        .box-shadow(none);
-        padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px;
-        margin: 15px 0px;
-        .opacity(100);
-          
-        text-shadow: 0px 1px 0px darken(@navbarLinkBackgroundActive, 5%);
-        
-        i {
-          background-image: url("@{iconWhiteSpritePath}");
-          .opacity(100);
-        }
-      }
-    }
-  }  
-    
-  // Sign-out button
-  .btn-link, .btn-link:link, .btn-link:visited {
-    .border-radius(4px);
-    padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 8px ((@navbarHeight - @baseLineHeight) / 2)-15px;
-    margin: 15px 0px;
-    .opacity(60);
-    
-    color: @textColor;
-    font-weight: bold;
-    font-size: 100%;
-    
-    i {
-      .opacity(100);
-    }
-  }
-  
-  .btn-link:active, .btn-link:hover {
-    background: @navbarLinkBackgroundHover;
-    .opacity(100);
-    
-    color: @textColor;
-  }
-}
-
-// Admin section actions navbar
-// -------------------------
-.navbar-actions {  
-  border-bottom: 1px solid @navbarInverseBorder;
-  
-  // Fix inverse navbar height
-  .navbar-inner {
-    min-height: @navbarInverseHeight;
-  }
-  
-  .nav {
-    li {
-      a, a:link, a:active, a:visited {
-        .opacity(50);
-        padding: ((@navbarInverseHeight - @baseLineHeight) / 2) 15px ((@navbarInverseHeight - @baseLineHeight) / 2);
-        
-        color: @black;
-        font-weight: bold;
-        text-shadow: 0px 1px 0px @grayLighter;
-      }
-      
-      a:hover {        
-        .opacity(100);
-        
-      }
-      
-      form {
-        margin: 0px;
-        padding: 0px;
-      }
-    }
-    
-    li.active {      
-      a, a:link, a:active, a:visited, a:hover {
-        border-bottom: 4px solid @linkColor;
-        .box-shadow(none);
-        .opacity(100);
-        padding-bottom: ((@navbarInverseHeight - @baseLineHeight) / 2) - 3px;
-        margin-bottom: -1px;
-        
-        color: @grayDark;
-        text-shadow: 0px 1px 0px @white;
-        
-        i {
-          background-image: url("@{iconSpritePath}");
-          .opacity(100);
-        }
-      }
-    }
-  }
-}
-
-// Both Navbars
-// -------------------------
-.navbar {
-  // Inner style
-  .navbar-inner {
-    border: none;
-    background: none;
-    background-color: @navbarBackground;
-    .box-shadow(none);
-  }
-  // And black navbar style
-  &.navbar-inverse {  
-    font-size: 90%;
-      
-    // Inner style
-    .navbar-inner {
-      background: none;
-      background-color: @navbarInverseBackground;
-    }
-  }
+// Admin sections and user navbar
+// -------------------------
+.navbar-sections {    
+  // Style brand
+  .brand {
+    margin-right: -10px;
+    
+    color: @grayDark;
+    font-size: 200%;
+    
+    span {
+      color: @grayLight;
+      font-size: 50%;
+      line-height: 50%;
+    }
+  }
+  
+  // Signed-in administrator profile 
+  .user-profile {
+    padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px 3px;
+    margin: 15px 5px;
+    
+    color: @grayDarker;
+    font-weight: bold;
+  }
+      
+  // Make links look better
+  .nav {
+    // Drop side margins so we fit 1024px clients
+    margin-left: 0px;
+    margin-right: 0px;
+    
+    li {
+      a, a:link, a:visited, a:active {
+        .opacity(50);
+        padding: ((@navbarHeight - @baseLineHeight) / 2) 10px ((@navbarHeight - @baseLineHeight) / 2);
+        margin: 0px 5px;
+        
+        font-weight: bold;
+        text-shadow: 0px 1px 0px @white;
+      }
+
+      a:focus, a:hover {
+        background-color: @navbarLinkBackgroundHover;
+        .border-radius(4px);
+        padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px;
+        margin: 15px 5px;
+        .opacity(100);
+      }
+      
+      form {
+        margin: 0px;
+        padding: 0px;
+      }
+    }
+    
+    li.active {
+      a, a:link, a:active, a:visited, a:hover {
+        background-color: @navbarLinkBackgroundActive;
+        .border-radius(4px);
+        .box-shadow(none);
+        padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 10px ((@navbarHeight - @baseLineHeight) / 2)-15px;
+        margin: 15px 0px;
+        .opacity(100);
+          
+        text-shadow: 0px 1px 0px darken(@navbarLinkBackgroundActive, 5%);
+        
+        i {
+          background-image: url("@{iconWhiteSpritePath}");
+          .opacity(100);
+        }
+      }
+    }
+  }  
+    
+  // Sign-out button
+  .btn-link, .btn-link:link, .btn-link:visited {
+    .border-radius(4px);
+    padding: ((@navbarHeight - @baseLineHeight) / 2)-15px 8px ((@navbarHeight - @baseLineHeight) / 2)-15px;
+    margin: 15px 0px;
+    .opacity(60);
+    
+    color: @textColor;
+    font-weight: bold;
+    font-size: 100%;
+    
+    i {
+      .opacity(100);
+    }
+  }
+  
+  .btn-link:active, .btn-link:hover {
+    background: @navbarLinkBackgroundHover;
+    .opacity(100);
+    
+    color: @textColor;
+  }
+}
+
+// Admin section actions navbar
+// -------------------------
+.navbar-actions {  
+  border-bottom: 1px solid @navbarInverseBorder;
+  
+  // Fix inverse navbar height
+  .navbar-inner {
+    min-height: @navbarInverseHeight;
+  }
+  
+  .nav {
+    li {
+      a, a:link, a:active, a:visited {
+        .opacity(50);
+        padding: ((@navbarInverseHeight - @baseLineHeight) / 2) 15px ((@navbarInverseHeight - @baseLineHeight) / 2);
+        
+        color: @black;
+        font-weight: bold;
+        text-shadow: 0px 1px 0px @grayLighter;
+      }
+      
+      a:hover {        
+        .opacity(100);
+        
+      }
+      
+      form {
+        margin: 0px;
+        padding: 0px;
+      }
+    }
+    
+    li.active {      
+      a, a:link, a:active, a:visited, a:hover {
+        border-bottom: 4px solid @linkColor;
+        .box-shadow(none);
+        .opacity(100);
+        padding-bottom: ((@navbarInverseHeight - @baseLineHeight) / 2) - 3px;
+        margin-bottom: -1px;
+        
+        color: @grayDark;
+        text-shadow: 0px 1px 0px @white;
+        
+        i {
+          background-image: url("@{iconSpritePath}");
+          .opacity(100);
+        }
+      }
+    }
+  }
+}
+
+// Both Navbars
+// -------------------------
+.navbar {
+  // Inner style
+  .navbar-inner {
+    border: none;
+    background: none;
+    background-color: @navbarBackground;
+    .box-shadow(none);
+  }
+  // And black navbar style
+  &.navbar-inverse {  
+    font-size: 90%;
+      
+    // Inner style
+    .navbar-inner {
+      background: none;
+      background-color: @navbarInverseBackground;
+    }
+  }
 }
 }

+ 74 - 74
static/admin/css/admin/navs.less

@@ -1,75 +1,75 @@
-// Admin navs
-// -------------------------
-.tabs-header {
-  border-bottom: none;
-  padding-bottom: 0px;
-  
-  .nav-tabs {
-    margin-bottom: 0px;
-  }
-}
-
-.nav-tabs li {
-  a:link, a:active, a:visited {    
-    font-weight: bold;
-  }
-  
-  &.active a:link, &.active a:active,
-  &.active a:visited, &.active a:hover {
-    background-color: @bodyBackground;
-    border-bottom: 4px solid @linkColor;
-    border-width: 0px 0px 4px 0px;
-    padding-top: (@baseLineHeight / 2) - 1px;
-    padding-bottom: (@baseLineHeight / 2) - 5px;
-        
-    color: @textColor;
-  }
-    
-  &.fallback {
-    float:right;
-    
-    a:link, a:active,
-    a:visited, a:hover {
-      border-bottom: none;
-      .border-radius(3px);
-      margin-top: 4px;
-      padding: 4px 12px;
-    }
-  }
-}
-
-// Tables lists actions
-td>ul.list-actions {
-  list-style: none;
-  margin: 0px;
-  overflow: auto;
-  padding: 0px;
-  
-  &>li {
-    float: left;
-    margin: 0px;
-    padding: 0px;
-    
-    &>form {
-      margin: 0px;
-      padding: 0px;
-    }
-    
-    &>a, &>form>button {
-      background: none;
-      border: none;
-      .border-radius(3px);
-      display: block;
-      margin: 0px 3px;
-      padding: 2px 5px;
-      
-      &:hover {
-        background-color: @linkColor;
-        
-        i {
-          background-image: url("@{iconWhiteSpritePath}");          
-        }
-      }
-    }
-  }
+// Admin navs
+// -------------------------
+.tabs-header {
+  border-bottom: none;
+  padding-bottom: 0px;
+  
+  .nav-tabs {
+    margin-bottom: 0px;
+  }
+}
+
+.nav-tabs li {
+  a:link, a:active, a:visited {    
+    font-weight: bold;
+  }
+  
+  &.active a:link, &.active a:active,
+  &.active a:visited, &.active a:hover {
+    background-color: @bodyBackground;
+    border-bottom: 4px solid @linkColor;
+    border-width: 0px 0px 4px 0px;
+    padding-top: (@baseLineHeight / 2) - 1px;
+    padding-bottom: (@baseLineHeight / 2) - 5px;
+        
+    color: @textColor;
+  }
+    
+  &.fallback {
+    float:right;
+    
+    a:link, a:active,
+    a:visited, a:hover {
+      border-bottom: none;
+      .border-radius(3px);
+      margin-top: 4px;
+      padding: 4px 12px;
+    }
+  }
+}
+
+// Tables lists actions
+td>ul.list-actions {
+  list-style: none;
+  margin: 0px;
+  overflow: auto;
+  padding: 0px;
+  
+  &>li {
+    float: left;
+    margin: 0px;
+    padding: 0px;
+    
+    &>form {
+      margin: 0px;
+      padding: 0px;
+    }
+    
+    &>a, &>form>button {
+      background: none;
+      border: none;
+      .border-radius(3px);
+      display: block;
+      margin: 0px 3px;
+      padding: 2px 5px;
+      
+      &:hover {
+        background-color: @linkColor;
+        
+        i {
+          background-image: url("@{iconWhiteSpritePath}");          
+        }
+      }
+    }
+  }
 }
 }

+ 32 - 32
static/admin/css/admin/popovers.less

@@ -1,32 +1,32 @@
-// Popovers
-// --------------------------------------------------
-.popover {
-  border: 1px solid #black;
-  .border-radius(3px);
-  .box-shadow(0px 0px 0px 3px rgba(0,0,0,.1));
-}
-
-.popover-title {
-  .user-card {
-    margin-top: 2px;
-    margin-bottom: -2px;
-    
-    font-weight: bold;
-    font-size: 160%;
-    
-    .avatar-small {
-      position: relative;
-      bottom: 2px;
-    }
-  }
-}
-
-// Popover infocard
-// --------------------------------------------------
-.user-infocard {
-  overflow: auto;
-  
-  .avatar-normal {
-    float: left;
-  }
-}
+// Popovers
+// --------------------------------------------------
+.popover {
+  border: 1px solid #black;
+  .border-radius(3px);
+  .box-shadow(0px 0px 0px 3px rgba(0,0,0,.1));
+}
+
+.popover-title {
+  .user-card {
+    margin-top: 2px;
+    margin-bottom: -2px;
+    
+    font-weight: bold;
+    font-size: 160%;
+    
+    .avatar-small {
+      position: relative;
+      bottom: 2px;
+    }
+  }
+}
+
+// Popover infocard
+// --------------------------------------------------
+.user-infocard {
+  overflow: auto;
+  
+  .avatar-normal {
+    float: left;
+  }
+}

+ 94 - 94
static/admin/css/admin/scaffolding.less

@@ -1,94 +1,94 @@
-// Compact layout, used for sign-in and "processing" pages
-// -------------------------
-body.layout-compact {
-  background-color: darken(@bodyBackground, 1%);
-  margin-top: 100px;
-  
-  h1 {
-    padding: 0px;
-    
-    color: @grayLight;
-    font-size: 180%;
-    
-    strong {
-      color: @grayDark;
-    }
-  }
-  
-  .block-bold {
-    background: @white;
-    border: 1px solid darken(@bodyBackground, 20%);
-    .border-radius(3px);
-    .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 10%));
-    margin: 0px -19px;
-    
-    .alert{
-      border-width: 0px 0px 1px 0px;
-      .border-radius(2px 2px 0px 0px);
-      
-      margin: 0px;
-      padding: 12px;
-      font-weight: bold;
-    }
-    
-    form {
-      margin: 0px;
-      padding: 0px;
-      
-      .form-container {
-        padding: 20px 18px;
-        padding-bottom: 12px;
-      }
-      
-      .form-actions {
-        .border-radius(0px 0px 2px 2px);
-        margin: 0px;
-      }
-    }
-  }
-}
-
-// Footer
-footer {
-  padding-top: 16px;
-  padding-bottom: 32px;
-  
-  color: darken(@bodyBackground, 30%);
-  
-  a, a:link, a:active, a:visited {
-    color: darken(@bodyBackground, 30%);
-    text-decoration: underline;
-  }
-  
-  a:hover {
-    color: darken(@bodyBackground, 50%);
-  }
-  
-  .go-to-top {
-    float: right;
-    
-    &, &:link, &:active, &:visited {
-      text-decoration: none;
-      
-      i {
-        .opacity(40);
-      }
-    }
-    
-    &:hover {
-      i {
-        .opacity(65);
-      }
-    }
-  }
-}
-
-// Side-panel
-.side-panel {
-  padding-right: 30px;
-  border-right: 1px solid darken(@bodyBackground, 15%);
-}
-  
-.sidepanel-header {
-  margin-top: 0px;
-}
+// Compact layout, used for sign-in and "processing" pages
+// -------------------------
+body.layout-compact {
+  background-color: darken(@bodyBackground, 1%);
+  margin-top: 100px;
+  
+  h1 {
+    padding: 0px;
+    
+    color: @grayLight;
+    font-size: 180%;
+    
+    strong {
+      color: @grayDark;
+    }
+  }
+  
+  .block-bold {
+    background: @white;
+    border: 1px solid darken(@bodyBackground, 20%);
+    .border-radius(3px);
+    .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 10%));
+    margin: 0px -19px;
+    
+    .alert{
+      border-width: 0px 0px 1px 0px;
+      .border-radius(2px 2px 0px 0px);
+      
+      margin: 0px;
+      padding: 12px;
+      font-weight: bold;
+    }
+    
+    form {
+      margin: 0px;
+      padding: 0px;
+      
+      .form-container {
+        padding: 20px 18px;
+        padding-bottom: 12px;
+      }
+      
+      .form-actions {
+        .border-radius(0px 0px 2px 2px);
+        margin: 0px;
+      }
+    }
+  }
+}
+
+// Footer
+footer {
+  padding-top: 16px;
+  padding-bottom: 32px;
+  
+  color: darken(@bodyBackground, 30%);
+  
+  a, a:link, a:active, a:visited {
+    color: darken(@bodyBackground, 30%);
+    text-decoration: underline;
+  }
+  
+  a:hover {
+    color: darken(@bodyBackground, 50%);
+  }
+  
+  .go-to-top {
+    float: right;
+    
+    &, &:link, &:active, &:visited {
+      text-decoration: none;
+      
+      i {
+        .opacity(40);
+      }
+    }
+    
+    &:hover {
+      i {
+        .opacity(65);
+      }
+    }
+  }
+}
+
+// Side-panel
+.side-panel {
+  padding-right: 30px;
+  border-right: 1px solid darken(@bodyBackground, 15%);
+}
+  
+.sidepanel-header {
+  margin-top: 0px;
+}

+ 183 - 183
static/admin/css/admin/tables.less

@@ -1,183 +1,183 @@
-// Tables
-// --------------------------------------------------
-.table-footer {
-  background: none;
-  margin-bottom: 0px;
-  padding: 0px 8px;
-  position: relative;
-  bottom: 20px;
-  
-  .pager {
-    margin: 0px 0px;
-    margin-top: 9px;
-    padding: 0px;
-    margin-right: 6px;
-    
-    &>li {
-      margin-right: 6px;
-    
-      &>a {
-        &:link, &:active, &:visited {
-          background: darken(@bodyBackground, 8%);
-          border: none;
-          .border-radius(3px);
-          padding: 2px 5px;
-        }
-      
-        &:hover {
-          background-color: @linkColor;
-          
-          i {
-            background-image: url("@{iconWhiteSpritePath}");          
-          }
-        }
-      }
-    }
-  }
-  
-  .table-count {
-    padding: 11px 0px;
-    
-    color: @gray;
-  }
-  
-  .form-inline {
-    margin: 0px;
-    padding: 6px 0px;
-  }
-  
-  .table-actions-right {
-    margin-right: 16px;
-  }
-}
-
-// Cell colors
-.table {
-  td {
-    &.perm-show,
-    &.perm-read,
-    &.perm-start,
-    &.perm-reply,
-    &.perm-upload,
-    &.perm-download {
-      text-align: center;
-    }
-  }
-  
-  tr:nth-child(even) {
-    .perm-show {background-color: lighten(@blue, 40%);}
-    .perm-read {background-color: lighten(@red, 40%);}
-    .perm-start {background-color: lighten(@green, 40%);}
-    .perm-reply {background-color: lighten(@yellow, 40%);}
-    .perm-upload {background-color: lighten(@pink, 40%);}
-    .perm-download {background-color: lighten(@orange, 40%);}
-  }
-  
-  tr:nth-child(odd) {
-    .perm-show {background-color: lighten(@blue, 30%);}
-    .perm-read {background-color: lighten(@red, 30%);}
-    .perm-start {background-color: lighten(@green, 30%);}
-    .perm-reply {background-color: lighten(@yellow, 30%);}
-    .perm-upload {background-color: lighten(@pink, 30%);}
-    .perm-download {background-color: lighten(@orange, 30%);}
-  }
-}
-
-// Checkbox cell
-td, th {
-  &.check-cell {
-    width: 32px;
-  }
-    
-  .checkbox {
-    margin-bottom: 0px;
-    position: relative;
-    bottom: 1px;
-    
-    input {
-      position: relative;
-      left: 9px;
-    }
-  }
-}
-
-// Lead cell
-td.lead-cell {
-  font-size: 120%;
-}
-
-// Vertically centered table
-.table {
-  td {
-    vertical-align: middle;
-  }
-  
-  select, .yes-no-switch {
-    margin: 0px;
-  }
-  
-  input {
-    width: 90% !important;
-  }
-    
-  .yes-no-switch {
-    position: relative;
-    top: 2px;
-  }
-}
-
-// Table sorting styles
-th.table-sort {
-  padding: 0px;
-  
-  a:link, a:active, a:visited a:hover{
-    display: block;
-    padding: 8px;
-  }
-  
-  &.sort-active-asc {
-    a:link, a:active, a:visited {
-      border-bottom: 3px solid @blue;
-      padding-bottom: 5px;
-    }
-    
-    a:hover {
-      border-bottom: 3px solid lighten(@red, 30%);
-      padding-bottom: 5px;
-      
-      text-decoration: none;
-    }
-  }
-  
-  &.sort-active-desc {
-    a:link, a:active, a:visited {
-      border-bottom: 3px solid lighten(@red, 20%);
-      padding-bottom: 5px;
-    }
-    
-    a:hover {
-      border-bottom: 3px solid lighten(@blue, 10%);
-      padding-bottom: 5px;
-      
-      text-decoration: none;
-    }
-  }
-  
-  &.sort-asc {
-    a:hover {
-      border-bottom: 3px solid lighten(@blue, 40%);
-      padding-bottom: 5px;
-      
-      text-decoration: none;
-    }
-  }
-  
-  &.sort-desc {    
-    a:hover {
-      border-bottom: 3px solid lighten(@red, 40%);
-      padding-bottom: 5px;
-      
-      text-decoration: none;
-    }    
-  }
-}
+// Tables
+// --------------------------------------------------
+.table-footer {
+  background: none;
+  margin-bottom: 0px;
+  padding: 0px 8px;
+  position: relative;
+  bottom: 20px;
+  
+  .pager {
+    margin: 0px 0px;
+    margin-top: 9px;
+    padding: 0px;
+    margin-right: 6px;
+    
+    &>li {
+      margin-right: 6px;
+    
+      &>a {
+        &:link, &:active, &:visited {
+          background: darken(@bodyBackground, 8%);
+          border: none;
+          .border-radius(3px);
+          padding: 2px 5px;
+        }
+      
+        &:hover {
+          background-color: @linkColor;
+          
+          i {
+            background-image: url("@{iconWhiteSpritePath}");          
+          }
+        }
+      }
+    }
+  }
+  
+  .table-count {
+    padding: 11px 0px;
+    
+    color: @gray;
+  }
+  
+  .form-inline {
+    margin: 0px;
+    padding: 6px 0px;
+  }
+  
+  .table-actions-right {
+    margin-right: 16px;
+  }
+}
+
+// Cell colors
+.table {
+  td {
+    &.perm-show,
+    &.perm-read,
+    &.perm-start,
+    &.perm-reply,
+    &.perm-upload,
+    &.perm-download {
+      text-align: center;
+    }
+  }
+  
+  tr:nth-child(even) {
+    .perm-show {background-color: lighten(@blue, 40%);}
+    .perm-read {background-color: lighten(@red, 40%);}
+    .perm-start {background-color: lighten(@green, 40%);}
+    .perm-reply {background-color: lighten(@yellow, 40%);}
+    .perm-upload {background-color: lighten(@pink, 40%);}
+    .perm-download {background-color: lighten(@orange, 40%);}
+  }
+  
+  tr:nth-child(odd) {
+    .perm-show {background-color: lighten(@blue, 30%);}
+    .perm-read {background-color: lighten(@red, 30%);}
+    .perm-start {background-color: lighten(@green, 30%);}
+    .perm-reply {background-color: lighten(@yellow, 30%);}
+    .perm-upload {background-color: lighten(@pink, 30%);}
+    .perm-download {background-color: lighten(@orange, 30%);}
+  }
+}
+
+// Checkbox cell
+td, th {
+  &.check-cell {
+    width: 32px;
+  }
+    
+  .checkbox {
+    margin-bottom: 0px;
+    position: relative;
+    bottom: 1px;
+    
+    input {
+      position: relative;
+      left: 9px;
+    }
+  }
+}
+
+// Lead cell
+td.lead-cell {
+  font-size: 120%;
+}
+
+// Vertically centered table
+.table {
+  td {
+    vertical-align: middle;
+  }
+  
+  select, .yes-no-switch {
+    margin: 0px;
+  }
+  
+  input {
+    width: 90% !important;
+  }
+    
+  .yes-no-switch {
+    position: relative;
+    top: 2px;
+  }
+}
+
+// Table sorting styles
+th.table-sort {
+  padding: 0px;
+  
+  a:link, a:active, a:visited a:hover{
+    display: block;
+    padding: 8px;
+  }
+  
+  &.sort-active-asc {
+    a:link, a:active, a:visited {
+      border-bottom: 3px solid @blue;
+      padding-bottom: 5px;
+    }
+    
+    a:hover {
+      border-bottom: 3px solid lighten(@red, 30%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-active-desc {
+    a:link, a:active, a:visited {
+      border-bottom: 3px solid lighten(@red, 20%);
+      padding-bottom: 5px;
+    }
+    
+    a:hover {
+      border-bottom: 3px solid lighten(@blue, 10%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-asc {
+    a:hover {
+      border-bottom: 3px solid lighten(@blue, 40%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }
+  }
+  
+  &.sort-desc {    
+    a:hover {
+      border-bottom: 3px solid lighten(@red, 40%);
+      padding-bottom: 5px;
+      
+      text-decoration: none;
+    }    
+  }
+}

+ 54 - 54
static/admin/css/admin/users-lists.less

@@ -1,54 +1,54 @@
-// Users list
-// --------------------------------------------------
-.table-users {
-  a:link, a:active,
-  a:visited, a:hover {
-    color: @textColor;
-    font-size: 150%;
-    text-decoration: none
-  }
-  
-  .avatar {
-    .border-radius(3px);
-    width: 42px;
-    height: 42px;
-  }
-  
-  // Info popover
-  .info-popover {
-    background: darken(@bodyBackground, 10%);
-    .border-radius(3px);
-    padding: 2px;
-    padding-top: 0px;
-  
-    i {
-      margin: 0px;
-    }
-    
-    &:hover {
-      background: darken(@bodyBackground, 50%);
-      
-      i {
-        background-image: url("@{iconWhiteSpritePath}");
-      }
-    }
-  }
-  
-  // Tiny size, for lists with tons of members
-  &.list-tiny {
-    a:link, a:active,
-    a:visited, a:hover {
-      font-size: 100%;
-    }
-    
-    .avatar {
-      width: 22px;
-      height: 22px;
-    }
-    
-    i {
-      position: relative;
-      top: 2px;
-    }
-  }
-}
+// Users list
+// --------------------------------------------------
+.table-users {
+  a:link, a:active,
+  a:visited, a:hover {
+    color: @textColor;
+    font-size: 150%;
+    text-decoration: none
+  }
+  
+  .avatar {
+    .border-radius(3px);
+    width: 42px;
+    height: 42px;
+  }
+  
+  // Info popover
+  .info-popover {
+    background: darken(@bodyBackground, 10%);
+    .border-radius(3px);
+    padding: 2px;
+    padding-top: 0px;
+  
+    i {
+      margin: 0px;
+    }
+    
+    &:hover {
+      background: darken(@bodyBackground, 50%);
+      
+      i {
+        background-image: url("@{iconWhiteSpritePath}");
+      }
+    }
+  }
+  
+  // Tiny size, for lists with tons of members
+  &.list-tiny {
+    a:link, a:active,
+    a:visited, a:hover {
+      font-size: 100%;
+    }
+    
+    .avatar {
+      width: 22px;
+      height: 22px;
+    }
+    
+    i {
+      position: relative;
+      top: 2px;
+    }
+  }
+}

+ 15 - 15
static/admin/css/admin/wells.less

@@ -1,15 +1,15 @@
-// Wells
-// --------------------------------------------------
-.well {
-  background: darken(@bodyBackground, 1%);
-  border: 1px solid @white;
-  .border-radius(3px);
-  .box-shadow(0px 0px 3px darken(@bodyBackground, 20%));
-}
-
-.strong-well {
-  background: @white;
-  border: 1px solid darken(@bodyBackground, 20%);
-  .border-radius(3px);
-  .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 10%));
-}
+// Wells
+// --------------------------------------------------
+.well {
+  background: darken(@bodyBackground, 1%);
+  border: 1px solid @white;
+  .border-radius(3px);
+  .box-shadow(0px 0px 3px darken(@bodyBackground, 20%));
+}
+
+.strong-well {
+  background: @white;
+  border: 1px solid darken(@bodyBackground, 20%);
+  .border-radius(3px);
+  .box-shadow(0px 0px 0px 3px darken(@bodyBackground, 10%));
+}

+ 64 - 64
static/admin/js/admin.js

@@ -1,65 +1,65 @@
-$(function () {
-	// Register tooltips
-	$('.tooltip-top').tooltip({placement: 'top'})
-	$('.tooltip-bottom').tooltip({placement: 'bottom'})
-	$('.tooltip-left').tooltip({placement: 'left'})
-	$('.tooltip-right').tooltip({placement: 'right'})
-	
-	// Register popovers
-	$('.popover-top').popover({placement: 'top'})
-	$('.popover-bottom').popover({placement: 'bottom'})
-	$('.popover-left').popover({placement: 'left'})
-	$('.popover-right').popover({placement: 'right'})
-	
-	// Start all dropdowns
-	$('.dropdown-toggle').dropdown()
-	
-	// Dont hide clickable dropdowns
-	$('.dropdown-clickable').on('click', function (e) {
-	  e.stopPropagation()
-	});
-	
-	// Make yes-no switches work
-	$('.yes-no-switch').toggleButtons({
-	  style: {
-	    enabled: "primary",
-	    disabled: "danger"
-	  }
-	});
-	
-	// Checkbox Group Master
-	$('input.checkbox-master').live('click', function(){
-		if($(this).is(':checked')){
-			$('input.checkbox-member').attr("checked" ,"checked");
-		}
-		else
-		{
-			$('input.checkbox-member').removeAttr('checked');
-		}
-	});
-	
-	// Checkbox Group Member
-	$('input.checkbox-member').live('click', function(){
-		if(!$(this).is(':checked')){
-			$('input.checkbox-master').removeAttr('checked');
-		}
-	});
-	
-	// Check Confirmation on links
-	$('a.confirm').live('click', function(){
-		var decision = confirm(jQuery.data(this, 'jsconfirm'));
-		return decision
-	});
-	
-	// Check Confirmation on forms
-	$('form.confirm').live('submit', function(){
-		data = $(this).data();
-		var decision = confirm(data.jsconfirm);
-		return decision
-	});
-	
-	// Go back one page
-	$('.go-back').on('click', function (e) {
-	  history.go(-1)
-	})
+$(function () {
+	// Register tooltips
+	$('.tooltip-top').tooltip({placement: 'top'})
+	$('.tooltip-bottom').tooltip({placement: 'bottom'})
+	$('.tooltip-left').tooltip({placement: 'left'})
+	$('.tooltip-right').tooltip({placement: 'right'})
+	
+	// Register popovers
+	$('.popover-top').popover({placement: 'top'})
+	$('.popover-bottom').popover({placement: 'bottom'})
+	$('.popover-left').popover({placement: 'left'})
+	$('.popover-right').popover({placement: 'right'})
+	
+	// Start all dropdowns
+	$('.dropdown-toggle').dropdown()
+	
+	// Dont hide clickable dropdowns
+	$('.dropdown-clickable').on('click', function (e) {
+	  e.stopPropagation()
+	});
+	
+	// Make yes-no switches work
+	$('.yes-no-switch').toggleButtons({
+	  style: {
+	    enabled: "primary",
+	    disabled: "danger"
+	  }
+	});
+	
+	// Checkbox Group Master
+	$('input.checkbox-master').live('click', function(){
+		if($(this).is(':checked')){
+			$('input.checkbox-member').attr("checked" ,"checked");
+		}
+		else
+		{
+			$('input.checkbox-member').removeAttr('checked');
+		}
+	});
+	
+	// Checkbox Group Member
+	$('input.checkbox-member').live('click', function(){
+		if(!$(this).is(':checked')){
+			$('input.checkbox-master').removeAttr('checked');
+		}
+	});
+	
+	// Check Confirmation on links
+	$('a.confirm').live('click', function(){
+		var decision = confirm(jQuery.data(this, 'jsconfirm'));
+		return decision
+	});
+	
+	// Check Confirmation on forms
+	$('form.confirm').live('submit', function(){
+		data = $(this).data();
+		var decision = confirm(data.jsconfirm);
+		return decision
+	});
+	
+	// Go back one page
+	$('.go-back').on('click', function (e) {
+	  history.go(-1)
+	})
 })
 })

+ 98 - 98
static/cranefly/css/cranefly.less

@@ -1,99 +1,99 @@
-/*!
- * Bootstrap v2.1.0
- *
- * Copyright 2012 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
- */
-
-// CSS Reset
-@import "bootstrap/reset.less";
-
-// Core variables and mixins
-@import "variables.less";
-@import "bootstrap/mixins.less";
-
-// Grid system and page structure
-@import "bootstrap/scaffolding.less";
-@import "bootstrap/grid.less";
-@import "bootstrap/layouts.less";
-
-// Base CSS
-@import "bootstrap/type.less";
-@import "bootstrap/code.less";
-@import "bootstrap/forms.less";
-@import "bootstrap/tables.less";
-
-// Components: common
-@import "bootstrap/sprites.less";
-@import "bootstrap/dropdowns.less";
-@import "bootstrap/wells.less";
-@import "bootstrap/component-animations.less";
-@import "bootstrap/close.less";
-
-// Components: Buttons & Alerts
-@import "bootstrap/buttons.less";
-@import "bootstrap/button-groups.less";
-@import "bootstrap/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
-
-// Components: Nav
-@import "bootstrap/navs.less";
-@import "bootstrap/navbar.less";
-@import "bootstrap/breadcrumbs.less";
-@import "bootstrap/pagination.less";
-@import "bootstrap/pager.less";
-
-// Components: Popovers
-@import "bootstrap/modals.less";
-@import "bootstrap/tooltip.less";
-@import "bootstrap/popovers.less";
-
-// Components: Misc
-@import "bootstrap/thumbnails.less";
-@import "bootstrap/labels-badges.less";
-@import "bootstrap/progress-bars.less";
-@import "bootstrap/accordion.less";
-@import "bootstrap/carousel.less";
-@import "bootstrap/hero-unit.less";
-
-// Utility classes
-@import "bootstrap/utilities.less"; // Has to be last to override when necessary
-
-// Responsiveness
-@import "bootstrap/responsive-1200px-min.less";
-
-// Sora theme
-@import "cranefly/header.less";
-@import "cranefly/scaffolding.less";
-@import "cranefly/navbar.less";
-@import "cranefly/breadcrumbs.less";
-@import "cranefly/messages.less";
-@import "cranefly/forms.less";
-@import "cranefly/buttons.less";
-@import "cranefly/pagination.less";
-@import "cranefly/editor.less";
-@import "cranefly/error.less";
-@import "cranefly/markdown.less";
-@import "cranefly/index.less";
-@import "cranefly/signin.less";
-@import "cranefly/usercp.less";
-@import "cranefly/forummap.less";
-@import "cranefly/watchedthreads.less";
-@import "cranefly/alerts.less";
-@import "cranefly/newsfeed.less";
-@import "cranefly/category.less";
-@import "cranefly/profiles.less";
-@import "cranefly/forum.less";
-@import "cranefly/thread.less";
-@import "cranefly/karmas.less";
-@import "cranefly/changelog.less";
-@import "cranefly/report.less";
-@import "cranefly/reports.less";
-@import "cranefly/search.less";
-
-// Keep ranks last for easy overrides!
-@import "ranks.less";
-@import "jquery.atwho.css";
+/*!
+ * Bootstrap v2.1.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+// CSS Reset
+@import "bootstrap/reset.less";
+
+// Core variables and mixins
+@import "variables.less";
+@import "bootstrap/mixins.less";
+
+// Grid system and page structure
+@import "bootstrap/scaffolding.less";
+@import "bootstrap/grid.less";
+@import "bootstrap/layouts.less";
+
+// Base CSS
+@import "bootstrap/type.less";
+@import "bootstrap/code.less";
+@import "bootstrap/forms.less";
+@import "bootstrap/tables.less";
+
+// Components: common
+@import "bootstrap/sprites.less";
+@import "bootstrap/dropdowns.less";
+@import "bootstrap/wells.less";
+@import "bootstrap/component-animations.less";
+@import "bootstrap/close.less";
+
+// Components: Buttons & Alerts
+@import "bootstrap/buttons.less";
+@import "bootstrap/button-groups.less";
+@import "bootstrap/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
+
+// Components: Nav
+@import "bootstrap/navs.less";
+@import "bootstrap/navbar.less";
+@import "bootstrap/breadcrumbs.less";
+@import "bootstrap/pagination.less";
+@import "bootstrap/pager.less";
+
+// Components: Popovers
+@import "bootstrap/modals.less";
+@import "bootstrap/tooltip.less";
+@import "bootstrap/popovers.less";
+
+// Components: Misc
+@import "bootstrap/thumbnails.less";
+@import "bootstrap/labels-badges.less";
+@import "bootstrap/progress-bars.less";
+@import "bootstrap/accordion.less";
+@import "bootstrap/carousel.less";
+@import "bootstrap/hero-unit.less";
+
+// Utility classes
+@import "bootstrap/utilities.less"; // Has to be last to override when necessary
+
+// Responsiveness
+@import "bootstrap/responsive-1200px-min.less";
+
+// Sora theme
+@import "cranefly/header.less";
+@import "cranefly/scaffolding.less";
+@import "cranefly/navbar.less";
+@import "cranefly/breadcrumbs.less";
+@import "cranefly/messages.less";
+@import "cranefly/forms.less";
+@import "cranefly/buttons.less";
+@import "cranefly/pagination.less";
+@import "cranefly/editor.less";
+@import "cranefly/error.less";
+@import "cranefly/markdown.less";
+@import "cranefly/index.less";
+@import "cranefly/signin.less";
+@import "cranefly/usercp.less";
+@import "cranefly/forummap.less";
+@import "cranefly/watchedthreads.less";
+@import "cranefly/alerts.less";
+@import "cranefly/newsfeed.less";
+@import "cranefly/category.less";
+@import "cranefly/profiles.less";
+@import "cranefly/forum.less";
+@import "cranefly/thread.less";
+@import "cranefly/karmas.less";
+@import "cranefly/changelog.less";
+@import "cranefly/report.less";
+@import "cranefly/reports.less";
+@import "cranefly/search.less";
+
+// Keep ranks last for easy overrides!
+@import "ranks.less";
+@import "jquery.atwho.css";
 @import "jquery.Jcrop.min.css";
 @import "jquery.Jcrop.min.css";

+ 41 - 41
static/cranefly/css/cranefly/alerts.less

@@ -1,42 +1,42 @@
-// User Alerts
-// -------------------------
-
-.user-alerts {
-  td {
-    vertical-align: middle;
-
-    &.alert-icon {
-      .label {
-        background-color: @gray;
-        border: 1px solid darken(@gray, 15%);
-        border-radius: @baseBorderRadius;
-        padding: 4px;
-        padding-top: 3px;
-
-        i {
-          background-image: url("@{iconWhiteSpritePath}");
-        }
-
-        &.label-warning {
-          background-color: @red;
-          border: 1px solid darken(@red, 15%);
-        }
-      }
-    }
-
-    &.alert-message {
-      color: @gray;
-      font-size: @baseFontSize * 1.2;
-
-      a:link, a:visited {
-        color: @textColor;
-        font-weight: bold;
-      }
-    }
-
-    &.alert-date {
-      color: @grayLight;
-      text-align: right;
-    }
-  }
+// User Alerts
+// -------------------------
+
+.user-alerts {
+  td {
+    vertical-align: middle;
+
+    &.alert-icon {
+      .label {
+        background-color: @gray;
+        border: 1px solid darken(@gray, 15%);
+        border-radius: @baseBorderRadius;
+        padding: 4px;
+        padding-top: 3px;
+
+        i {
+          background-image: url("@{iconWhiteSpritePath}");
+        }
+
+        &.label-warning {
+          background-color: @red;
+          border: 1px solid darken(@red, 15%);
+        }
+      }
+    }
+
+    &.alert-message {
+      color: @gray;
+      font-size: @baseFontSize * 1.2;
+
+      a:link, a:visited {
+        color: @textColor;
+        font-weight: bold;
+      }
+    }
+
+    &.alert-date {
+      color: @grayLight;
+      text-align: right;
+    }
+  }
 }
 }

+ 32 - 32
static/cranefly/css/cranefly/breadcrumbs.less

@@ -1,33 +1,33 @@
-// Breadcrumbs
-// -------------------------
-
-// Footer
-// -------------------------
-footer {
-  .breadcrumb {
-  	background: none;
-  	border: none;
-  	margin: 0px;
-  	padding: 0px;
-
-  	font-weight: bold;
-	text-shadow: none;
-
-    li {
-	  text-shadow: none;
-
-	  a:link, a:active, a:visited, a:hover {
-	    color: @textColor;
-	  }	
-
-	  .divider {
-	  	.opacity(30);
-	  	margin-left: -6px;
-	  }
-
-	  &.active {
-	  	color: @gray;
-	  }
-    }
-  }
+// Breadcrumbs
+// -------------------------
+
+// Footer
+// -------------------------
+footer {
+  .breadcrumb {
+  	background: none;
+  	border: none;
+  	margin: 0px;
+  	padding: 0px;
+
+  	font-weight: bold;
+	text-shadow: none;
+
+    li {
+	  text-shadow: none;
+
+	  a:link, a:active, a:visited, a:hover {
+	    color: @textColor;
+	  }	
+
+	  .divider {
+	  	.opacity(30);
+	  	margin-left: -6px;
+	  }
+
+	  &.active {
+	  	color: @gray;
+	  }
+    }
+  }
 }
 }

+ 164 - 164
static/cranefly/css/cranefly/buttons.less

@@ -1,165 +1,165 @@
-// Buttons styles
-// -------------------------
-
-.btn {
-  background: none;
-  background-color: @bodyBackground;
-  .box-shadow(none);
-
-  font-weight: bold;
-  
-  i {
-    .opacity(60);
-    position: relative;
-    bottom: 1px;
-  }
-
-  &:hover, &:active {
-    background-color: @white;
-    border-color: lighten(@grayLight, 5%);
-  
-    i {
-      .opacity(90);
-    }
-  }
-
-  &.btn-primary {
-    background-color: @bluePale;
-    border-color: darken(@bluePale, 5%);
-
-    text-shadow: 0px 1px 0px darken(@bluePale, 15%);
-
-    &:hover, &:active {
-      &:enabled {
-        background-color: saturate(@bluePale, 20%);
-        border-color: darken(saturate(@bluePale, 20%), 5%);
-      }
-    }
-
-    i {
-      background-image: url("@{iconWhiteSpritePath}");
-    }
-  }
-
-  &.btn-info {
-    background-color: desaturate(@bluePale, 25%);
-    border-color: darken(desaturate(@bluePale, 25%), 5%);
-
-    text-shadow: 0px 1px 0px darken(desaturate(@bluePale, 25%), 15%);
-
-    &:hover, &:active {
-      &:enabled {
-        background-color: desaturate(@bluePale, 10%);
-        border-color: darken(desaturate(@bluePale, 10%), 5%);
-      }
-    }
-
-    i {
-      background-image: url("@{iconWhiteSpritePath}");
-    }
-  }
-
-  &.btn-success {
-    background-color: desaturate(@green, 15%);
-    border-color: darken(desaturate(@green, 15%), 5%);
-
-    text-shadow: 0px 1px 0px darken(@green, 15%);
-
-    &:hover, &:active {
-      &:enabled {
-        background-color: @green;
-        border-color: darken(@green, 5%);
-      }
-    }
-
-    i {
-      background-image: url("@{iconWhiteSpritePath}");
-    }
-  }
-
-  &.btn-warning {
-    background-color: desaturate(@orange, 15%);
-    border-color: darken(desaturate(@orange, 15%), 5%);
-
-    text-shadow: 0px 1px 0px darken(@orange, 15%);
-
-    &:hover, &:active {
-      &:enabled {
-        background-color: @orange;
-        border-color: darken(@orange, 5%);
-      }
-    }
-
-    i {
-      background-image: url("@{iconWhiteSpritePath}");
-    }
-  }
-
-  &.btn-danger {
-    background-color: @red;
-    border-color: darken(@red, 5%);
-
-    text-shadow: 0px 1px 0px darken(@red, 15%);
-
-    &:hover, &:active {
-      &:enabled {
-        background-color: saturate(@red, 20%);
-        border-color: darken(saturate(@red, 20%), 5%);
-      }
-    }
-
-    i {
-      background-image: url("@{iconWhiteSpritePath}");
-    }
-  }
-
-  &.btn-inverse {
-    background-color: @textColor;
-    border-color: darken(@textColor, 5%);
-
-    text-shadow: 0px 1px 0px darken(@textColor, 15%);
-
-    &:hover, &:active {
-      &:enabled {
-        background-color: darken(@textColor, 5%);
-        border-color: darken(@textColor, 10%);
-      }
-    }
-
-    i {
-      background-image: url("@{iconWhiteSpritePath}");
-    }
-  }
-
-  &.btn-link {
-    background: none;
-    border: none;
-    .opacity(70);
-
-    color: @textColor;
-
-    &:hover, &:active, &:focus {
-      &:enabled {
-        .opacity(90);
-        
-        text-decoration: none;
-      }
-    }
-  }
-
-
-  &.btn-primary, &.btn-info, &.btn-success, &.btn-warning, &.btn-danger, &.btn-inverse, &.btn-link {
-    i {
-      .opacity(100);
-    }
-  }
-}
-
-a.btn-link {
-  &:hover, &:active, &:focus {
-    .opacity(90);
-
-    color: @textColor;
-    text-decoration: none;
-  }
+// Buttons styles
+// -------------------------
+
+.btn {
+  background: none;
+  background-color: @bodyBackground;
+  .box-shadow(none);
+
+  font-weight: bold;
+  
+  i {
+    .opacity(60);
+    position: relative;
+    bottom: 1px;
+  }
+
+  &:hover, &:active {
+    background-color: @white;
+    border-color: lighten(@grayLight, 5%);
+  
+    i {
+      .opacity(90);
+    }
+  }
+
+  &.btn-primary {
+    background-color: @bluePale;
+    border-color: darken(@bluePale, 5%);
+
+    text-shadow: 0px 1px 0px darken(@bluePale, 15%);
+
+    &:hover, &:active {
+      &:enabled {
+        background-color: saturate(@bluePale, 20%);
+        border-color: darken(saturate(@bluePale, 20%), 5%);
+      }
+    }
+
+    i {
+      background-image: url("@{iconWhiteSpritePath}");
+    }
+  }
+
+  &.btn-info {
+    background-color: desaturate(@bluePale, 25%);
+    border-color: darken(desaturate(@bluePale, 25%), 5%);
+
+    text-shadow: 0px 1px 0px darken(desaturate(@bluePale, 25%), 15%);
+
+    &:hover, &:active {
+      &:enabled {
+        background-color: desaturate(@bluePale, 10%);
+        border-color: darken(desaturate(@bluePale, 10%), 5%);
+      }
+    }
+
+    i {
+      background-image: url("@{iconWhiteSpritePath}");
+    }
+  }
+
+  &.btn-success {
+    background-color: desaturate(@green, 15%);
+    border-color: darken(desaturate(@green, 15%), 5%);
+
+    text-shadow: 0px 1px 0px darken(@green, 15%);
+
+    &:hover, &:active {
+      &:enabled {
+        background-color: @green;
+        border-color: darken(@green, 5%);
+      }
+    }
+
+    i {
+      background-image: url("@{iconWhiteSpritePath}");
+    }
+  }
+
+  &.btn-warning {
+    background-color: desaturate(@orange, 15%);
+    border-color: darken(desaturate(@orange, 15%), 5%);
+
+    text-shadow: 0px 1px 0px darken(@orange, 15%);
+
+    &:hover, &:active {
+      &:enabled {
+        background-color: @orange;
+        border-color: darken(@orange, 5%);
+      }
+    }
+
+    i {
+      background-image: url("@{iconWhiteSpritePath}");
+    }
+  }
+
+  &.btn-danger {
+    background-color: @red;
+    border-color: darken(@red, 5%);
+
+    text-shadow: 0px 1px 0px darken(@red, 15%);
+
+    &:hover, &:active {
+      &:enabled {
+        background-color: saturate(@red, 20%);
+        border-color: darken(saturate(@red, 20%), 5%);
+      }
+    }
+
+    i {
+      background-image: url("@{iconWhiteSpritePath}");
+    }
+  }
+
+  &.btn-inverse {
+    background-color: @textColor;
+    border-color: darken(@textColor, 5%);
+
+    text-shadow: 0px 1px 0px darken(@textColor, 15%);
+
+    &:hover, &:active {
+      &:enabled {
+        background-color: darken(@textColor, 5%);
+        border-color: darken(@textColor, 10%);
+      }
+    }
+
+    i {
+      background-image: url("@{iconWhiteSpritePath}");
+    }
+  }
+
+  &.btn-link {
+    background: none;
+    border: none;
+    .opacity(70);
+
+    color: @textColor;
+
+    &:hover, &:active, &:focus {
+      &:enabled {
+        .opacity(90);
+        
+        text-decoration: none;
+      }
+    }
+  }
+
+
+  &.btn-primary, &.btn-info, &.btn-success, &.btn-warning, &.btn-danger, &.btn-inverse, &.btn-link {
+    i {
+      .opacity(100);
+    }
+  }
+}
+
+a.btn-link {
+  &:hover, &:active, &:focus {
+    .opacity(90);
+
+    color: @textColor;
+    text-decoration: none;
+  }
 }
 }

+ 260 - 260
static/cranefly/css/cranefly/category.less

@@ -1,261 +1,261 @@
-// Category view
-// -------------------------
-
-.category-forums-list {
-  background-color: @categoryBackground;
-  border: 1px solid @categoryBorder;
-  border-radius: @borderRadiusSmall;
-  .box-shadow(0px 0px 0px 3px @categoryShadow);
-  margin-bottom: @baseLineHeight;
-
-  .header {
-    background-color: @categoryHeader;
-    border: 1px solid @categoryBorder;
-    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
-    margin: -1px;
-    margin-bottom: 0px;
-    padding: (@fontSizeSmall / 3) (@fontSizeSmall - 2px);
-
-    h2 {
-      margin: 0px;
-      padding: 0px;
-
-      color: @grayDark;
-      font-size: @fontSizeSmall;
-      font-weight: bold;
-      line-height: @baseLineHeight;
-      text-align: left;
-
-      small {
-        margin-left: @baseFontSize / 2;
-
-        color: @grayLight;
-        font-size: @fontSizeSmall;
-      }
-    }
-  }
-
-  .forum {
-    border-bottom: 1px solid @categoryBorder;
-    height: 21px;
-    overflow: visible;
-    padding: ((@fontSizeLarge / 2) + 6px) (@fontSizeSmall - 2px);
-
-    &.last {
-      border-bottom: none;
-    }
-
-    .forum-icon {
-      float: left;
-
-      .forum-icon-wrap {
-        background-color: @itemOldColor;
-        border: 1px solid darken(@itemOldColor, 10%);
-        border-radius: @baseBorderRadius;
-        padding: ((@forumIconSize - 22px) / 2) ((@forumIconSize - 16px) / 2);
-        position: relative;
-        bottom: (@forumIconSize - @baseLineHeight) / 2;
-
-        &.forum-icon-new {
-          background-color: @itemNewColor;
-          border: 1px solid darken(@itemNewColor, 10%);
-        }
-
-        &.forum-icon-redirect {
-          background-color: @itemMovedColor;
-          border: 1px solid darken(@itemMovedColor, 10%);
-        }
-      }
-    }
-
-    .forum-main {
-      margin-left: @forumIconSize + 10px;
-
-      h3 {
-        float: left;
-        margin: 0px;
-        padding: 0px;
-
-        font-size: @fontSizeLarge;
-        font-weight: normal;
-        line-height: @baseLineHeight;
-
-        a:link, a:visited {
-          color: @textColor;
-        }
-      }
-
-      .dropdown {
-        float: right;
-        right: @baseFontSize;
-
-        .subforum {
-          &:link, &:visited {
-            color: @grayLight;
-            font-weight: bold;
-          }
-
-          &:hover, &:active {
-            color: @textColor;
-          }
-        }
-
-        .dropdown-toggle {
-          padding: 4px 8px;
-          .opacity(60);
-
-          color: @textColor;
-          font-weight: bold;
-
-          &:hover, &:active, &:focus {
-            .opacity(100);
-
-            text-decoration: none;
-          }
-        }
-
-        &.open .dropdown-toggle {
-          background-color: @categoryShadow;
-          border-radius: @baseBorderRadius @baseBorderRadius 0px 0px;
-          .opacity(100);
-          padding-bottom: 6px;
-
-          text-decoration: none;
-        }
-
-        .dropdown-menu {
-          background: none;
-          border: none;
-          box-shadow: none;
-          z-index: 2;
-
-          .dropdown-shadow{
-            border-radius: @baseBorderRadius;
-            .box-shadow(0px 0px 3px @grayLight);
-            width: 256px;
-            position: relative;
-            right: 0px;
-            top: -4px;
-
-            ul {
-              background-color: @bodyBackground;
-              border-radius: @baseBorderRadius;
-              margin: 0px;
-              padding: 0px;
-
-              li {
-                margin: 0px;
-                padding: 0px;
-                list-style: none;
-
-                a {
-                  border-bottom: 1px dotted @categoryBorder;
-                  display: block;
-                  .opacity(70);
-                  padding: 6px 8px;
-
-                  color: @textColor;
-                  text-decoration: none;
-
-                  &:hover, &:active {
-                    .opacity(100);
-                  }
-                }
-
-                &:last-child a {
-                  border-bottom: none;
-                }
-              }
-            }
-          }
-        }
-      }
-
-      .forum-details {
-        border-left: 1px dotted darken(@categoryBackground, 12%);
-        float: right;
-        margin: -10px 0px;
-        margin-top: ((@baseFontSize - @fontSizeSmall) * -1) - 6px;
-        padding-left: @baseFontSize;
-        height: 35px;
-        width: 220px;
-
-        .thread-name {
-          a:link, a:active, a:visited, a:hover {
-            margin-bottom: 1px;
-
-            color: @textColor;
-            font-size: @fontSizeSmall;
-            font-weight: bold;
-          }
-        }
-
-        .muted {
-          font-size: @fontSizeMini;
-          line-height: @fontSizeMini;
-
-          .last-poster, a:link, a:active, a:visited, a:hover {
-            color: @gray;
-          }
-        }
-
-        em {
-          position: relative;
-          top: (35px - @baseLineHeight) / 2;
-
-          color: @grayLight;
-
-          strong {
-            color: @textColor;
-            font-weight: normal;
-          }
-        }
-      }
-
-      .forum-meta-tooltip {
-        .tooltip-inner {
-          max-width: 400px;
-          text-align: left;
-
-          .forum-stats {
-            color: @grayLight;
-            font-size: @fontSizeMini;
-
-            strong {
-              color: @white;
-            }
-
-            span {
-              margin-right: @baseFontSize;
-            }
-          }
-
-          .forum-description {
-            clear: both;
-            margin: 0px;
-            margin-bottom: @baseFontSize / 2;
-            padding: 0px;
-
-            color: @grayLighter;
-            font-size: @baseFontSize;
-          }
-        }
-      }
-    }
-  }
-
-  &.category-forums-important {
-    border-color: @red;
-    .box-shadow(0px 0px 0px 3px darken(@red, 10%));
-  }
-
-  &.category-forums-inverse {
-    border-color: @grayDark;
-    .box-shadow(0px 0px 0px 3px darken(@grayDark, 10%));
-  }
-
-  &.category-forums-info {
-    border-color: @bluePale;
-    .box-shadow(0px 0px 0px 3px darken(@bluePale, 10%));
-  }
+// Category view
+// -------------------------
+
+.category-forums-list {
+  background-color: @categoryBackground;
+  border: 1px solid @categoryBorder;
+  border-radius: @borderRadiusSmall;
+  .box-shadow(0px 0px 0px 3px @categoryShadow);
+  margin-bottom: @baseLineHeight;
+
+  .header {
+    background-color: @categoryHeader;
+    border: 1px solid @categoryBorder;
+    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
+    margin: -1px;
+    margin-bottom: 0px;
+    padding: (@fontSizeSmall / 3) (@fontSizeSmall - 2px);
+
+    h2 {
+      margin: 0px;
+      padding: 0px;
+
+      color: @grayDark;
+      font-size: @fontSizeSmall;
+      font-weight: bold;
+      line-height: @baseLineHeight;
+      text-align: left;
+
+      small {
+        margin-left: @baseFontSize / 2;
+
+        color: @grayLight;
+        font-size: @fontSizeSmall;
+      }
+    }
+  }
+
+  .forum {
+    border-bottom: 1px solid @categoryBorder;
+    height: 21px;
+    overflow: visible;
+    padding: ((@fontSizeLarge / 2) + 6px) (@fontSizeSmall - 2px);
+
+    &.last {
+      border-bottom: none;
+    }
+
+    .forum-icon {
+      float: left;
+
+      .forum-icon-wrap {
+        background-color: @itemOldColor;
+        border: 1px solid darken(@itemOldColor, 10%);
+        border-radius: @baseBorderRadius;
+        padding: ((@forumIconSize - 22px) / 2) ((@forumIconSize - 16px) / 2);
+        position: relative;
+        bottom: (@forumIconSize - @baseLineHeight) / 2;
+
+        &.forum-icon-new {
+          background-color: @itemNewColor;
+          border: 1px solid darken(@itemNewColor, 10%);
+        }
+
+        &.forum-icon-redirect {
+          background-color: @itemMovedColor;
+          border: 1px solid darken(@itemMovedColor, 10%);
+        }
+      }
+    }
+
+    .forum-main {
+      margin-left: @forumIconSize + 10px;
+
+      h3 {
+        float: left;
+        margin: 0px;
+        padding: 0px;
+
+        font-size: @fontSizeLarge;
+        font-weight: normal;
+        line-height: @baseLineHeight;
+
+        a:link, a:visited {
+          color: @textColor;
+        }
+      }
+
+      .dropdown {
+        float: right;
+        right: @baseFontSize;
+
+        .subforum {
+          &:link, &:visited {
+            color: @grayLight;
+            font-weight: bold;
+          }
+
+          &:hover, &:active {
+            color: @textColor;
+          }
+        }
+
+        .dropdown-toggle {
+          padding: 4px 8px;
+          .opacity(60);
+
+          color: @textColor;
+          font-weight: bold;
+
+          &:hover, &:active, &:focus {
+            .opacity(100);
+
+            text-decoration: none;
+          }
+        }
+
+        &.open .dropdown-toggle {
+          background-color: @categoryShadow;
+          border-radius: @baseBorderRadius @baseBorderRadius 0px 0px;
+          .opacity(100);
+          padding-bottom: 6px;
+
+          text-decoration: none;
+        }
+
+        .dropdown-menu {
+          background: none;
+          border: none;
+          box-shadow: none;
+          z-index: 2;
+
+          .dropdown-shadow{
+            border-radius: @baseBorderRadius;
+            .box-shadow(0px 0px 3px @grayLight);
+            width: 256px;
+            position: relative;
+            right: 0px;
+            top: -4px;
+
+            ul {
+              background-color: @bodyBackground;
+              border-radius: @baseBorderRadius;
+              margin: 0px;
+              padding: 0px;
+
+              li {
+                margin: 0px;
+                padding: 0px;
+                list-style: none;
+
+                a {
+                  border-bottom: 1px dotted @categoryBorder;
+                  display: block;
+                  .opacity(70);
+                  padding: 6px 8px;
+
+                  color: @textColor;
+                  text-decoration: none;
+
+                  &:hover, &:active {
+                    .opacity(100);
+                  }
+                }
+
+                &:last-child a {
+                  border-bottom: none;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      .forum-details {
+        border-left: 1px dotted darken(@categoryBackground, 12%);
+        float: right;
+        margin: -10px 0px;
+        margin-top: ((@baseFontSize - @fontSizeSmall) * -1) - 6px;
+        padding-left: @baseFontSize;
+        height: 35px;
+        width: 220px;
+
+        .thread-name {
+          a:link, a:active, a:visited, a:hover {
+            margin-bottom: 1px;
+
+            color: @textColor;
+            font-size: @fontSizeSmall;
+            font-weight: bold;
+          }
+        }
+
+        .muted {
+          font-size: @fontSizeMini;
+          line-height: @fontSizeMini;
+
+          .last-poster, a:link, a:active, a:visited, a:hover {
+            color: @gray;
+          }
+        }
+
+        em {
+          position: relative;
+          top: (35px - @baseLineHeight) / 2;
+
+          color: @grayLight;
+
+          strong {
+            color: @textColor;
+            font-weight: normal;
+          }
+        }
+      }
+
+      .forum-meta-tooltip {
+        .tooltip-inner {
+          max-width: 400px;
+          text-align: left;
+
+          .forum-stats {
+            color: @grayLight;
+            font-size: @fontSizeMini;
+
+            strong {
+              color: @white;
+            }
+
+            span {
+              margin-right: @baseFontSize;
+            }
+          }
+
+          .forum-description {
+            clear: both;
+            margin: 0px;
+            margin-bottom: @baseFontSize / 2;
+            padding: 0px;
+
+            color: @grayLighter;
+            font-size: @baseFontSize;
+          }
+        }
+      }
+    }
+  }
+
+  &.category-forums-important {
+    border-color: @red;
+    .box-shadow(0px 0px 0px 3px darken(@red, 10%));
+  }
+
+  &.category-forums-inverse {
+    border-color: @grayDark;
+    .box-shadow(0px 0px 0px 3px darken(@grayDark, 10%));
+  }
+
+  &.category-forums-info {
+    border-color: @bluePale;
+    .box-shadow(0px 0px 0px 3px darken(@bluePale, 10%));
+  }
 }
 }

+ 132 - 132
static/cranefly/css/cranefly/changelog.less

@@ -1,133 +1,133 @@
-// Post Changelog
-// -------------------------
-
-.post-changelog {
-  table {
-    td {
-      vertical-align: middle;
-
-      .change-added, .change-removed, .change-none {
-        display: block;
-        font-size: @baseFontSize * 2;
-        font-weight: bold;
-        text-align: right;
-
-        &.change-small {
-          font-size: @baseFontSize;
-        }
-      }
-
-      .change-neutral {
-        color: @gray;
-      }
-
-      .change-added {
-        color: @green;
-      }
-
-      .change-removed {
-        color: @red;
-      }
-
-      .change-reason {
-        a:link, a:active, a:visited, a:hover {
-          color: @textColor;
-          font-weight: bold;
-        }
-      }
-
-      .change-no {
-        &:link, &:active, &:visited, &:hover {
-          float: right;
-
-          color: @gray;
-          font-weight: bold;
-        }
-      }
-
-      .change-description {
-        float: left;
-
-        font-size: @fontSizeSmall;
-
-        a:link, a:active, a:visited, a:hover {
-          color: @textColor;
-        }
-
-        .change-details {
-          color: @grayLight;
-
-          a:link, a:visited {
-            color: @gray;
-          }
-        }
-      }
-    }
-  }
-}
-
-.post-diff {
-  .diff-extra {
-    overflow: auto;
-  }
-
-  .post-diff-details {
-    background-color: @white;
-    border: 1px solid @categoryBorder;
-    border-radius: @borderRadiusSmall;
-    .box-shadow(0px 0px 0px 3px @categoryShadow);
-    margin-bottom: @baseLineHeight;
-
-    table {
-      margin: 0px;
-      width: 100%;
-
-      td {
-        padding: 1px 4px;
-
-        &.line {
-          background-color: @categoryShadow;
-          border-right: 1px solid @categoryBorder;
-          padding-left: @baseFontSize;
-          width: 1%;
-
-          text-align: right;
-
-          a:link, a:active, a:visited, a:hover {
-            color: @grayLight;
-          }
-        }
-
-        &.even {
-          background-color: darken(@white, 3%);
-        }
-
-        &.added {
-          background-color: lighten(@green, 45%);
-
-          color: darken(@green, 20%);
-          font-weight: bold;
-
-          &.even {
-            background-color: lighten(@green, 40%);
-          }
-        }
-
-        &.removed {
-          background-color: lighten(@red, 45%);
-
-          color: darken(@red, 20%);
-          font-weight: bold;
-
-          &.even {
-            background-color: lighten(@red, 40%);
-          }
-        }
-
-        &.stag {
-          color: @gray;
-        }
-      }
-    }
-  }
+// Post Changelog
+// -------------------------
+
+.post-changelog {
+  table {
+    td {
+      vertical-align: middle;
+
+      .change-added, .change-removed, .change-none {
+        display: block;
+        font-size: @baseFontSize * 2;
+        font-weight: bold;
+        text-align: right;
+
+        &.change-small {
+          font-size: @baseFontSize;
+        }
+      }
+
+      .change-neutral {
+        color: @gray;
+      }
+
+      .change-added {
+        color: @green;
+      }
+
+      .change-removed {
+        color: @red;
+      }
+
+      .change-reason {
+        a:link, a:active, a:visited, a:hover {
+          color: @textColor;
+          font-weight: bold;
+        }
+      }
+
+      .change-no {
+        &:link, &:active, &:visited, &:hover {
+          float: right;
+
+          color: @gray;
+          font-weight: bold;
+        }
+      }
+
+      .change-description {
+        float: left;
+
+        font-size: @fontSizeSmall;
+
+        a:link, a:active, a:visited, a:hover {
+          color: @textColor;
+        }
+
+        .change-details {
+          color: @grayLight;
+
+          a:link, a:visited {
+            color: @gray;
+          }
+        }
+      }
+    }
+  }
+}
+
+.post-diff {
+  .diff-extra {
+    overflow: auto;
+  }
+
+  .post-diff-details {
+    background-color: @white;
+    border: 1px solid @categoryBorder;
+    border-radius: @borderRadiusSmall;
+    .box-shadow(0px 0px 0px 3px @categoryShadow);
+    margin-bottom: @baseLineHeight;
+
+    table {
+      margin: 0px;
+      width: 100%;
+
+      td {
+        padding: 1px 4px;
+
+        &.line {
+          background-color: @categoryShadow;
+          border-right: 1px solid @categoryBorder;
+          padding-left: @baseFontSize;
+          width: 1%;
+
+          text-align: right;
+
+          a:link, a:active, a:visited, a:hover {
+            color: @grayLight;
+          }
+        }
+
+        &.even {
+          background-color: darken(@white, 3%);
+        }
+
+        &.added {
+          background-color: lighten(@green, 45%);
+
+          color: darken(@green, 20%);
+          font-weight: bold;
+
+          &.even {
+            background-color: lighten(@green, 40%);
+          }
+        }
+
+        &.removed {
+          background-color: lighten(@red, 45%);
+
+          color: darken(@red, 20%);
+          font-weight: bold;
+
+          &.even {
+            background-color: lighten(@red, 40%);
+          }
+        }
+
+        &.stag {
+          color: @gray;
+        }
+      }
+    }
+  }
 }
 }

+ 70 - 70
static/cranefly/css/cranefly/editor.less

@@ -1,71 +1,71 @@
-// Editor style
-// -------------------------
-
-.editor {
-  background-color: @editorBackground;
-  border: 1px solid darken(@editorBackground, 10%);
-  border-radius: @baseBorderRadius;
-
-  .editor-error {
-    padding: @editorPadding;
-    padding-bottom: 0px;
-    margin-bottom: @editorPadding * -1;
-
-    .help-block {
-      color: @red;
-      font-weight: bold;
-    }
-  }
-
-  .editor-input {
-    padding: @editorPadding;
-
-    &>div {
-      margin-right: 2px;
-
-      textarea {
-        border: none;
-        box-shadow: none;
-        margin: @editorPadding * -1;
-        padding: @editorPadding;
-        width: 100%;
-
-        font-family: @monoFontFamily;
-      } 
-    }
-  }
-
-  .editor-actions {
-    border-top: 1px solid darken(@editorBackground, 10%);
-    overflow: auto;
-    padding: @editorPadding;
-
-    &>.btn {
-      margin-left: @baseFontSize;
-    }
-
-    .editor-tools {
-      margin: 0px;
-
-      li {
-        float: left;
-        margin-right: @editorPadding;
-
-        .btn {
-          padding-left: 7px;
-          padding-right: 7px;
-        }
-      }
-    }
-  }
-}
-
-.form-container {
-  .editor {
-    border-color: darken(@editorBackground, 20%);
-
-    .editor-actions {
-      border-top-color: darken(@editorBackground, 20%);
-    }
-  }
+// Editor style
+// -------------------------
+
+.editor {
+  background-color: @editorBackground;
+  border: 1px solid darken(@editorBackground, 10%);
+  border-radius: @baseBorderRadius;
+
+  .editor-error {
+    padding: @editorPadding;
+    padding-bottom: 0px;
+    margin-bottom: @editorPadding * -1;
+
+    .help-block {
+      color: @red;
+      font-weight: bold;
+    }
+  }
+
+  .editor-input {
+    padding: @editorPadding;
+
+    &>div {
+      margin-right: 2px;
+
+      textarea {
+        border: none;
+        box-shadow: none;
+        margin: @editorPadding * -1;
+        padding: @editorPadding;
+        width: 100%;
+
+        font-family: @monoFontFamily;
+      } 
+    }
+  }
+
+  .editor-actions {
+    border-top: 1px solid darken(@editorBackground, 10%);
+    overflow: auto;
+    padding: @editorPadding;
+
+    &>.btn {
+      margin-left: @baseFontSize;
+    }
+
+    .editor-tools {
+      margin: 0px;
+
+      li {
+        float: left;
+        margin-right: @editorPadding;
+
+        .btn {
+          padding-left: 7px;
+          padding-right: 7px;
+        }
+      }
+    }
+  }
+}
+
+.form-container {
+  .editor {
+    border-color: darken(@editorBackground, 20%);
+
+    .editor-actions {
+      border-top-color: darken(@editorBackground, 20%);
+    }
+  }
 }
 }

+ 55 - 55
static/cranefly/css/cranefly/error.less

@@ -1,56 +1,56 @@
-// Error pages
-// -------------------------
-
-.error-page {
-	text-align: center;
-
-	.error-color {
-		color: @red;
-	}
-
-	.error-ban-reason {
-		border: 1px solid @red;
-		border-radius: @baseBorderRadius;
-		background-color: lighten(@red, 46%);
-		background-image: repeating-linear-gradient(45deg, transparent, transparent 5px, lighten(@red, 48%) 5px, lighten(@red, 48%) 10px);
-		.box-shadow(0px 0px 0px 3px lighten(@red, 20%));
-		padding: @paddingLarge;
-
-		text-align: left;
-
-		:last-child {
-			margin-bottom: 0px;
-			padding-bottom: 0px;
-		}
-	}
-
-	.error-ban-expires {
-		color: @red;
-		font-weight: bold;
-	}
-
-	.error-protips {
-		margin-top: (@baseLineHeight * 2);
-
-		.go-back {
-			display: none;
-		}
-
-		a:link, a:visited {
-			background-color: @textColor;
-			border: 1px solid @black;
-			border-radius: @borderRadiusLarge;
-			margin: @paddingSmall;
-			.opacity(70);
-			padding: @paddingLarge;
-
-			color: @white;
-			font-weight: bold;
-			text-decoration: none;
-		}
-
-		a:hover, a:active {
-			.opacity(100);
-		}
-	}
+// Error pages
+// -------------------------
+
+.error-page {
+	text-align: center;
+
+	.error-color {
+		color: @red;
+	}
+
+	.error-ban-reason {
+		border: 1px solid @red;
+		border-radius: @baseBorderRadius;
+		background-color: lighten(@red, 46%);
+		background-image: repeating-linear-gradient(45deg, transparent, transparent 5px, lighten(@red, 48%) 5px, lighten(@red, 48%) 10px);
+		.box-shadow(0px 0px 0px 3px lighten(@red, 20%));
+		padding: @paddingLarge;
+
+		text-align: left;
+
+		:last-child {
+			margin-bottom: 0px;
+			padding-bottom: 0px;
+		}
+	}
+
+	.error-ban-expires {
+		color: @red;
+		font-weight: bold;
+	}
+
+	.error-protips {
+		margin-top: (@baseLineHeight * 2);
+
+		.go-back {
+			display: none;
+		}
+
+		a:link, a:visited {
+			background-color: @textColor;
+			border: 1px solid @black;
+			border-radius: @borderRadiusLarge;
+			margin: @paddingSmall;
+			.opacity(70);
+			padding: @paddingLarge;
+
+			color: @white;
+			font-weight: bold;
+			text-decoration: none;
+		}
+
+		a:hover, a:active {
+			.opacity(100);
+		}
+	}
 }
 }

+ 124 - 124
static/cranefly/css/cranefly/forms.less

@@ -1,125 +1,125 @@
-// Forms themes
-// -------------------------
-
-// Full-page form
-// -------------------------
-.form-container {
-  background: @white;
-  border: 1px solid darken(@bodyBackground, 8%);
-  border-radius: @baseBorderRadius;
-  margin: 0px ((@baseLineHeight * -1) - 1px);
-  margin-bottom: @baseLineHeight;
-  padding: @baseLineHeight;
-  padding-top: @baseLineHeight * -0.75;
-
-  &.container-horizontal {
-    margin: 0px ((@baseLineHeight * -2) - 1px);
-    margin-bottom: @baseLineHeight;
-  }
-
-  .form-header {
-  	border-bottom: 1px solid darken(@bodyBackground, 8%);
-  	margin-top: @baseLineHeight * -1;
-  	margin-bottom: @baseLineHeight;
-  	padding: (@baseLineHeight / 2) 0px;
-
-  	h1 {
-  	  margin: 0px;
-  	  padding: 0px;
-
-  	  font-size: @fontSizeLarge;
-
-      small {
-        font-size: @fontSizeLarge * 0.75;
-        font-weight: bold;
-      }
-  	}
-
-    .btn {
-      margin-left: @baseFontSize;
-      position: relative;
-      bottom: @baseLineHeight * 1.5;
-    }
-  }
-
-  .form-preview {
-    background-color: @bodyBackground;
-    border: 1px solid darken(@bodyBackground, 8%);
-    border-radius: @baseBorderRadius;
-    margin-bottom: @baseLineHeight;
-    padding: @baseLineHeight;
-  }
-
-  form {
-  	margin: 0px;
-
-  	fieldset {
-  	  border-top: 1px solid darken(@bodyBackground, 8%);
-  	  padding-top: @baseLineHeight * 0.5;
-
-      legend {
-        margin-bottom: @baseLineHeight * -0.5;
-
-        color: @grayLight;
-        font-size: @baseFontSize;
-        font-weight: bold;
-
-        div {
-          margin-bottom: @baseLineHeight * -0.5;
-        }
-      }
-
-  	  &.first {
-  	  	border-top: none;
-  	  	padding-top: 0px;
-  	  }
-  	}
-
-  	.control-label {
-  	  font-weight: bold;
-  	}
-
-  	&.form-horizontal {
-  		fieldset {
-  			&.last {
-  		    .control-group:last-child {
-  		  		margin-bottom: 0px;
-  		  		padding-bottom: 0px;
-  		    }
-  		  }
-  		}
-
-		  .form-actions {
-	      padding-left: @horizontalComponentOffset + @baseLineHeight;
-		  }
-	  }
-  }
-
-  .form-actions {
-		border-radius: 0px 0px @baseBorderRadius @baseBorderRadius;
-		margin: (@baseLineHeight * -1);
-		margin-top: @baseLineHeight * 0.5;
-
-    .form-actions-protip {
-      margin-left: @baseFontSize;
-
-      color: @gray;
-      font-weight: bold;
-    }
-  }
-}
-
-// Only v-resize textareas
-textarea {
-  resize: vertical;
-}
-
-// Responsiveness haxxord
-@media (min-width: 1200px) {
-  .form-container {
-    &.container-horizontal {
-      margin: 0px ((@baseLineHeight * -1) - 1px);
-      margin-bottom: @baseLineHeight;
-    }
-  }
+// Forms themes
+// -------------------------
+
+// Full-page form
+// -------------------------
+.form-container {
+  background: @white;
+  border: 1px solid darken(@bodyBackground, 8%);
+  border-radius: @baseBorderRadius;
+  margin: 0px ((@baseLineHeight * -1) - 1px);
+  margin-bottom: @baseLineHeight;
+  padding: @baseLineHeight;
+  padding-top: @baseLineHeight * -0.75;
+
+  &.container-horizontal {
+    margin: 0px ((@baseLineHeight * -2) - 1px);
+    margin-bottom: @baseLineHeight;
+  }
+
+  .form-header {
+  	border-bottom: 1px solid darken(@bodyBackground, 8%);
+  	margin-top: @baseLineHeight * -1;
+  	margin-bottom: @baseLineHeight;
+  	padding: (@baseLineHeight / 2) 0px;
+
+  	h1 {
+  	  margin: 0px;
+  	  padding: 0px;
+
+  	  font-size: @fontSizeLarge;
+
+      small {
+        font-size: @fontSizeLarge * 0.75;
+        font-weight: bold;
+      }
+  	}
+
+    .btn {
+      margin-left: @baseFontSize;
+      position: relative;
+      bottom: @baseLineHeight * 1.5;
+    }
+  }
+
+  .form-preview {
+    background-color: @bodyBackground;
+    border: 1px solid darken(@bodyBackground, 8%);
+    border-radius: @baseBorderRadius;
+    margin-bottom: @baseLineHeight;
+    padding: @baseLineHeight;
+  }
+
+  form {
+  	margin: 0px;
+
+  	fieldset {
+  	  border-top: 1px solid darken(@bodyBackground, 8%);
+  	  padding-top: @baseLineHeight * 0.5;
+
+      legend {
+        margin-bottom: @baseLineHeight * -0.5;
+
+        color: @grayLight;
+        font-size: @baseFontSize;
+        font-weight: bold;
+
+        div {
+          margin-bottom: @baseLineHeight * -0.5;
+        }
+      }
+
+  	  &.first {
+  	  	border-top: none;
+  	  	padding-top: 0px;
+  	  }
+  	}
+
+  	.control-label {
+  	  font-weight: bold;
+  	}
+
+  	&.form-horizontal {
+  		fieldset {
+  			&.last {
+  		    .control-group:last-child {
+  		  		margin-bottom: 0px;
+  		  		padding-bottom: 0px;
+  		    }
+  		  }
+  		}
+
+		  .form-actions {
+	      padding-left: @horizontalComponentOffset + @baseLineHeight;
+		  }
+	  }
+  }
+
+  .form-actions {
+		border-radius: 0px 0px @baseBorderRadius @baseBorderRadius;
+		margin: (@baseLineHeight * -1);
+		margin-top: @baseLineHeight * 0.5;
+
+    .form-actions-protip {
+      margin-left: @baseFontSize;
+
+      color: @gray;
+      font-weight: bold;
+    }
+  }
+}
+
+// Only v-resize textareas
+textarea {
+  resize: vertical;
+}
+
+// Responsiveness haxxord
+@media (min-width: 1200px) {
+  .form-container {
+    &.container-horizontal {
+      margin: 0px ((@baseLineHeight * -1) - 1px);
+      margin-bottom: @baseLineHeight;
+    }
+  }
 }
 }

+ 566 - 566
static/cranefly/css/cranefly/forum.less

@@ -1,567 +1,567 @@
-// Forum View
-// -------------------------
-
-// Subforums list
-.forum-subforums-list {
-  background-color: @categoryBackground;
-  border: 1px solid @categoryBorder;
-  border-radius: @borderRadiusSmall;
-  .box-shadow(0px 0px 0px 3px @categoryShadow);
-  margin-bottom: @baseLineHeight;
-
-  .header {
-    background-color: @categoryHeader;
-    border: 1px solid @categoryBorder;
-    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
-    margin: -1px;
-    margin-bottom: 0px;
-    padding: (@fontSizeSmall / 3) (@fontSizeSmall - 2px);
-
-    h2 {
-      margin: 0px;
-      padding: 0px;
-
-      color: @grayDark;
-      font-size: @fontSizeSmall;
-      font-weight: bold;
-      line-height: @baseLineHeight;
-      text-align: left;
-
-      small {
-        margin-left: @baseFontSize / 2;
-
-        color: @grayLight;
-        font-size: @fontSizeSmall;
-      }
-    }
-  }
-
-  .forum {
-    border-bottom: 1px solid @categoryBorder;
-    height: 21px;
-    overflow: visible;
-    padding: ((@fontSizeLarge / 2) + 6px) (@fontSizeSmall - 2px);
-
-    &.last {
-      border-bottom: none;
-    }
-
-    .forum-icon {
-      float: left;
-
-      .forum-icon-wrap {
-        background-color: @itemOldColor;
-        border: 1px solid darken(@itemOldColor, 10%);
-        border-radius: @baseBorderRadius;
-        padding: ((@forumIconSize - 22px) / 2) ((@forumIconSize - 16px) / 2);
-        position: relative;
-        bottom: (@forumIconSize - @baseLineHeight) / 2;
-
-        &.forum-icon-new {
-          background-color: @itemNewColor;
-          border: 1px solid darken(@itemNewColor, 10%);
-        }
-
-        &.forum-icon-redirect {
-          background-color: @itemMovedColor;
-          border: 1px solid darken(@itemMovedColor, 10%);
-        }
-      }
-    }
-
-    .forum-main {
-      margin-left: 34px;
-
-      h3 {
-        float: left;
-        margin: 0px;
-        padding: 0px;
-
-        font-size: @fontSizeLarge;
-        font-weight: normal;
-        line-height: @baseLineHeight;
-
-        a:link, a:visited {
-          color: @textColor;
-        }
-      }
-
-      .dropdown {
-        float: right;
-        right: @baseFontSize;
-
-        .subforum {
-          &:link, &:visited {
-            color: @grayLight;
-            font-weight: bold;
-          }
-
-          &:hover, &:active {
-            color: @textColor;
-          }
-        }
-
-        .dropdown-toggle {
-          padding: 4px 8px;
-          .opacity(60);
-
-          color: @textColor;
-          font-weight: bold;
-
-          &:hover, &:active, &:focus {
-            .opacity(100);
-
-            text-decoration: none;
-          }
-        }
-
-        &.open .dropdown-toggle {
-          background-color: @categoryShadow;
-          border-radius: @baseBorderRadius @baseBorderRadius 0px 0px;
-          .opacity(100);
-          padding-bottom: 6px;
-
-          text-decoration: none;
-        }
-
-        .dropdown-menu {
-          background: none;
-          border: none;
-          box-shadow: none;
-          z-index: 2;
-
-          .dropdown-shadow{
-            border-radius: @baseBorderRadius;
-            .box-shadow(0px 0px 3px @grayLight);
-            width: 256px;
-            position: relative;
-            right: 0px;
-            top: -4px;
-
-            ul {
-              background-color: @bodyBackground;
-              border-radius: @baseBorderRadius;
-              margin: 0px;
-              padding: 0px;
-
-              li {
-                margin: 0px;
-                padding: 0px;
-                list-style: none;
-
-                a {
-                  border-bottom: 1px dotted @categoryBorder;
-                  display: block;
-                  .opacity(70);
-                  padding: 6px 8px;
-
-                  color: @textColor;
-                  text-decoration: none;
-
-                  &:hover, &:active {
-                    .opacity(100);
-                  }
-                }
-
-                &:last-child a {
-                  border-bottom: none;
-                }
-              }
-            }
-          }
-        }
-      }
-
-      .forum-details {
-        border-left: 1px dotted darken(@categoryBackground, 12%);
-        float: right;
-        margin: -10px 0px;
-        margin-top: ((@baseFontSize - @fontSizeSmall) * -1) - 6px;
-        padding-left: @baseFontSize;
-        height: 35px;
-        width: 220px;
-
-        .thread-name {
-          a:link, a:active, a:visited, a:hover {
-            margin-bottom: 1px;
-
-            color: @textColor;
-            font-size: @fontSizeSmall;
-            font-weight: bold;
-          }
-        }
-
-        .muted {
-          font-size: @fontSizeMini;
-          line-height: @fontSizeMini;
-
-          .last-poster, a:link, a:active, a:visited, a:hover {
-            color: @gray;
-          }
-        }
-
-        em {
-          position: relative;
-          top: (35px - @baseLineHeight) / 2;
-
-          color: @grayLight;
-
-          strong {
-            color: @textColor;
-            font-weight: normal;
-          }
-        }
-      }
-
-      .forum-meta-tooltip {
-        .tooltip-inner {
-          max-width: 400px;
-          text-align: left;
-
-          .forum-stats {
-            color: @grayLight;
-            font-size: @fontSizeMini;
-
-            strong {
-              color: @white;
-            }
-
-            span {
-              margin-right: @baseFontSize;
-            }
-          }
-
-          .forum-description {
-            clear: both;
-            margin: 0px;
-            margin-bottom: @baseFontSize / 2;
-            padding: 0px;
-
-            color: @grayLighter;
-            font-size: @baseFontSize;
-          }
-        }
-      }
-    }
-  }
-
-  &.forum-subforums-important {
-    .header {
-      background-color: @red;
-      border: 1px solid darken(@red, 10%);
-
-      h2 {
-        color: @white;
-        text-shadow: 0px 1px 0px darken(@red, 25%);
-
-        small {
-          color: darken(@red, 40%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
-
-  &.forum-subforums-inverse {
-    .header {
-      background-color: @grayDark;
-      border: 1px solid darken(@grayDark, 10%);
-
-      h2 {
-        color: @grayLighter;
-        text-shadow: 0px 1px 0px darken(@black, 25%);
-
-        small {
-          color: lighten(@grayLight, 10%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
-
-  &.forum-subforums-info {
-    .header {
-      background-color: @bluePale;
-      border: 1px solid darken(@bluePale, 10%);
-
-      h2 {
-        color: @white;
-        text-shadow: 0px 1px 0px darken(@bluePale, 25%);
-
-        small {
-          color: darken(@bluePale, 25%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
-}
-
-// Threads list
-.forum-threads-list {
-  background-color: @categoryBackground;
-  border: 1px solid @categoryBorder;
-  border-radius: @borderRadiusSmall;
-  .box-shadow(0px 0px 0px 3px @categoryShadow);
-  margin-bottom: @baseLineHeight;
-
-  .header {
-    background-color: @categoryHeader;
-    border: 1px solid @categoryBorder;
-    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
-    margin: -1px;
-    margin-bottom: 0px;
-    padding-bottom: 1px;
-    overflow: auto;
-
-    color: @grayLight;
-    font-weight: bold;
-    font-size: @fontSizeSmall;
-
-    .row-fluid {
-      &>div {
-        min-height: auto;
-        padding: @paddingSmall;
-      }
-
-      .thread-replies {
-        float: left;
-        width: 106px;
-      }
-
-      .thread-last {
-        float: left;
-      }
-    }
-
-    .check-cell {
-      label {
-        margin: 0px;
-      }
-    }
-  }
-
-  .thread-row {
-    border-bottom: 1px solid @categoryBorder;
-    height: 38px;
-    overflow: hidden;
-    padding: (@fontSizeSmall - 2px) 0px;
-
-    .row-fluid {
-      &>div {
-        min-height: auto;
-        padding: @paddingSmall;
-        padding-bottom: 0px;
-      }
-    }
-
-    &.thread-last {
-      border-bottom: none;
-
-      &>div {
-        padding-bottom: 1px;
-      }
-    }
-
-    .thread-icon {
-      background-color: @itemOldColor;
-      border: 1px solid darken(@itemOldColor, 10%);
-      .border-radius(@baseBorderRadius);
-      display: block;
-      float: left;
-      margin: -2px 0px;
-      margin-left: -1px;
-      padding: 1px 4px;
-
-      &:hover, &:active {
-        .opacity(100);
-      }
-
-      i {
-        background-image: url("@{iconWhiteSpritePath}");
-      }
-    }
-
-    &.thread-new {
-      .thread-icon {
-        background-color: @itemNewColor;
-        border: 1px solid darken(@itemNewColor, 10%);
-      }
-
-      .thread-name {
-        color: @textColor !important;
-      }
-    }
-
-    &.threads-list-empty {
-      height: auto;
-      padding: @paddingLarge;
-
-      font-size: @fontSizeLarge;
-      text-align: center;
-    }
-
-    .thread-name {
-      margin-left: 10px;
-
-      color: lighten(@textColor, 17%);
-      font-size: @baseFontSize + 2px;
-      font-weight: bold;
-    }
-
-    .thread-details, .thread-last-reply {
-      color: @grayLight;
-      line-height: @baseFontSize;
-
-      a:link, a:visited {
-        color: @textColor;
-      }
-    }
-
-    .thread-details {
-      margin-left: 34px;
-
-      font-size: @fontSizeMini;
-    }
-
-    .thread-flags {
-      float: right;
-      margin: 0px;
-      position: relative;
-      right: -30px;
-      top: 5px;
-      padding: 0px;
-
-      li {
-        .border-radius(@baseBorderRadius);
-        display: block;
-        float: left;
-        margin-left: 3px;
-        padding: 2px 5px;
-
-        &.flag-reported {
-          background-color: @flagReported;
-        }
-
-        &.flag-notreviewed {
-          background-color: @flagReviewed;
-        }
-
-        &.flag-announcement {
-          background-color: @flagAnnouncement;
-        }
-
-        &.flag-sticky {
-          background-color: @flagSticky;
-        }
-
-        &.flag-deleted {
-          background-color: @flagDeleted;
-        }
-
-        &.flag-closed {
-          background-color: @flagClosed;
-        }
-      }
-
-      i {
-        background-image: url("@{iconWhiteSpritePath}");
-      }
-    }
-
-    .thread-activity {
-      border-left: 1px dotted darken(@categoryBackground, 12%);
-      position: relative;
-      bottom: 3px;
-
-      .thread-last-avatar {
-        float: left;
-
-        img {
-          .border-radius(@baseBorderRadius);
-          margin: 0px;
-          margin-right: @baseFontSize;
-          width: 40px;
-          height: 40px;
-        }
-      }
-
-      .thread-replies {
-        float: left;
-        margin-top: -1px;
-        margin-right: @baseFontSize;
-
-        color: @grayLight;
-        font-size: @fontSizeSmall;
-
-        .lead {
-          font-size: @baseFontSize;
-          font-weight: bold;
-          line-height: @baseLineHeight;
-        }
-
-        a:link, a:active, a:visited, a:hover {
-          color: @gray;
-        }
-      }
-
-      .thread-last-reply {
-        border-left: 1px dotted darken(@categoryBackground, 12%);
-        padding-left: @baseFontSize;
-
-      }
-
-      .thread-select {
-        background-color: darken(@categoryBackground, 5%);
-        display: block;
-        float: right;
-        margin: -12px -11px;
-        margin-left: 0px;
-        width: 29px;
-        height: 61px;
-
-        &:hover, &:focus, &:active {
-          background-color: @linkColor;
-        }
-
-        input {
-          margin: 0px;
-          position: relative;
-          right: 2px;
-          top: 26px;
-        }
-      }
-    }
-  }
-
-  .threads-actions {
-    background-color: @bodyBackground;
-    border-top: 1px solid @categoryBorder;
-    border-radius: 0px 0px @borderRadiusSmall @borderRadiusSmall;
-    overflow: auto;
-    padding: 4px;
-
-    color: @grayLight;
-    font-size: @fontSizeSmall;
-
-    form {
-      margin-bottom: 0px;
-    }
-  }
-}
-
-.forum-threads-extra {
-  overflow: auto;
-
-  &.extra-top {
-    margin-bottom: @baseLineHeight;
-  }
-
-  .threads-signin-message {
-    float: right;
-
-    a:link, a:visited {
-      color: @textColor;
-    }
-  }
+// Forum View
+// -------------------------
+
+// Subforums list
+.forum-subforums-list {
+  background-color: @categoryBackground;
+  border: 1px solid @categoryBorder;
+  border-radius: @borderRadiusSmall;
+  .box-shadow(0px 0px 0px 3px @categoryShadow);
+  margin-bottom: @baseLineHeight;
+
+  .header {
+    background-color: @categoryHeader;
+    border: 1px solid @categoryBorder;
+    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
+    margin: -1px;
+    margin-bottom: 0px;
+    padding: (@fontSizeSmall / 3) (@fontSizeSmall - 2px);
+
+    h2 {
+      margin: 0px;
+      padding: 0px;
+
+      color: @grayDark;
+      font-size: @fontSizeSmall;
+      font-weight: bold;
+      line-height: @baseLineHeight;
+      text-align: left;
+
+      small {
+        margin-left: @baseFontSize / 2;
+
+        color: @grayLight;
+        font-size: @fontSizeSmall;
+      }
+    }
+  }
+
+  .forum {
+    border-bottom: 1px solid @categoryBorder;
+    height: 21px;
+    overflow: visible;
+    padding: ((@fontSizeLarge / 2) + 6px) (@fontSizeSmall - 2px);
+
+    &.last {
+      border-bottom: none;
+    }
+
+    .forum-icon {
+      float: left;
+
+      .forum-icon-wrap {
+        background-color: @itemOldColor;
+        border: 1px solid darken(@itemOldColor, 10%);
+        border-radius: @baseBorderRadius;
+        padding: ((@forumIconSize - 22px) / 2) ((@forumIconSize - 16px) / 2);
+        position: relative;
+        bottom: (@forumIconSize - @baseLineHeight) / 2;
+
+        &.forum-icon-new {
+          background-color: @itemNewColor;
+          border: 1px solid darken(@itemNewColor, 10%);
+        }
+
+        &.forum-icon-redirect {
+          background-color: @itemMovedColor;
+          border: 1px solid darken(@itemMovedColor, 10%);
+        }
+      }
+    }
+
+    .forum-main {
+      margin-left: 34px;
+
+      h3 {
+        float: left;
+        margin: 0px;
+        padding: 0px;
+
+        font-size: @fontSizeLarge;
+        font-weight: normal;
+        line-height: @baseLineHeight;
+
+        a:link, a:visited {
+          color: @textColor;
+        }
+      }
+
+      .dropdown {
+        float: right;
+        right: @baseFontSize;
+
+        .subforum {
+          &:link, &:visited {
+            color: @grayLight;
+            font-weight: bold;
+          }
+
+          &:hover, &:active {
+            color: @textColor;
+          }
+        }
+
+        .dropdown-toggle {
+          padding: 4px 8px;
+          .opacity(60);
+
+          color: @textColor;
+          font-weight: bold;
+
+          &:hover, &:active, &:focus {
+            .opacity(100);
+
+            text-decoration: none;
+          }
+        }
+
+        &.open .dropdown-toggle {
+          background-color: @categoryShadow;
+          border-radius: @baseBorderRadius @baseBorderRadius 0px 0px;
+          .opacity(100);
+          padding-bottom: 6px;
+
+          text-decoration: none;
+        }
+
+        .dropdown-menu {
+          background: none;
+          border: none;
+          box-shadow: none;
+          z-index: 2;
+
+          .dropdown-shadow{
+            border-radius: @baseBorderRadius;
+            .box-shadow(0px 0px 3px @grayLight);
+            width: 256px;
+            position: relative;
+            right: 0px;
+            top: -4px;
+
+            ul {
+              background-color: @bodyBackground;
+              border-radius: @baseBorderRadius;
+              margin: 0px;
+              padding: 0px;
+
+              li {
+                margin: 0px;
+                padding: 0px;
+                list-style: none;
+
+                a {
+                  border-bottom: 1px dotted @categoryBorder;
+                  display: block;
+                  .opacity(70);
+                  padding: 6px 8px;
+
+                  color: @textColor;
+                  text-decoration: none;
+
+                  &:hover, &:active {
+                    .opacity(100);
+                  }
+                }
+
+                &:last-child a {
+                  border-bottom: none;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      .forum-details {
+        border-left: 1px dotted darken(@categoryBackground, 12%);
+        float: right;
+        margin: -10px 0px;
+        margin-top: ((@baseFontSize - @fontSizeSmall) * -1) - 6px;
+        padding-left: @baseFontSize;
+        height: 35px;
+        width: 220px;
+
+        .thread-name {
+          a:link, a:active, a:visited, a:hover {
+            margin-bottom: 1px;
+
+            color: @textColor;
+            font-size: @fontSizeSmall;
+            font-weight: bold;
+          }
+        }
+
+        .muted {
+          font-size: @fontSizeMini;
+          line-height: @fontSizeMini;
+
+          .last-poster, a:link, a:active, a:visited, a:hover {
+            color: @gray;
+          }
+        }
+
+        em {
+          position: relative;
+          top: (35px - @baseLineHeight) / 2;
+
+          color: @grayLight;
+
+          strong {
+            color: @textColor;
+            font-weight: normal;
+          }
+        }
+      }
+
+      .forum-meta-tooltip {
+        .tooltip-inner {
+          max-width: 400px;
+          text-align: left;
+
+          .forum-stats {
+            color: @grayLight;
+            font-size: @fontSizeMini;
+
+            strong {
+              color: @white;
+            }
+
+            span {
+              margin-right: @baseFontSize;
+            }
+          }
+
+          .forum-description {
+            clear: both;
+            margin: 0px;
+            margin-bottom: @baseFontSize / 2;
+            padding: 0px;
+
+            color: @grayLighter;
+            font-size: @baseFontSize;
+          }
+        }
+      }
+    }
+  }
+
+  &.forum-subforums-important {
+    .header {
+      background-color: @red;
+      border: 1px solid darken(@red, 10%);
+
+      h2 {
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@red, 25%);
+
+        small {
+          color: darken(@red, 40%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
+
+  &.forum-subforums-inverse {
+    .header {
+      background-color: @grayDark;
+      border: 1px solid darken(@grayDark, 10%);
+
+      h2 {
+        color: @grayLighter;
+        text-shadow: 0px 1px 0px darken(@black, 25%);
+
+        small {
+          color: lighten(@grayLight, 10%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
+
+  &.forum-subforums-info {
+    .header {
+      background-color: @bluePale;
+      border: 1px solid darken(@bluePale, 10%);
+
+      h2 {
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@bluePale, 25%);
+
+        small {
+          color: darken(@bluePale, 25%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
+}
+
+// Threads list
+.forum-threads-list {
+  background-color: @categoryBackground;
+  border: 1px solid @categoryBorder;
+  border-radius: @borderRadiusSmall;
+  .box-shadow(0px 0px 0px 3px @categoryShadow);
+  margin-bottom: @baseLineHeight;
+
+  .header {
+    background-color: @categoryHeader;
+    border: 1px solid @categoryBorder;
+    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
+    margin: -1px;
+    margin-bottom: 0px;
+    padding-bottom: 1px;
+    overflow: auto;
+
+    color: @grayLight;
+    font-weight: bold;
+    font-size: @fontSizeSmall;
+
+    .row-fluid {
+      &>div {
+        min-height: auto;
+        padding: @paddingSmall;
+      }
+
+      .thread-replies {
+        float: left;
+        width: 106px;
+      }
+
+      .thread-last {
+        float: left;
+      }
+    }
+
+    .check-cell {
+      label {
+        margin: 0px;
+      }
+    }
+  }
+
+  .thread-row {
+    border-bottom: 1px solid @categoryBorder;
+    height: 38px;
+    overflow: hidden;
+    padding: (@fontSizeSmall - 2px) 0px;
+
+    .row-fluid {
+      &>div {
+        min-height: auto;
+        padding: @paddingSmall;
+        padding-bottom: 0px;
+      }
+    }
+
+    &.thread-last {
+      border-bottom: none;
+
+      &>div {
+        padding-bottom: 1px;
+      }
+    }
+
+    .thread-icon {
+      background-color: @itemOldColor;
+      border: 1px solid darken(@itemOldColor, 10%);
+      .border-radius(@baseBorderRadius);
+      display: block;
+      float: left;
+      margin: -2px 0px;
+      margin-left: -1px;
+      padding: 1px 4px;
+
+      &:hover, &:active {
+        .opacity(100);
+      }
+
+      i {
+        background-image: url("@{iconWhiteSpritePath}");
+      }
+    }
+
+    &.thread-new {
+      .thread-icon {
+        background-color: @itemNewColor;
+        border: 1px solid darken(@itemNewColor, 10%);
+      }
+
+      .thread-name {
+        color: @textColor !important;
+      }
+    }
+
+    &.threads-list-empty {
+      height: auto;
+      padding: @paddingLarge;
+
+      font-size: @fontSizeLarge;
+      text-align: center;
+    }
+
+    .thread-name {
+      margin-left: 10px;
+
+      color: lighten(@textColor, 17%);
+      font-size: @baseFontSize + 2px;
+      font-weight: bold;
+    }
+
+    .thread-details, .thread-last-reply {
+      color: @grayLight;
+      line-height: @baseFontSize;
+
+      a:link, a:visited {
+        color: @textColor;
+      }
+    }
+
+    .thread-details {
+      margin-left: 34px;
+
+      font-size: @fontSizeMini;
+    }
+
+    .thread-flags {
+      float: right;
+      margin: 0px;
+      position: relative;
+      right: -30px;
+      top: 5px;
+      padding: 0px;
+
+      li {
+        .border-radius(@baseBorderRadius);
+        display: block;
+        float: left;
+        margin-left: 3px;
+        padding: 2px 5px;
+
+        &.flag-reported {
+          background-color: @flagReported;
+        }
+
+        &.flag-notreviewed {
+          background-color: @flagReviewed;
+        }
+
+        &.flag-announcement {
+          background-color: @flagAnnouncement;
+        }
+
+        &.flag-sticky {
+          background-color: @flagSticky;
+        }
+
+        &.flag-deleted {
+          background-color: @flagDeleted;
+        }
+
+        &.flag-closed {
+          background-color: @flagClosed;
+        }
+      }
+
+      i {
+        background-image: url("@{iconWhiteSpritePath}");
+      }
+    }
+
+    .thread-activity {
+      border-left: 1px dotted darken(@categoryBackground, 12%);
+      position: relative;
+      bottom: 3px;
+
+      .thread-last-avatar {
+        float: left;
+
+        img {
+          .border-radius(@baseBorderRadius);
+          margin: 0px;
+          margin-right: @baseFontSize;
+          width: 40px;
+          height: 40px;
+        }
+      }
+
+      .thread-replies {
+        float: left;
+        margin-top: -1px;
+        margin-right: @baseFontSize;
+
+        color: @grayLight;
+        font-size: @fontSizeSmall;
+
+        .lead {
+          font-size: @baseFontSize;
+          font-weight: bold;
+          line-height: @baseLineHeight;
+        }
+
+        a:link, a:active, a:visited, a:hover {
+          color: @gray;
+        }
+      }
+
+      .thread-last-reply {
+        border-left: 1px dotted darken(@categoryBackground, 12%);
+        padding-left: @baseFontSize;
+
+      }
+
+      .thread-select {
+        background-color: darken(@categoryBackground, 5%);
+        display: block;
+        float: right;
+        margin: -12px -11px;
+        margin-left: 0px;
+        width: 29px;
+        height: 61px;
+
+        &:hover, &:focus, &:active {
+          background-color: @linkColor;
+        }
+
+        input {
+          margin: 0px;
+          position: relative;
+          right: 2px;
+          top: 26px;
+        }
+      }
+    }
+  }
+
+  .threads-actions {
+    background-color: @bodyBackground;
+    border-top: 1px solid @categoryBorder;
+    border-radius: 0px 0px @borderRadiusSmall @borderRadiusSmall;
+    overflow: auto;
+    padding: 4px;
+
+    color: @grayLight;
+    font-size: @fontSizeSmall;
+
+    form {
+      margin-bottom: 0px;
+    }
+  }
+}
+
+.forum-threads-extra {
+  overflow: auto;
+
+  &.extra-top {
+    margin-bottom: @baseLineHeight;
+  }
+
+  .threads-signin-message {
+    float: right;
+
+    a:link, a:visited {
+      color: @textColor;
+    }
+  }
 }
 }

+ 165 - 165
static/cranefly/css/cranefly/forummap.less

@@ -1,166 +1,166 @@
-// Forum Map
-// -------------------------
-
-.forum-map-category {
-  background-color: @categoryBackground;
-  border: 1px solid @categoryBorder;
-  border-radius: @borderRadiusSmall;
-  .box-shadow(0px 0px 0px 3px @categoryShadow);
-  margin-bottom: @baseLineHeight;
-
-  .header {
-    background-color: @categoryHeader;
-    border: 1px solid @categoryBorder;
-    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
-    margin: -1px;
-    margin-bottom: 0px;
-    padding: (@fontSizeSmall / 3) (@fontSizeSmall - 2px);
-
-    h2 {
-      margin: 0px;
-      padding: 0px;
-
-      color: @grayDark;
-      font-size: @fontSizeSmall;
-      font-weight: bold;
-      line-height: @baseLineHeight;
-      text-align: left;
-
-      small {
-        margin-left: @baseFontSize / 2;
-
-        color: @grayLight;
-        font-size: @fontSizeSmall;
-      }
-    }
-  }
-
-  .forum-map-forum, .forum-map-subforum {
-    border-bottom: 1px solid @categoryBorder;
-    overflow: auto;
-    padding: (@fontSizeSmall / 2) (@fontSizeSmall - 2px);
-
-    h3 {
-      margin: 0px;
-      padding: 0px;
-
-      font-size: @baseFontSize;
-      line-height: @baseLineHeight;
-
-      a:link, a:visited {
-        color: @gray;
-      }
-
-      a:active, a:hover {
-        color: @textColor;
-      }
-    }
-  }
-
-  .forum-map-subforum {
-  	padding-left: @baseLineHeight * 0.75;
-
-  	span {
-  		&.tree-t, &.tree-l, &.tree-s, &.tree-i {
-  			display: inline-block;
-  			height: @baseLineHeight;
-  			width: @baseLineHeight * 0.5;
-  		}
-
-  		&.tree-t {
-  			border-left: 1px solid @grayLight;
-        margin-right: 2px;
-
-  			span {
-  				border-top: 1px solid @grayLight;
-  			  display: inline-block;
-  				height: 1px;
-  				width: 100%;
-  				margin-bottom: 3px;
-  			}
-  		}
-
-  		&.tree-l {
-        margin-right: 4px;
-
-  			span {
-  			  border-left: 1px solid @grayLight;
-  				border-bottom: 1px solid @grayLight;
-  			  display: inline-block;
-  				height: @baseLineHeight * 0.5;
-  				width: 100%;
-  				margin-bottom: 3px;
-  			}
-  		}
-
-  		&.tree-i {
-  			border-left: 1px solid @grayLight;
-  			position: relative;
-  			top: 5px;
-        margin-top: -5px;
-  			margin-right: 4px;
-  		}
-
-  		&.tree-s {
-        height: 1px;
-  			width: (@baseLineHeight * 0.5) + 2px;
-  			margin-right: 4px;
-  		}
-  	}
-  }
-
-  &>div:last-child {
-    border-bottom: none;
-  }
-  
-  &.forum-map-category-important {
-    .header {
-      background-color: @red;
-      border: 1px solid darken(@red, 10%);
-
-      h2 {
-        color: @white;
-        text-shadow: 0px 1px 0px darken(@red, 25%);
-
-        small {
-          color: darken(@red, 40%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
-
-  &.forum-map-category-inverse {
-    .header {
-      background-color: @grayDark;
-      border: 1px solid darken(@grayDark, 10%);
-
-      h2 {
-        color: @grayLighter;
-        text-shadow: 0px 1px 0px darken(@black, 25%);
-
-        small {
-          color: lighten(@grayLight, 10%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
-
-  &.forum-map-category-info {
-    .header {
-      background-color: @bluePale;
-      border: 1px solid darken(@bluePale, 10%);
-
-      h2 {
-        color: @white;
-        text-shadow: 0px 1px 0px darken(@bluePale, 25%);
-
-        small {
-          color: darken(@bluePale, 25%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
+// Forum Map
+// -------------------------
+
+.forum-map-category {
+  background-color: @categoryBackground;
+  border: 1px solid @categoryBorder;
+  border-radius: @borderRadiusSmall;
+  .box-shadow(0px 0px 0px 3px @categoryShadow);
+  margin-bottom: @baseLineHeight;
+
+  .header {
+    background-color: @categoryHeader;
+    border: 1px solid @categoryBorder;
+    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
+    margin: -1px;
+    margin-bottom: 0px;
+    padding: (@fontSizeSmall / 3) (@fontSizeSmall - 2px);
+
+    h2 {
+      margin: 0px;
+      padding: 0px;
+
+      color: @grayDark;
+      font-size: @fontSizeSmall;
+      font-weight: bold;
+      line-height: @baseLineHeight;
+      text-align: left;
+
+      small {
+        margin-left: @baseFontSize / 2;
+
+        color: @grayLight;
+        font-size: @fontSizeSmall;
+      }
+    }
+  }
+
+  .forum-map-forum, .forum-map-subforum {
+    border-bottom: 1px solid @categoryBorder;
+    overflow: auto;
+    padding: (@fontSizeSmall / 2) (@fontSizeSmall - 2px);
+
+    h3 {
+      margin: 0px;
+      padding: 0px;
+
+      font-size: @baseFontSize;
+      line-height: @baseLineHeight;
+
+      a:link, a:visited {
+        color: @gray;
+      }
+
+      a:active, a:hover {
+        color: @textColor;
+      }
+    }
+  }
+
+  .forum-map-subforum {
+  	padding-left: @baseLineHeight * 0.75;
+
+  	span {
+  		&.tree-t, &.tree-l, &.tree-s, &.tree-i {
+  			display: inline-block;
+  			height: @baseLineHeight;
+  			width: @baseLineHeight * 0.5;
+  		}
+
+  		&.tree-t {
+  			border-left: 1px solid @grayLight;
+        margin-right: 2px;
+
+  			span {
+  				border-top: 1px solid @grayLight;
+  			  display: inline-block;
+  				height: 1px;
+  				width: 100%;
+  				margin-bottom: 3px;
+  			}
+  		}
+
+  		&.tree-l {
+        margin-right: 4px;
+
+  			span {
+  			  border-left: 1px solid @grayLight;
+  				border-bottom: 1px solid @grayLight;
+  			  display: inline-block;
+  				height: @baseLineHeight * 0.5;
+  				width: 100%;
+  				margin-bottom: 3px;
+  			}
+  		}
+
+  		&.tree-i {
+  			border-left: 1px solid @grayLight;
+  			position: relative;
+  			top: 5px;
+        margin-top: -5px;
+  			margin-right: 4px;
+  		}
+
+  		&.tree-s {
+        height: 1px;
+  			width: (@baseLineHeight * 0.5) + 2px;
+  			margin-right: 4px;
+  		}
+  	}
+  }
+
+  &>div:last-child {
+    border-bottom: none;
+  }
+  
+  &.forum-map-category-important {
+    .header {
+      background-color: @red;
+      border: 1px solid darken(@red, 10%);
+
+      h2 {
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@red, 25%);
+
+        small {
+          color: darken(@red, 40%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
+
+  &.forum-map-category-inverse {
+    .header {
+      background-color: @grayDark;
+      border: 1px solid darken(@grayDark, 10%);
+
+      h2 {
+        color: @grayLighter;
+        text-shadow: 0px 1px 0px darken(@black, 25%);
+
+        small {
+          color: lighten(@grayLight, 10%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
+
+  &.forum-map-category-info {
+    .header {
+      background-color: @bluePale;
+      border: 1px solid darken(@bluePale, 10%);
+
+      h2 {
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@bluePale, 25%);
+
+        small {
+          color: darken(@bluePale, 25%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
 }
 }

+ 170 - 170
static/cranefly/css/cranefly/header.less

@@ -1,171 +1,171 @@
-// Primary header
-// -------------------------
-.header-primary {
-  background-color: @pageHeaderBackground;
-  border-bottom: 1px solid @pageHeaderBorder;
-  margin: 0px;
-  margin-top: 0px;
-  padding-top: @baseLineHeight / 2;
-
-  .breadcrumb {
-    background: none;
-    border: none;
-    margin: 0px;
-    margin-bottom: @fontSizeSmall * -0.5;
-    padding: 0px;
-
-    li {
-      font-size: @fontSizeSmall;
-      font-weight: bold;
-      text-shadow: none;
-
-      a:link, a:visited {
-        color: @grayLight;
-      }
-
-      a:hover, a:active {
-        color: @textColor;
-      }
-
-      .divider {
-        padding-left: 0px;
-        padding-right: 0px;
-
-        i {
-          .opacity(20);
-          position: relative;
-          bottom: 1px;
-        }
-      }
-    }
-  }
-
-  h1 {
-    color: @gray;
-    font-size: @fontSizeLarge * 2;
-    font-weight: normal;
-  }
-
-  .header-stats {
-    overflow: visible;
-    margin-bottom: 0px;
-
-    color: @grayLight;
-
-    li {
-      float: left;
-      margin-right: @baseFontSize;
-
-      &>a {
-        color: @grayLight;
-
-        &:hover, &:active {
-          color: @textColor;
-        }
-      }
-
-      &>i {
-        .opacity(50);
-      }
-
-      &.stats-form {
-        float: right;
-
-        form {
-          margin: 0px;
-          margin-bottom: -12px;
-          padding: 0px;
-
-          button {
-            position: relative;
-            bottom: 12px;
-
-            &>i {
-              position: relative;
-              top: 0px;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  .header-tabs {
-    border-bottom: 0px;
-    margin: 0px;
-    margin-top: (@baseLineHeight  * -0.5);
-    position: relative;
-    top: (@baseLineHeight * 0.5) - 1px;
-
-    li {
-      a {
-        &:link, &:visited {
-          background: none;
-          border: none;
-          border-radius: 0px;
-          margin-bottom: 4px;
-          padding: (@baseLineHeight / 3) (@baseLineHeight / 2);
-
-          color: lighten(@gray, 20%);
-          font-weight: bold;
-        }
-
-        &:hover, &:active, a:focus {
-          background: none;
-          border-bottom: 4px solid @gray;
-          margin-bottom: 0px;
-
-          color: @gray;
-        }
-      }
-
-      &.active {
-        a:link, a:visited, a:hover, a:active {
-          background: none;
-          border-bottom: 4px solid @red;
-          margin-bottom: 0px;
-
-          color: @textColor;
-        }
-      }
-
-      .form-inline {
-        margin: 0px;
-        margin-left: @baseFontSize;
-        margin-bottom: 7px;
-
-        .btn-icon {
-          padding-left: 7px;
-          padding-right: 7px;
-        }
-
-        i {
-          position: relative;
-          top: 0px;
-        }
-      }
-
-      & {
-        a.btn {
-          border: 1px solid @btnBorder;
-          *border: 0; // Remove the border to prevent IE7's black border on input:focus
-          border-radius: @baseBorderRadius;
-          margin: 0px;
-          padding: 4px 12px;
-
-          color: @textColor;
-
-          i {
-            position: relative;
-            top: 0px;
-          }
-
-          &:visited, &:hover {
-            background-color: @white;
-            border-color: lighten(@grayLight, 5%);
-          }
-        }
-      }
-    }
-  }
+// Primary header
+// -------------------------
+.header-primary {
+  background-color: @pageHeaderBackground;
+  border-bottom: 1px solid @pageHeaderBorder;
+  margin: 0px;
+  margin-top: 0px;
+  padding-top: @baseLineHeight / 2;
+
+  .breadcrumb {
+    background: none;
+    border: none;
+    margin: 0px;
+    margin-bottom: @fontSizeSmall * -0.5;
+    padding: 0px;
+
+    li {
+      font-size: @fontSizeSmall;
+      font-weight: bold;
+      text-shadow: none;
+
+      a:link, a:visited {
+        color: @grayLight;
+      }
+
+      a:hover, a:active {
+        color: @textColor;
+      }
+
+      .divider {
+        padding-left: 0px;
+        padding-right: 0px;
+
+        i {
+          .opacity(20);
+          position: relative;
+          bottom: 1px;
+        }
+      }
+    }
+  }
+
+  h1 {
+    color: @gray;
+    font-size: @fontSizeLarge * 2;
+    font-weight: normal;
+  }
+
+  .header-stats {
+    overflow: visible;
+    margin-bottom: 0px;
+
+    color: @grayLight;
+
+    li {
+      float: left;
+      margin-right: @baseFontSize;
+
+      &>a {
+        color: @grayLight;
+
+        &:hover, &:active {
+          color: @textColor;
+        }
+      }
+
+      &>i {
+        .opacity(50);
+      }
+
+      &.stats-form {
+        float: right;
+
+        form {
+          margin: 0px;
+          margin-bottom: -12px;
+          padding: 0px;
+
+          button {
+            position: relative;
+            bottom: 12px;
+
+            &>i {
+              position: relative;
+              top: 0px;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .header-tabs {
+    border-bottom: 0px;
+    margin: 0px;
+    margin-top: (@baseLineHeight  * -0.5);
+    position: relative;
+    top: (@baseLineHeight * 0.5) - 1px;
+
+    li {
+      a {
+        &:link, &:visited {
+          background: none;
+          border: none;
+          border-radius: 0px;
+          margin-bottom: 4px;
+          padding: (@baseLineHeight / 3) (@baseLineHeight / 2);
+
+          color: lighten(@gray, 20%);
+          font-weight: bold;
+        }
+
+        &:hover, &:active, a:focus {
+          background: none;
+          border-bottom: 4px solid @gray;
+          margin-bottom: 0px;
+
+          color: @gray;
+        }
+      }
+
+      &.active {
+        a:link, a:visited, a:hover, a:active {
+          background: none;
+          border-bottom: 4px solid @red;
+          margin-bottom: 0px;
+
+          color: @textColor;
+        }
+      }
+
+      .form-inline {
+        margin: 0px;
+        margin-left: @baseFontSize;
+        margin-bottom: 7px;
+
+        .btn-icon {
+          padding-left: 7px;
+          padding-right: 7px;
+        }
+
+        i {
+          position: relative;
+          top: 0px;
+        }
+      }
+
+      & {
+        a.btn {
+          border: 1px solid @btnBorder;
+          *border: 0; // Remove the border to prevent IE7's black border on input:focus
+          border-radius: @baseBorderRadius;
+          margin: 0px;
+          padding: 4px 12px;
+
+          color: @textColor;
+
+          i {
+            position: relative;
+            top: 0px;
+          }
+
+          &:visited, &:hover {
+            background-color: @white;
+            border-color: lighten(@grayLight, 5%);
+          }
+        }
+      }
+    }
+  }
 }
 }

+ 444 - 444
static/cranefly/css/cranefly/index.less

@@ -1,444 +1,444 @@
-//
-// Board index page
-// --------------------------------------------------
-
-.index-sidebar {
-  position: relative;
-  bottom: 9px;
-}
-
-// Forums list
-// -------------------------
-.index-category {
-  background-color: @categoryBackground;
-  border: 1px solid @categoryBorder;
-  border-radius: @borderRadiusSmall;
-  .box-shadow(0px 0px 0px 3px @categoryShadow);
-  margin-bottom: @baseLineHeight;
-
-  .header {
-    background-color: @categoryHeader;
-    border: 1px solid @categoryBorder;
-    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
-    margin: -1px;
-    margin-bottom: 0px;
-    padding: (@fontSizeSmall / 3) (@fontSizeSmall - 2px);
-
-    h2 {
-      margin: 0px;
-      padding: 0px;
-
-      color: @grayDark;
-      font-size: @fontSizeSmall;
-      font-weight: bold;
-      line-height: @baseLineHeight;
-      text-align: left;
-
-      small {
-        margin-left: @baseFontSize / 2;
-
-        color: @grayLight;
-        font-size: @fontSizeSmall;
-      }
-    }
-  }
-
-  .forum {
-    border-bottom: 1px solid @categoryBorder;
-    height: 21px;
-    overflow: visible;
-    padding: ((@fontSizeLarge / 2) + 6px) (@fontSizeSmall - 2px);
-
-    &.last {
-      border-bottom: none;
-    }
-
-    .forum-icon {
-      float: left;
-
-      .forum-icon-wrap {
-        background-color: @itemOldColor;
-        border: 1px solid darken(@itemOldColor, 10%);
-        border-radius: @baseBorderRadius;
-        padding: ((@forumIconSize - 22px) / 2) ((@forumIconSize - 16px) / 2);
-        position: relative;
-        bottom: (@forumIconSize - @baseLineHeight) / 2;
-
-        &.forum-icon-new {
-          background-color: @itemNewColor;
-          border: 1px solid darken(@itemNewColor, 10%);
-        }
-
-        &.forum-icon-redirect {
-          background-color: @itemMovedColor;
-          border: 1px solid darken(@itemMovedColor, 10%);
-        }
-      }
-    }
-
-    .forum-main {
-      margin-left: @forumIconSize + 10px;
-
-      h3 {
-        float: left;
-        margin: 0px;
-        padding: 0px;
-
-        font-size: @fontSizeLarge;
-        font-weight: normal;
-        line-height: @baseLineHeight;
-
-        a:link, a:visited {
-          color: @textColor;
-        }
-      }
-
-      .dropdown {
-        float: right;
-        right: @baseFontSize;
-
-        .subforum {
-          &:link, &:visited {
-            color: @grayLight;
-            font-weight: bold;
-          }
-
-          &:hover, &:active {
-            color: @textColor;
-          }
-        }
-
-        .dropdown-toggle {
-          padding: 4px 8px;
-          .opacity(60);
-
-          color: @textColor;
-          font-weight: bold;
-
-          &:hover, &:active, &:focus {
-            .opacity(100);
-
-            text-decoration: none;
-          }
-        }
-
-        &.open .dropdown-toggle {
-          background-color: @categoryShadow;
-          border-radius: @baseBorderRadius @baseBorderRadius 0px 0px;
-          .opacity(100);
-          padding-bottom: 6px;
-
-          text-decoration: none;
-        }
-
-        .dropdown-menu {
-          background: none;
-          border: none;
-          box-shadow: none;
-          z-index: 2;
-
-          .dropdown-shadow{
-            border-radius: @baseBorderRadius;
-            .box-shadow(0px 0px 3px @grayLight);
-            width: 256px;
-            position: relative;
-            right: 0px;
-            top: -4px;
-
-            ul {
-              background-color: @bodyBackground;
-              border-radius: @baseBorderRadius;
-              margin: 0px;
-              padding: 0px;
-
-              li {
-                margin: 0px;
-                padding: 0px;
-                list-style: none;
-
-                a {
-                  border-bottom: 1px dotted @categoryBorder;
-                  display: block;
-                  .opacity(70);
-                  padding: 6px 8px;
-
-                  color: @textColor;
-                  text-decoration: none;
-
-                  &:hover, &:active {
-                    .opacity(100);
-                  }
-                }
-
-                &:last-child a {
-                  border-bottom: none;
-                }
-              }
-            }
-          }
-        }
-      }
-
-      .forum-details {
-        border-left: 1px dotted darken(@categoryBackground, 12%);
-        float: right;
-        margin: -10px 0px;
-        margin-top: ((@baseFontSize - @fontSizeSmall) * -1) - 6px;
-        padding-left: @baseFontSize;
-        height: 35px;
-        width: 220px;
-
-        .thread-name {
-          a:link, a:active, a:visited, a:hover {
-            margin-bottom: 1px;
-
-            color: @textColor;
-            font-size: @fontSizeSmall;
-            font-weight: bold;
-          }
-        }
-
-        .muted {
-          font-size: @fontSizeMini;
-          line-height: @fontSizeMini;
-
-          .last-poster, a:link, a:active, a:visited, a:hover {
-            color: @gray;
-          }
-        }
-
-        em {
-          position: relative;
-          top: (35px - @baseLineHeight) / 2;
-
-          color: @grayLight;
-
-          strong {
-            color: @textColor;
-            font-weight: normal;
-          }
-        }
-      }
-
-      .forum-meta-tooltip {
-        .tooltip-inner {
-          max-width: 400px;
-          text-align: left;
-
-          .forum-stats {
-            color: @grayLight;
-            font-size: @fontSizeMini;
-
-            strong {
-              color: @white;
-            }
-
-            span {
-              margin-right: @baseFontSize;
-            }
-          }
-
-          .forum-description {
-            clear: both;
-            margin: 0px;
-            margin-bottom: @baseFontSize / 2;
-            padding: 0px;
-
-            color: @grayLighter;
-            font-size: @baseFontSize;
-          }
-        }
-      }
-    }
-  }
-
-  &.index-category-important {
-    .header {
-      background-color: @red;
-      border: 1px solid darken(@red, 10%);
-
-      h2 {
-        color: @white;
-        text-shadow: 0px 1px 0px darken(@red, 25%);
-
-        small {
-          color: darken(@red, 40%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
-
-  &.index-category-inverse {
-    .header {
-      background-color: @grayDark;
-      border: 1px solid darken(@grayDark, 10%);
-
-      h2 {
-        color: @grayLighter;
-        text-shadow: 0px 1px 0px darken(@black, 25%);
-
-        small {
-          color: lighten(@grayLight, 10%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
-
-  &.index-category-info {
-    .header{
-      background-color: @bluePale;
-      border: 1px solid darken(@bluePale, 10%);
-
-      h2 {
-
-        color: @white;
-        text-shadow: 0px 1px 0px darken(@bluePale, 25%);
-
-        small {
-          color: darken(@bluePale, 25%);
-          text-shadow: none;
-        }
-      }
-    }
-  }
-}
-
-.index-forums-read-all {
-  margin: 0px;
-  padding: 0px;
-
-  .btn-link {
-    margin: 0px;
-    .opacity(50);
-    padding: 0px;
-
-    color: @textColor;
-    font-weight: bold;
-
-    &:active, &:hover {
-      .opacity(90);
-    }
-  }
-}
-
-// Online list
-// -------------------------
-.index-ranks-list {
-  h3 {
-    margin: 0px;
-    padding: 0px;
-
-    color: @grayLight;
-    font-size: @fontSizeLarge;
-    font-weight: bold;
-
-    a:link, a:active, a:visited, a:hover {
-      color: @grayLight;
-      font-size: @fontSizeLarge;
-      text-decoration: none;
-    }
-  }
-
-  ul {
-    background-color: @white;
-    border: 1px solid darken(@bodyBackground, 10%);
-    border-radius: @baseBorderRadius;
-    margin: 0px;
-    margin-bottom: @baseLineHeight;
-    padding: 0px;
-
-    li {
-      border-bottom: 1px dotted darken(@bodyBackground, 10%);
-      margin: 0px;
-      padding: 6px 8px;
-
-      font-weight: bold;
-
-      img {
-        background-color: @white;
-        border-radius: @borderRadiusSmall;
-        width: 28px;
-        height: 28px;
-      }
-
-      .user-name {
-        &:link, &:active, &:visited, &:hover {
-          position: relative;
-          top: (@fontSizeLarge - @baseFontSize) / 2;
-          margin: 0px 4px;
-
-          color: @textColor;
-          font-size: @fontSizeLarge;
-        }
-      }
-
-      .label {
-        float: right;
-        position: relative;
-        top: (@fontSizeLarge - @baseFontSize) + 1px;
-      }
-
-      &:last-child {
-        border-bottom: none;
-      }
-    }
-  }
-}
-
-// Popular threads
-// -------------------------
-.index-popular-threads { 
-  h3 {
-    margin: 0px;
-    margin-bottom: (@baseLineHeight * -0.5);
-    padding: 0px;
-
-    color: @grayLight;
-    font-size: @fontSizeLarge;
-    font-weight: bold;
-  }
-
-  ul {
-    margin: 0px;
-    margin-bottom: @baseLineHeight;
-    padding: 0px;
-
-    li {
-      border-bottom: 1px solid darken(@bodyBackground, 10%);
-      padding: (@baseFontSize / 2) 0px;
-
-      a:link, a:active, a:visited, a:hover {
-        color: @textColor;
-        font-weight: bold;
-      }
-
-      .muted {
-        font-size: @fontSizeMini;
-
-        a:link, a:active, a:visited, a:hover {
-          color: @gray;
-        }
-      }
-    }
-  }
-}
-
-// Forum stats
-// -------------------------
-.index-stats {
-  margin-bottom: @baseLineHeight;
-  .opacity(60);
-  overflow: auto;
-
-  font-weight: bold;
-
-  ul {
-    li {
-      float: left;
-      padding: 0px;
-      padding-right: @baseLineHeight;
-    }
-  }
-}
+//
+// Board index page
+// --------------------------------------------------
+
+.index-sidebar {
+  position: relative;
+  bottom: 9px;
+}
+
+// Forums list
+// -------------------------
+.index-category {
+  background-color: @categoryBackground;
+  border: 1px solid @categoryBorder;
+  border-radius: @borderRadiusSmall;
+  .box-shadow(0px 0px 0px 3px @categoryShadow);
+  margin-bottom: @baseLineHeight;
+
+  .header {
+    background-color: @categoryHeader;
+    border: 1px solid @categoryBorder;
+    border-radius: @borderRadiusSmall @borderRadiusSmall 0px 0px;
+    margin: -1px;
+    margin-bottom: 0px;
+    padding: (@fontSizeSmall / 3) (@fontSizeSmall - 2px);
+
+    h2 {
+      margin: 0px;
+      padding: 0px;
+
+      color: @grayDark;
+      font-size: @fontSizeSmall;
+      font-weight: bold;
+      line-height: @baseLineHeight;
+      text-align: left;
+
+      small {
+        margin-left: @baseFontSize / 2;
+
+        color: @grayLight;
+        font-size: @fontSizeSmall;
+      }
+    }
+  }
+
+  .forum {
+    border-bottom: 1px solid @categoryBorder;
+    height: 21px;
+    overflow: visible;
+    padding: ((@fontSizeLarge / 2) + 6px) (@fontSizeSmall - 2px);
+
+    &.last {
+      border-bottom: none;
+    }
+
+    .forum-icon {
+      float: left;
+
+      .forum-icon-wrap {
+        background-color: @itemOldColor;
+        border: 1px solid darken(@itemOldColor, 10%);
+        border-radius: @baseBorderRadius;
+        padding: ((@forumIconSize - 22px) / 2) ((@forumIconSize - 16px) / 2);
+        position: relative;
+        bottom: (@forumIconSize - @baseLineHeight) / 2;
+
+        &.forum-icon-new {
+          background-color: @itemNewColor;
+          border: 1px solid darken(@itemNewColor, 10%);
+        }
+
+        &.forum-icon-redirect {
+          background-color: @itemMovedColor;
+          border: 1px solid darken(@itemMovedColor, 10%);
+        }
+      }
+    }
+
+    .forum-main {
+      margin-left: @forumIconSize + 10px;
+
+      h3 {
+        float: left;
+        margin: 0px;
+        padding: 0px;
+
+        font-size: @fontSizeLarge;
+        font-weight: normal;
+        line-height: @baseLineHeight;
+
+        a:link, a:visited {
+          color: @textColor;
+        }
+      }
+
+      .dropdown {
+        float: right;
+        right: @baseFontSize;
+
+        .subforum {
+          &:link, &:visited {
+            color: @grayLight;
+            font-weight: bold;
+          }
+
+          &:hover, &:active {
+            color: @textColor;
+          }
+        }
+
+        .dropdown-toggle {
+          padding: 4px 8px;
+          .opacity(60);
+
+          color: @textColor;
+          font-weight: bold;
+
+          &:hover, &:active, &:focus {
+            .opacity(100);
+
+            text-decoration: none;
+          }
+        }
+
+        &.open .dropdown-toggle {
+          background-color: @categoryShadow;
+          border-radius: @baseBorderRadius @baseBorderRadius 0px 0px;
+          .opacity(100);
+          padding-bottom: 6px;
+
+          text-decoration: none;
+        }
+
+        .dropdown-menu {
+          background: none;
+          border: none;
+          box-shadow: none;
+          z-index: 2;
+
+          .dropdown-shadow{
+            border-radius: @baseBorderRadius;
+            .box-shadow(0px 0px 3px @grayLight);
+            width: 256px;
+            position: relative;
+            right: 0px;
+            top: -4px;
+
+            ul {
+              background-color: @bodyBackground;
+              border-radius: @baseBorderRadius;
+              margin: 0px;
+              padding: 0px;
+
+              li {
+                margin: 0px;
+                padding: 0px;
+                list-style: none;
+
+                a {
+                  border-bottom: 1px dotted @categoryBorder;
+                  display: block;
+                  .opacity(70);
+                  padding: 6px 8px;
+
+                  color: @textColor;
+                  text-decoration: none;
+
+                  &:hover, &:active {
+                    .opacity(100);
+                  }
+                }
+
+                &:last-child a {
+                  border-bottom: none;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      .forum-details {
+        border-left: 1px dotted darken(@categoryBackground, 12%);
+        float: right;
+        margin: -10px 0px;
+        margin-top: ((@baseFontSize - @fontSizeSmall) * -1) - 6px;
+        padding-left: @baseFontSize;
+        height: 35px;
+        width: 220px;
+
+        .thread-name {
+          a:link, a:active, a:visited, a:hover {
+            margin-bottom: 1px;
+
+            color: @textColor;
+            font-size: @fontSizeSmall;
+            font-weight: bold;
+          }
+        }
+
+        .muted {
+          font-size: @fontSizeMini;
+          line-height: @fontSizeMini;
+
+          .last-poster, a:link, a:active, a:visited, a:hover {
+            color: @gray;
+          }
+        }
+
+        em {
+          position: relative;
+          top: (35px - @baseLineHeight) / 2;
+
+          color: @grayLight;
+
+          strong {
+            color: @textColor;
+            font-weight: normal;
+          }
+        }
+      }
+
+      .forum-meta-tooltip {
+        .tooltip-inner {
+          max-width: 400px;
+          text-align: left;
+
+          .forum-stats {
+            color: @grayLight;
+            font-size: @fontSizeMini;
+
+            strong {
+              color: @white;
+            }
+
+            span {
+              margin-right: @baseFontSize;
+            }
+          }
+
+          .forum-description {
+            clear: both;
+            margin: 0px;
+            margin-bottom: @baseFontSize / 2;
+            padding: 0px;
+
+            color: @grayLighter;
+            font-size: @baseFontSize;
+          }
+        }
+      }
+    }
+  }
+
+  &.index-category-important {
+    .header {
+      background-color: @red;
+      border: 1px solid darken(@red, 10%);
+
+      h2 {
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@red, 25%);
+
+        small {
+          color: darken(@red, 40%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
+
+  &.index-category-inverse {
+    .header {
+      background-color: @grayDark;
+      border: 1px solid darken(@grayDark, 10%);
+
+      h2 {
+        color: @grayLighter;
+        text-shadow: 0px 1px 0px darken(@black, 25%);
+
+        small {
+          color: lighten(@grayLight, 10%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
+
+  &.index-category-info {
+    .header{
+      background-color: @bluePale;
+      border: 1px solid darken(@bluePale, 10%);
+
+      h2 {
+
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@bluePale, 25%);
+
+        small {
+          color: darken(@bluePale, 25%);
+          text-shadow: none;
+        }
+      }
+    }
+  }
+}
+
+.index-forums-read-all {
+  margin: 0px;
+  padding: 0px;
+
+  .btn-link {
+    margin: 0px;
+    .opacity(50);
+    padding: 0px;
+
+    color: @textColor;
+    font-weight: bold;
+
+    &:active, &:hover {
+      .opacity(90);
+    }
+  }
+}
+
+// Online list
+// -------------------------
+.index-ranks-list {
+  h3 {
+    margin: 0px;
+    padding: 0px;
+
+    color: @grayLight;
+    font-size: @fontSizeLarge;
+    font-weight: bold;
+
+    a:link, a:active, a:visited, a:hover {
+      color: @grayLight;
+      font-size: @fontSizeLarge;
+      text-decoration: none;
+    }
+  }
+
+  ul {
+    background-color: @white;
+    border: 1px solid darken(@bodyBackground, 10%);
+    border-radius: @baseBorderRadius;
+    margin: 0px;
+    margin-bottom: @baseLineHeight;
+    padding: 0px;
+
+    li {
+      border-bottom: 1px dotted darken(@bodyBackground, 10%);
+      margin: 0px;
+      padding: 6px 8px;
+
+      font-weight: bold;
+
+      img {
+        background-color: @white;
+        border-radius: @borderRadiusSmall;
+        width: 28px;
+        height: 28px;
+      }
+
+      .user-name {
+        &:link, &:active, &:visited, &:hover {
+          position: relative;
+          top: (@fontSizeLarge - @baseFontSize) / 2;
+          margin: 0px 4px;
+
+          color: @textColor;
+          font-size: @fontSizeLarge;
+        }
+      }
+
+      .label {
+        float: right;
+        position: relative;
+        top: (@fontSizeLarge - @baseFontSize) + 1px;
+      }
+
+      &:last-child {
+        border-bottom: none;
+      }
+    }
+  }
+}
+
+// Popular threads
+// -------------------------
+.index-popular-threads { 
+  h3 {
+    margin: 0px;
+    margin-bottom: (@baseLineHeight * -0.5);
+    padding: 0px;
+
+    color: @grayLight;
+    font-size: @fontSizeLarge;
+    font-weight: bold;
+  }
+
+  ul {
+    margin: 0px;
+    margin-bottom: @baseLineHeight;
+    padding: 0px;
+
+    li {
+      border-bottom: 1px solid darken(@bodyBackground, 10%);
+      padding: (@baseFontSize / 2) 0px;
+
+      a:link, a:active, a:visited, a:hover {
+        color: @textColor;
+        font-weight: bold;
+      }
+
+      .muted {
+        font-size: @fontSizeMini;
+
+        a:link, a:active, a:visited, a:hover {
+          color: @gray;
+        }
+      }
+    }
+  }
+}
+
+// Forum stats
+// -------------------------
+.index-stats {
+  margin-bottom: @baseLineHeight;
+  .opacity(60);
+  overflow: auto;
+
+  font-weight: bold;
+
+  ul {
+    li {
+      float: left;
+      padding: 0px;
+      padding-right: @baseLineHeight;
+    }
+  }
+}

+ 44 - 44
static/cranefly/css/cranefly/karmas.less

@@ -1,45 +1,45 @@
-// Post Love/Hate Votes List
-// -------------------------
-
-.post-votes-list {
-  .vote-user {
-    &, &:link, &:visited {
-      color: @gray;
-      font-size: @fontSizeLarge;
-      font-weight: bold;
-    }
-
-    .vote-icon {
-      background-color: @grayLight;
-      border-radius: @baseBorderRadius;
-      padding: 2px 3px;
-      position: relative;
-      bottom: (@fontSizeLarge - @baseFontSize) / 2;
-
-      font-size: @baseFontSize;
-
-      i {
-        background-image: url("@{iconWhiteSpritePath}");
-      }
-    }
-  }
-
-  a.vote-user {
-    &:hover, &:active {
-      color: @textColor;
-      text-decoration: none;
-    }
-  }
-
-  .post-likes {
-    .vote-icon {
-      background-color: @green;
-    }
-  }
-
-  .post-dislikes {
-    .vote-icon {
-      background-color: @red;
-    }
-  }
+// Post Love/Hate Votes List
+// -------------------------
+
+.post-votes-list {
+  .vote-user {
+    &, &:link, &:visited {
+      color: @gray;
+      font-size: @fontSizeLarge;
+      font-weight: bold;
+    }
+
+    .vote-icon {
+      background-color: @grayLight;
+      border-radius: @baseBorderRadius;
+      padding: 2px 3px;
+      position: relative;
+      bottom: (@fontSizeLarge - @baseFontSize) / 2;
+
+      font-size: @baseFontSize;
+
+      i {
+        background-image: url("@{iconWhiteSpritePath}");
+      }
+    }
+  }
+
+  a.vote-user {
+    &:hover, &:active {
+      color: @textColor;
+      text-decoration: none;
+    }
+  }
+
+  .post-likes {
+    .vote-icon {
+      background-color: @green;
+    }
+  }
+
+  .post-dislikes {
+    .vote-icon {
+      background-color: @red;
+    }
+  }
 }
 }

+ 156 - 156
static/cranefly/css/cranefly/markdown.less

@@ -1,157 +1,157 @@
-// Markdown output
-// -------------------------
-
-.markdown, .markdown article {
-  margin: 0px;
-  overflow: auto;
-
-  &>:first-child {
-    margin-top: 0px;
-  }
-
-  &>:last-child {
-    margin-bottom: 0px;
-
-    img:last-child {
-      margin-bottom: 0px;
-    }
-  }
-
-  h1 {
-    font-size: @baseFontSize * 1.7;
-  }
-
-  h2 {
-    font-size: @baseFontSize * 1.5;
-  }
-
-  h3 {
-    font-size: @baseFontSize * 1.2;
-  }
-
-  h4 {
-    font-size: @baseFontSize;
-  }
-
-  hr {
-    border: none;
-    border-top: 1px solid @hrBorder;
-    margin: @baseLineHeight 0px;
-  }
-
-  blockquote {
-    border-left-color: darken(@grayLighter, 5%);
-    padding: (@baseFontSize / 3) @baseFontSize;
-
-    header {
-      padding-bottom: (@baseLineHeight / 2);
-
-      font-size: @baseFontSize * 1.1;
-      font-weight: bold;
-      line-height: @baseLineHeight;
-    }
-
-    p {
-      margin: 0 0 (@baseLineHeight / 2);
-
-      font-size: @baseFontSize;
-    }
-
-    blockquote {
-      .opacity(85);
-    }
-  }
-
-  code {
-    background-color: @grayDark;
-    border: none;
-
-    color: @grayLighter;
-    font-size: @baseFontSize;
-  }
-
-  pre {
-    background-color: @grayDarker;
-    padding: (@baseFontSize / 2) @baseFontSize;
-
-    code {
-      background: none;
-      border: none;
-
-      color: @grayLighter;
-      font-size: @fontSizeSmall;
-    }
-  }
-
-  img {
-    background-color: @white;
-    border-radius: @baseBorderRadius;
-    margin: (@baseLineHeight / 2) 0px;
-  }
-
-  // Blocks margins
-  pre, blockquote, iframe {
-    margin-top: @baseLineHeight;
-    margin-bottom: @baseLineHeight;
-
-    &>:first-child {
-      margin-top: 0px;
-    }
-
-    &>:last-child {
-      margin-bottom: 0px;
-    }
-  }
-
-  // Emoticons
-  .emoji {
-    background: none;
-    border-radius: 0px;
-    margin: 0px;
-    vertical-align: middle;
-  }
-
-  h1 {
-    .emoji {
-      width: (@baseFontSize * 1.7) + 4px;
-      height: (@baseFontSize * 1.7) + 4px;
-    }
-  }
-
-  h2 {
-    .emoji {
-      width: (@baseFontSize * 1.5) + 4px;
-      height: (@baseFontSize * 1.5) + 4px;
-    }
-  }
-
-  h3 {
-    .emoji {
-      width: (@baseFontSize * 1.2) + 4px;
-      height: (@baseFontSize * 1.2) + 4px;
-    }
-  }
-
-  h4 {
-    .emoji {
-      width: @baseFontSize + 4px;
-      height: @baseFontSize + 4px;
-    }
-  }
-
-  p {
-    .emoji {
-      width: @baseFontSize + 4px;
-      height: @baseFontSize + 4px;
-    }
-  }
-
-  blockquote {
-    p {
-      .emoji {
-        width: @fontSizeSmall + 2px;
-        height: @fontSizeSmall + 2px;
-      }
-    }
-  }
+// Markdown output
+// -------------------------
+
+.markdown, .markdown article {
+  margin: 0px;
+  overflow: auto;
+
+  &>:first-child {
+    margin-top: 0px;
+  }
+
+  &>:last-child {
+    margin-bottom: 0px;
+
+    img:last-child {
+      margin-bottom: 0px;
+    }
+  }
+
+  h1 {
+    font-size: @baseFontSize * 1.7;
+  }
+
+  h2 {
+    font-size: @baseFontSize * 1.5;
+  }
+
+  h3 {
+    font-size: @baseFontSize * 1.2;
+  }
+
+  h4 {
+    font-size: @baseFontSize;
+  }
+
+  hr {
+    border: none;
+    border-top: 1px solid @hrBorder;
+    margin: @baseLineHeight 0px;
+  }
+
+  blockquote {
+    border-left-color: darken(@grayLighter, 5%);
+    padding: (@baseFontSize / 3) @baseFontSize;
+
+    header {
+      padding-bottom: (@baseLineHeight / 2);
+
+      font-size: @baseFontSize * 1.1;
+      font-weight: bold;
+      line-height: @baseLineHeight;
+    }
+
+    p {
+      margin: 0 0 (@baseLineHeight / 2);
+
+      font-size: @baseFontSize;
+    }
+
+    blockquote {
+      .opacity(85);
+    }
+  }
+
+  code {
+    background-color: @grayDark;
+    border: none;
+
+    color: @grayLighter;
+    font-size: @baseFontSize;
+  }
+
+  pre {
+    background-color: @grayDarker;
+    padding: (@baseFontSize / 2) @baseFontSize;
+
+    code {
+      background: none;
+      border: none;
+
+      color: @grayLighter;
+      font-size: @fontSizeSmall;
+    }
+  }
+
+  img {
+    background-color: @white;
+    border-radius: @baseBorderRadius;
+    margin: (@baseLineHeight / 2) 0px;
+  }
+
+  // Blocks margins
+  pre, blockquote, iframe {
+    margin-top: @baseLineHeight;
+    margin-bottom: @baseLineHeight;
+
+    &>:first-child {
+      margin-top: 0px;
+    }
+
+    &>:last-child {
+      margin-bottom: 0px;
+    }
+  }
+
+  // Emoticons
+  .emoji {
+    background: none;
+    border-radius: 0px;
+    margin: 0px;
+    vertical-align: middle;
+  }
+
+  h1 {
+    .emoji {
+      width: (@baseFontSize * 1.7) + 4px;
+      height: (@baseFontSize * 1.7) + 4px;
+    }
+  }
+
+  h2 {
+    .emoji {
+      width: (@baseFontSize * 1.5) + 4px;
+      height: (@baseFontSize * 1.5) + 4px;
+    }
+  }
+
+  h3 {
+    .emoji {
+      width: (@baseFontSize * 1.2) + 4px;
+      height: (@baseFontSize * 1.2) + 4px;
+    }
+  }
+
+  h4 {
+    .emoji {
+      width: @baseFontSize + 4px;
+      height: @baseFontSize + 4px;
+    }
+  }
+
+  p {
+    .emoji {
+      width: @baseFontSize + 4px;
+      height: @baseFontSize + 4px;
+    }
+  }
+
+  blockquote {
+    p {
+      .emoji {
+        width: @fontSizeSmall + 2px;
+        height: @fontSizeSmall + 2px;
+      }
+    }
+  }
 }
 }

+ 43 - 43
static/cranefly/css/cranefly/messages.less

@@ -1,44 +1,44 @@
-//
-// Board Messages
-// --------------------------------------------------
-
-.messages-list {
-  .alert-icon {
-    float: left;
-  }
-
-  p {
-    margin: 0px;
-    margin-left: 28px;
-
-    color: @white;
-  }
-
-  a:link, a:visited {
-    .opacity(85);
-
-    color: @white;
-    font-size: 120%;
-    text-decoration: underline;
-  }
-
-  a:active, a:hover {
-    .opacity(100);
-  }
-  
-  .alert-info {
-    text-shadow: 0px 1px 0px darken(@infoBorder, 10%);
-  }
-
-  .alert-success {
-    text-shadow: 0px 1px 0px darken(@successBorder, 10%);
-  }
-
-  .alert-warning {
-    text-shadow: 0px 1px 0px darken(@warningBorder, 10%);
-  }
-
-  .alert-error {
-    text-shadow: 0px 1px 0px darken(@errorBorder, 10%);    
-  }
+//
+// Board Messages
+// --------------------------------------------------
+
+.messages-list {
+  .alert-icon {
+    float: left;
+  }
+
+  p {
+    margin: 0px;
+    margin-left: 28px;
+
+    color: @white;
+  }
+
+  a:link, a:visited {
+    .opacity(85);
+
+    color: @white;
+    font-size: 120%;
+    text-decoration: underline;
+  }
+
+  a:active, a:hover {
+    .opacity(100);
+  }
+  
+  .alert-info {
+    text-shadow: 0px 1px 0px darken(@infoBorder, 10%);
+  }
+
+  .alert-success {
+    text-shadow: 0px 1px 0px darken(@successBorder, 10%);
+  }
+
+  .alert-warning {
+    text-shadow: 0px 1px 0px darken(@warningBorder, 10%);
+  }
+
+  .alert-error {
+    text-shadow: 0px 1px 0px darken(@errorBorder, 10%);    
+  }
 }
 }

+ 495 - 495
static/cranefly/css/cranefly/navbar.less

@@ -1,496 +1,496 @@
-// Navbar
-// -------------------------
-.navbar-header {
-  .navbar-inner {
-    background: none;
-    background-color: @navbarBackground;
-    border-bottom: 1px solid @navbarBorder;
-    .box-shadow(none);
-
-    .container {
-      background: url(@navbarLogoImage);
-      background-position: left center;
-      background-repeat: no-repeat;
-    }
-
-    .brand {
-      margin-left: @navbarLogoWidth - @baseFontSize;
-
-      text-shadow: none;
-
-      &:link, &:active, &:visited, &:hover {
-        color: @navbarBrandColor;
-        font-size: 200%;
-
-        span {
-          color: @navbarMottoColor;
-        } 
-      }
-    }
-
-    .navbar-search-form {
-      height: 30px;
-      width: @navbarSearchWidth + @baseFontSize;
-      margin-top: ((@navbarHeight - @baseLineHeight) / 2) - 6px;
-      overflow: visible;
-      position: relative;
-
-      .navbar-search-border {
-        background-color: @white;
-        border: 1px solid darken(@navbarBorder, 10%);
-        .border-radius(@baseBorderRadius);
-        position: absolute;
-        z-index: @zindexFixedNavbar;
-
-        .navbar-search-text {
-          border: 1px solid darken(@navbarBorder, 10%);
-          border-radius: @baseBorderRadius;
-          margin: -1px;
-          padding: 1px 0px;
-
-          color: @textColor;
-
-          &>input {
-            border: none;
-            .box-shadow(none);
-            background: none;
-            margin: 0px;
-            width: @navbarSearchWidth;
-          }
-
-          .icon-search {
-            float: right;
-            .opacity(50);
-            margin-bottom: (@baseLineHeight * -1);
-            margin-right: 8px;
-            position: relative;
-            bottom: (@baseLineHeight * -1) + @baseFontSize;
-          }
-        }
-
-        &:hover, &.open {
-          .box-shadow(0px 0px 3px fadeout(@gray, 30%));
-
-          .navbar-search-text {
-            border-color: lighten(@blue, 20%);
-            margin-bottom: 0px;
-          }
-
-          .extra {
-            display: block;
-          }
-        }
-
-        &>.extra {
-          background: @white;
-          border-radius: @baseBorderRadius;
-          display: none;
-
-          .extra-form {
-            padding: 0px @navbarSearchPadding;
-
-            .control {
-              margin: (@navbarSearchPadding * 1.5) 0px;
-
-              label {
-                color: @gray;
-                font-weight: bold;
-              }
-
-              select {
-                margin: 0px;
-                width: 100%;
-              }
-
-              input[type="text"] {
-                margin: 0px;
-                width: (@navbarSearchWidth - (@navbarSearchPadding * 2) - 2px);
-              }
-
-              .checkbox {
-                margin-bottom: -3px;
-              }
-            }
-          }
-
-          .form-actions {
-            border-radius: 0px 0px (@baseBorderRadius - 1);
-            margin: 0px;
-            margin-top: (@baseFontSize / 2);
-            margin-bottom: 0px;
-            padding: @navbarSearchPadding;
-
-            .btn {
-              margin: 0px;
-            }
-
-            a:link, a:visited {
-              margin-left: (@baseFontSize / 2);
-              position: relative;
-              top: 1px;
-
-              color: @grayLight;
-              font-weight: bold;
-            }
-
-            a:active, a:hover {
-              color: @textColor;
-            }
-          }
-        }
-      }
-    }
-
-    .navbar-blocks {
-      margin-left: 6px;
-
-      li {
-        margin-left: 6px;
-
-        form {
-          margin: 0px;
-          padding: 0px;
-        }
-
-        a:link, a:visited, .btn-link {
-          background-color: lighten(@navbarBackground, 2%);
-          border: 1px solid darken(@navbarBorder, 2%);
-          border-radius: @baseBorderRadius;
-          padding: 5px 8px;
-          margin-top: ((@navbarHeight - @baseLineHeight) / 2) - 6;
-
-          i {
-            .opacity(70);
-          }
-
-          .label {
-            background-color: @red;
-            margin-left: 4px;
-            padding-left: 6px;
-            padding-right: 5px;
-            position: relative;
-            bottom: 1px;
-          }
-        }
-
-        a:hover, a:active, .btn-link:hover, .btn-link:active {
-          background-color: @linkColor;
-          border-color: darken(@linkColor, 10%);
-
-          &.danger {
-            background-color: @red;
-            border-color: darken(@red, 10%);
-          }
-
-          &.hot {
-            background-color: @orange;
-            border-color: darken(@orange, 10%);
-          }
-
-          &.fresh {
-            background-color: @green;
-            border-color: darken(@green, 10%);
-          }
-
-          i {
-            background-image: url("@{iconWhiteSpritePath}");
-            .opacity(100);
-          }
-
-          .label {
-            background-color: @grayLighter;
-
-            color: @textColor;
-          }
-        }
-
-        &.user-profile {
-          a:link, a:visited, a:hover, a:active {
-            background: none;
-            border: none;
-            margin-right: 8px;
-            margin-top: 5px;
-
-            font-weight: bold;
-            text-shadow: none;
-
-            img {
-              border-radius: @baseBorderRadius;
-              margin-right: 6px;
-              width: 32px;
-              height: 32px;
-              position: relative;
-              bottom: 1px;
-            }
-          }
-        }
-      }
-    }
-
-    .navbar-compact {
-      display: none;
-      
-      li {
-        &.user-profile {
-          &>a {
-            &:link, &:visited {
-              .border-radius(0);
-              margin-top: 0px;
-              padding: @baseFontSize;
-              padding-top: 10px;
-              padding-bottom: 8px;
-
-              img {
-                margin-right: 0px;
-                margin-left: 6px;
-              }
-
-              .caret-border {
-                background-color: @bodyBackground;
-                border: 1px solid @navbarBorder;
-                .border-radius(@baseBorderRadius);
-                margin-left: 8px;
-                padding: 0px 4px;
-
-                .caret {
-                  margin: 0px;
-                  padding: 0px;
-                  position: relative;
-                  top: 13px;
-                }
-              }
-            }
-
-            &:hover, &:active {
-              background-color: @bodyBackground;
-
-              .caret-border {
-                border-color: @grayLight;
-              }
-            }
-          }
-
-          &.open .dropdown-toggle {
-            &:link, &:visited, &:hover, &:focus {
-              background-color: @bodyBackground;
-
-              .caret-border {
-                background: @red;
-                border-color: @red;
-
-                .caret {
-                  border-top-color: @white;
-                }
-              }
-            }
-          }
-
-          .dropdown-menu {
-            border: none;
-            border: 1px solid darken(@navbarBorder, 10%);
-            .border-radius(@baseBorderRadius);
-            .box-shadow(0px 4px 3px fadeout(@gray, 30%));
-            margin: 0px;
-            margin-top: -6px;
-            margin-right: 0px;
-            padding: 4px 0px;
-            width: 270px;
-
-            &:before {
-              margin-right: 11px;
-            }
-
-            &:after {
-              border-bottom: 6px solid @white;
-              margin-top: 0px;
-              margin-right: 11px;
-            }
-
-            &>li {
-              margin: 0px;
-              padding: 0px;
-
-              .label {
-                float: right;
-                margin: 0px;
-                margin-top: 2px;
-              }
-
-              a, .btn-link {
-                .border-radius(0);
-                clear: none;
-                display: block;
-                float: none;
-                margin: 0px;
-                padding: 6px 12px;
-
-                color: @textColor;
-                font-weight: normal;
-                text-align: left;
-                
-                i {
-                  .opacity(100);
-                }
-
-                &:link, &:active, &:visited, &:hover {
-                  .label {
-                    background-color: @red;
-                    float: right;
-
-                    color: @white;
-                    text-shadow: 0px 1px 0px darken(@red, 20%);
-                  }
-                }
-              }
-
-              a:link, a:visited {
-                .opacity(80);
-              }
-
-              a:hover {
-                background-color: @gray;
-                .opacity(100);
-
-                color: @white;
-              }
-
-              .btn-link {
-                background: none;
-                border: none;
-                width: 100%;
-
-                &:hover, &:active {
-                  background: @red;
-                  .opacity(100);
-
-                  color: @white;
-                  text-shadow: 0px 1px 0px darken(@red, 20%);
-                }
-
-                i {
-                  position: relative;
-                  top: 0px;
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-
-    .navbar-user-nav {
-      li {
-        .btn {
-          padding: 2px 8px;
-          margin-left: 8px;
-          margin-top: (@baseLineHeight / 2) + 3;
-
-          &:link, &:active, &:hover, &:visited {
-            color: @white;
-          }
-
-          &.btn-danger {
-            text-shadow: 0px 1px 0px darken(@red, 15%);
-
-            &:hover, &:active, &:focus {
-              background-color: saturate(@red, 20%);
-              border-color: darken(saturate(@red, 20%), 5%);
-            }
-          }
-
-          &.btn-inverse {
-            text-shadow: 0px 1px 0px darken(@textColor, 15%);
-
-            &:hover, &:active, &:focus {
-              background-color: darken(@textColor, 5%);
-              border-color: darken(@textColor, 10%);
-            }
-          }
-        }
-      }
-    }
-  }
-}
-
-// Inversed navbar, used for moderation tools
-.navbar-modbar {
-  .navbar-inner {
-    background: none;
-    background-color: @grayDarker;
-    background-image: -webkit-gradient(linear, 0 0, 100% 100%,
-                color-stop(.25, rgba(0, 0, 0, 0.1)), color-stop(.25, transparent),
-                color-stop(.5, transparent), color-stop(.5, rgba(0, 0, 0, 0.1)),
-                color-stop(.75, rgba(0, 0, 0, 0.1)), color-stop(.75, transparent),
-                to(transparent));
-    background-image: -webkit-linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
-              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
-              transparent 75%, transparent);
-    background-image: -moz-linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
-              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
-              transparent 75%, transparent);
-    background-image: -ms-linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
-              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
-              transparent 75%, transparent);
-    background-image: -o-linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
-              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
-              transparent 75%, transparent);
-    background-image: linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
-              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
-              transparent 75%, transparent);
-    -webkit-background-size: 15px 15px;
-    -moz-background-size: 15px 15px;
-    background-size: 15px 15px;
-    border-bottom: 2px dashed @red;
-    min-height: 1px;
-
-    .nav {
-      a:link, a:visited {
-        padding: @fontSizeMini (@fontSizeMini * 1.4);
-
-        color: darken(@white, 30%);
-        text-shadow: 0px 1px 0px @black;
-      }
-
-      a:hover, a:active {
-        color: @white;
-      }
-
-      li.active {
-        a:link, a:visited {
-          color: @white;
-        }
-      }
-    }
-
-    .navbar-search-form {
-      background-color: darken(@grayDark, 15%);
-      border-radius: @baseBorderRadius;
-      margin-top: ((@navbarHeight - @baseLineHeight) / 2) - 9px;
-      padding: 1px 0px;
-
-      input {
-        background-color: darken(@grayDark, 15%);
-        border: none;
-        .box-shadow(none);
-        margin: 0px;
-
-        color: @grayLighter;
-        text-shadow: 0px 1px 0px @black;
-      }
-
-      button {
-        margin: 0px;
-        .opacity(60);
-
-        i {
-          background-image: url("@{iconWhiteSpritePath}");
-          .opacity(100);
-        }
-
-        &:hover, &:active {
-          .opacity(100);
-        }
-      }
-    }
-  }
+// Navbar
+// -------------------------
+.navbar-header {
+  .navbar-inner {
+    background: none;
+    background-color: @navbarBackground;
+    border-bottom: 1px solid @navbarBorder;
+    .box-shadow(none);
+
+    .container {
+      background: url(@navbarLogoImage);
+      background-position: left center;
+      background-repeat: no-repeat;
+    }
+
+    .brand {
+      margin-left: @navbarLogoWidth - @baseFontSize;
+
+      text-shadow: none;
+
+      &:link, &:active, &:visited, &:hover {
+        color: @navbarBrandColor;
+        font-size: 200%;
+
+        span {
+          color: @navbarMottoColor;
+        } 
+      }
+    }
+
+    .navbar-search-form {
+      height: 30px;
+      width: @navbarSearchWidth + @baseFontSize;
+      margin-top: ((@navbarHeight - @baseLineHeight) / 2) - 6px;
+      overflow: visible;
+      position: relative;
+
+      .navbar-search-border {
+        background-color: @white;
+        border: 1px solid darken(@navbarBorder, 10%);
+        .border-radius(@baseBorderRadius);
+        position: absolute;
+        z-index: @zindexFixedNavbar;
+
+        .navbar-search-text {
+          border: 1px solid darken(@navbarBorder, 10%);
+          border-radius: @baseBorderRadius;
+          margin: -1px;
+          padding: 1px 0px;
+
+          color: @textColor;
+
+          &>input {
+            border: none;
+            .box-shadow(none);
+            background: none;
+            margin: 0px;
+            width: @navbarSearchWidth;
+          }
+
+          .icon-search {
+            float: right;
+            .opacity(50);
+            margin-bottom: (@baseLineHeight * -1);
+            margin-right: 8px;
+            position: relative;
+            bottom: (@baseLineHeight * -1) + @baseFontSize;
+          }
+        }
+
+        &:hover, &.open {
+          .box-shadow(0px 0px 3px fadeout(@gray, 30%));
+
+          .navbar-search-text {
+            border-color: lighten(@blue, 20%);
+            margin-bottom: 0px;
+          }
+
+          .extra {
+            display: block;
+          }
+        }
+
+        &>.extra {
+          background: @white;
+          border-radius: @baseBorderRadius;
+          display: none;
+
+          .extra-form {
+            padding: 0px @navbarSearchPadding;
+
+            .control {
+              margin: (@navbarSearchPadding * 1.5) 0px;
+
+              label {
+                color: @gray;
+                font-weight: bold;
+              }
+
+              select {
+                margin: 0px;
+                width: 100%;
+              }
+
+              input[type="text"] {
+                margin: 0px;
+                width: (@navbarSearchWidth - (@navbarSearchPadding * 2) - 2px);
+              }
+
+              .checkbox {
+                margin-bottom: -3px;
+              }
+            }
+          }
+
+          .form-actions {
+            border-radius: 0px 0px (@baseBorderRadius - 1);
+            margin: 0px;
+            margin-top: (@baseFontSize / 2);
+            margin-bottom: 0px;
+            padding: @navbarSearchPadding;
+
+            .btn {
+              margin: 0px;
+            }
+
+            a:link, a:visited {
+              margin-left: (@baseFontSize / 2);
+              position: relative;
+              top: 1px;
+
+              color: @grayLight;
+              font-weight: bold;
+            }
+
+            a:active, a:hover {
+              color: @textColor;
+            }
+          }
+        }
+      }
+    }
+
+    .navbar-blocks {
+      margin-left: 6px;
+
+      li {
+        margin-left: 6px;
+
+        form {
+          margin: 0px;
+          padding: 0px;
+        }
+
+        a:link, a:visited, .btn-link {
+          background-color: lighten(@navbarBackground, 2%);
+          border: 1px solid darken(@navbarBorder, 2%);
+          border-radius: @baseBorderRadius;
+          padding: 5px 8px;
+          margin-top: ((@navbarHeight - @baseLineHeight) / 2) - 6;
+
+          i {
+            .opacity(70);
+          }
+
+          .label {
+            background-color: @red;
+            margin-left: 4px;
+            padding-left: 6px;
+            padding-right: 5px;
+            position: relative;
+            bottom: 1px;
+          }
+        }
+
+        a:hover, a:active, .btn-link:hover, .btn-link:active {
+          background-color: @linkColor;
+          border-color: darken(@linkColor, 10%);
+
+          &.danger {
+            background-color: @red;
+            border-color: darken(@red, 10%);
+          }
+
+          &.hot {
+            background-color: @orange;
+            border-color: darken(@orange, 10%);
+          }
+
+          &.fresh {
+            background-color: @green;
+            border-color: darken(@green, 10%);
+          }
+
+          i {
+            background-image: url("@{iconWhiteSpritePath}");
+            .opacity(100);
+          }
+
+          .label {
+            background-color: @grayLighter;
+
+            color: @textColor;
+          }
+        }
+
+        &.user-profile {
+          a:link, a:visited, a:hover, a:active {
+            background: none;
+            border: none;
+            margin-right: 8px;
+            margin-top: 5px;
+
+            font-weight: bold;
+            text-shadow: none;
+
+            img {
+              border-radius: @baseBorderRadius;
+              margin-right: 6px;
+              width: 32px;
+              height: 32px;
+              position: relative;
+              bottom: 1px;
+            }
+          }
+        }
+      }
+    }
+
+    .navbar-compact {
+      display: none;
+      
+      li {
+        &.user-profile {
+          &>a {
+            &:link, &:visited {
+              .border-radius(0);
+              margin-top: 0px;
+              padding: @baseFontSize;
+              padding-top: 10px;
+              padding-bottom: 8px;
+
+              img {
+                margin-right: 0px;
+                margin-left: 6px;
+              }
+
+              .caret-border {
+                background-color: @bodyBackground;
+                border: 1px solid @navbarBorder;
+                .border-radius(@baseBorderRadius);
+                margin-left: 8px;
+                padding: 0px 4px;
+
+                .caret {
+                  margin: 0px;
+                  padding: 0px;
+                  position: relative;
+                  top: 13px;
+                }
+              }
+            }
+
+            &:hover, &:active {
+              background-color: @bodyBackground;
+
+              .caret-border {
+                border-color: @grayLight;
+              }
+            }
+          }
+
+          &.open .dropdown-toggle {
+            &:link, &:visited, &:hover, &:focus {
+              background-color: @bodyBackground;
+
+              .caret-border {
+                background: @red;
+                border-color: @red;
+
+                .caret {
+                  border-top-color: @white;
+                }
+              }
+            }
+          }
+
+          .dropdown-menu {
+            border: none;
+            border: 1px solid darken(@navbarBorder, 10%);
+            .border-radius(@baseBorderRadius);
+            .box-shadow(0px 4px 3px fadeout(@gray, 30%));
+            margin: 0px;
+            margin-top: -6px;
+            margin-right: 0px;
+            padding: 4px 0px;
+            width: 270px;
+
+            &:before {
+              margin-right: 11px;
+            }
+
+            &:after {
+              border-bottom: 6px solid @white;
+              margin-top: 0px;
+              margin-right: 11px;
+            }
+
+            &>li {
+              margin: 0px;
+              padding: 0px;
+
+              .label {
+                float: right;
+                margin: 0px;
+                margin-top: 2px;
+              }
+
+              a, .btn-link {
+                .border-radius(0);
+                clear: none;
+                display: block;
+                float: none;
+                margin: 0px;
+                padding: 6px 12px;
+
+                color: @textColor;
+                font-weight: normal;
+                text-align: left;
+                
+                i {
+                  .opacity(100);
+                }
+
+                &:link, &:active, &:visited, &:hover {
+                  .label {
+                    background-color: @red;
+                    float: right;
+
+                    color: @white;
+                    text-shadow: 0px 1px 0px darken(@red, 20%);
+                  }
+                }
+              }
+
+              a:link, a:visited {
+                .opacity(80);
+              }
+
+              a:hover {
+                background-color: @gray;
+                .opacity(100);
+
+                color: @white;
+              }
+
+              .btn-link {
+                background: none;
+                border: none;
+                width: 100%;
+
+                &:hover, &:active {
+                  background: @red;
+                  .opacity(100);
+
+                  color: @white;
+                  text-shadow: 0px 1px 0px darken(@red, 20%);
+                }
+
+                i {
+                  position: relative;
+                  top: 0px;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .navbar-user-nav {
+      li {
+        .btn {
+          padding: 2px 8px;
+          margin-left: 8px;
+          margin-top: (@baseLineHeight / 2) + 3;
+
+          &:link, &:active, &:hover, &:visited {
+            color: @white;
+          }
+
+          &.btn-danger {
+            text-shadow: 0px 1px 0px darken(@red, 15%);
+
+            &:hover, &:active, &:focus {
+              background-color: saturate(@red, 20%);
+              border-color: darken(saturate(@red, 20%), 5%);
+            }
+          }
+
+          &.btn-inverse {
+            text-shadow: 0px 1px 0px darken(@textColor, 15%);
+
+            &:hover, &:active, &:focus {
+              background-color: darken(@textColor, 5%);
+              border-color: darken(@textColor, 10%);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+// Inversed navbar, used for moderation tools
+.navbar-modbar {
+  .navbar-inner {
+    background: none;
+    background-color: @grayDarker;
+    background-image: -webkit-gradient(linear, 0 0, 100% 100%,
+                color-stop(.25, rgba(0, 0, 0, 0.1)), color-stop(.25, transparent),
+                color-stop(.5, transparent), color-stop(.5, rgba(0, 0, 0, 0.1)),
+                color-stop(.75, rgba(0, 0, 0, 0.1)), color-stop(.75, transparent),
+                to(transparent));
+    background-image: -webkit-linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
+              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
+              transparent 75%, transparent);
+    background-image: -moz-linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
+              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
+              transparent 75%, transparent);
+    background-image: -ms-linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
+              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
+              transparent 75%, transparent);
+    background-image: -o-linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
+              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
+              transparent 75%, transparent);
+    background-image: linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%,
+              transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%,
+              transparent 75%, transparent);
+    -webkit-background-size: 15px 15px;
+    -moz-background-size: 15px 15px;
+    background-size: 15px 15px;
+    border-bottom: 2px dashed @red;
+    min-height: 1px;
+
+    .nav {
+      a:link, a:visited {
+        padding: @fontSizeMini (@fontSizeMini * 1.4);
+
+        color: darken(@white, 30%);
+        text-shadow: 0px 1px 0px @black;
+      }
+
+      a:hover, a:active {
+        color: @white;
+      }
+
+      li.active {
+        a:link, a:visited {
+          color: @white;
+        }
+      }
+    }
+
+    .navbar-search-form {
+      background-color: darken(@grayDark, 15%);
+      border-radius: @baseBorderRadius;
+      margin-top: ((@navbarHeight - @baseLineHeight) / 2) - 9px;
+      padding: 1px 0px;
+
+      input {
+        background-color: darken(@grayDark, 15%);
+        border: none;
+        .box-shadow(none);
+        margin: 0px;
+
+        color: @grayLighter;
+        text-shadow: 0px 1px 0px @black;
+      }
+
+      button {
+        margin: 0px;
+        .opacity(60);
+
+        i {
+          background-image: url("@{iconWhiteSpritePath}");
+          .opacity(100);
+        }
+
+        &:hover, &:active {
+          .opacity(100);
+        }
+      }
+    }
+  }
 }
 }

+ 46 - 46
static/cranefly/css/cranefly/newsfeed.less

@@ -1,47 +1,47 @@
-// News feed
-// -------------------------
-
-.news-feed {
-  .media {
-    overflow: auto;
-
-    .media-object {
-      border-radius: @baseBorderRadius;
-      width: 52px;
-      height: 52px;
-    }
-
-    .media-body {
-      margin-left: 52px + @baseFontSize;
-
-      .post-preview {
-        &:link, &:active, &:visited, &:hover {
-          display: block;
-          margin-top: @baseFontSize / 2;
-
-          color: @textColor;
-          font-size: @baseFontSize * 1.2;
-          text-decoration: none;
-        }
-      }
-
-      .media-footer {
-        margin: 0px;
-
-        color: @grayLight;
-        font-size: @fontSizeMini;
-        font-weight: normal;
-
-        a {
-          color: @gray;
-        }
-      }
-    }
-  }
-
-  hr {
-    border: none;
-    border-top: 1px solid @grayLighter;
-    margin: @baseLineHeight 0px;
-  }
+// News feed
+// -------------------------
+
+.news-feed {
+  .media {
+    overflow: auto;
+
+    .media-object {
+      border-radius: @baseBorderRadius;
+      width: 52px;
+      height: 52px;
+    }
+
+    .media-body {
+      margin-left: 52px + @baseFontSize;
+
+      .post-preview {
+        &:link, &:active, &:visited, &:hover {
+          display: block;
+          margin-top: @baseFontSize / 2;
+
+          color: @textColor;
+          font-size: @baseFontSize * 1.2;
+          text-decoration: none;
+        }
+      }
+
+      .media-footer {
+        margin: 0px;
+
+        color: @grayLight;
+        font-size: @fontSizeMini;
+        font-weight: normal;
+
+        a {
+          color: @gray;
+        }
+      }
+    }
+  }
+
+  hr {
+    border: none;
+    border-top: 1px solid @grayLighter;
+    margin: @baseLineHeight 0px;
+  }
 }
 }

+ 46 - 46
static/cranefly/css/cranefly/pagination.less

@@ -1,47 +1,47 @@
-// Pagination
-// -------------------------
-.pagination {
-  margin: 0px;
-  padding: 0px;
-  
-  .count {
-    margin-right: @baseFontSize * 0.75;
-    padding: 4px 0px;
-
-    color: @grayLight;
-  }
-
-  ul {
-    .box-shadow(none);
-
-    li {
-      float: left;
-      margin: 0px;
-      margin-right: @baseFontSize / 3;
-      padding: 0px;
-
-      a:link, a:visited {
-        background-color: @paginationBackground;
-        border: 1px solid @paginationBorder;
-        border-radius: @baseBorderRadius;
-        padding: 3px 7px;
-
-        color: @grayLight;
-
-        i {
-          .opacity(55);
-        }
-      }
-
-      a:active, a:hover {
-        border: 1px solid @paginationActiveBackground;
-
-        color: @gray;
-
-        i {
-          .opacity(100);
-        }
-      }
-    }
-  }
+// Pagination
+// -------------------------
+.pagination {
+  margin: 0px;
+  padding: 0px;
+  
+  .count {
+    margin-right: @baseFontSize * 0.75;
+    padding: 4px 0px;
+
+    color: @grayLight;
+  }
+
+  ul {
+    .box-shadow(none);
+
+    li {
+      float: left;
+      margin: 0px;
+      margin-right: @baseFontSize / 3;
+      padding: 0px;
+
+      a:link, a:visited {
+        background-color: @paginationBackground;
+        border: 1px solid @paginationBorder;
+        border-radius: @baseBorderRadius;
+        padding: 3px 7px;
+
+        color: @grayLight;
+
+        i {
+          .opacity(55);
+        }
+      }
+
+      a:active, a:hover {
+        border: 1px solid @paginationActiveBackground;
+
+        color: @gray;
+
+        i {
+          .opacity(100);
+        }
+      }
+    }
+  }
 }
 }

+ 162 - 162
static/cranefly/css/cranefly/profiles.less

@@ -1,163 +1,163 @@
-// User Profiles
-// -------------------------
-
-.profiles-list {
-  .user-cell {
-    overflow: auto;
-
-    .user-avatar {
-      float: left;
-
-      img {
-        border-radius: @baseBorderRadius;
-        width: 36px;
-        height: 36px; 
-      }
-    }
-
-    .user-name {
-      &:link, &:active, &:visited, &:hover {
-        display: block;
-        margin: 0px;
-        margin-left: 36px + (@baseFontSize / 2);
-        margin-top: (36px - (@baseFontSize * 1.7)) / 2 + 3px;
-
-        color: @textColor;
-        font-size: @baseFontSize * 1.7;
-      }
-    }
-  }
-}
-
-.user-profile {
-  .user-profile-header {
-    .header-avatar {
-      border-radius: @baseBorderRadius;
-      float: left;
-      width: 80px;
-      height: 80px;
-      margin-top: -8px;
-    }
-
-    .header-side {
-      margin-left: 80px + @baseFontSize;
-
-      h1 {
-        margin-left: (@baseLineHeight / 3);
-      }
-    }
-  }
-
-  hr {
-    border: none;
-    border-top: 1px solid @grayLighter;
-    margin: @baseFontSize 0px;
-  }
-
-  .stat-header {
-    border-bottom: 1px solid darken(@bodyBackground, 10%);
-    margin-bottom: @baseFontSize;
-
-    .user-graph {
-      height: 40px;
-
-      svg {
-        position: relative;
-        left: 1px;
-        height: 100%;
-        width: 100%;
-      }
-    }
-
-    .help-text {
-      float: right;
-      margin-bottom: @baseLineHeight * -1;
-      position: relative;
-      top: 40px + @fontSizeSmall - 4px;
-
-      color: @grayLight;
-      font-size: @fontSizeSmall;
-    }
-
-    h2 {
-      margin-top: ((40px + @baseFontSize) * -1);
-      position: relative;
-      top: 4px;
-
-      text-shadow: 0px 0px 3px @bodyBackground;
-    }
-  }
-
-  .user-details {
-    .label {
-      font-size: @baseFontSize;
-    }
-  }
-
-  .user-list {
-    .user-cell {
-      overflow: auto;
-
-      .user-avatar {
-        float: left;
-
-        img {
-          border-radius: @baseBorderRadius;
-          width: 36px;
-          height: 36px; 
-        }
-      }
-
-      .user-name {
-        &:link, &:active, &:visited, &:hover {
-          display: block;
-          margin: 0px;
-          margin-left: 36px + (@baseFontSize / 2);
-          margin-top: (36px - (@baseFontSize * 1.7)) / 2 + 3px;
-
-          color: @textColor;
-          font-size: @baseFontSize * 1.7;
-        }
-      }
-    }
-  }
-
-  .content-list {
-    .media {
-      overflow: auto;
-
-      .media-object {
-        border-radius: @baseBorderRadius;
-        width: 52px;
-        height: 52px;
-      }
-
-      .media-body {
-        margin-left: 52px + @baseFontSize;
-
-        .post-preview {
-          &:link, &:active, &:visited, &:hover {
-            display: block;
-            margin-top: @baseFontSize / 2;
-
-            color: @textColor;
-            font-size: @baseFontSize * 1.2;
-            text-decoration: none;
-          }
-        }
-
-        .media-footer {
-          margin: 0px;
-
-          color: @grayLight;
-          font-size: @fontSizeMini;
-          font-weight: normal;
-
-          a {
-            color: @gray;
-          }
-        }
-      }
-    }
-  }
+// User Profiles
+// -------------------------
+
+.profiles-list {
+  .user-cell {
+    overflow: auto;
+
+    .user-avatar {
+      float: left;
+
+      img {
+        border-radius: @baseBorderRadius;
+        width: 36px;
+        height: 36px; 
+      }
+    }
+
+    .user-name {
+      &:link, &:active, &:visited, &:hover {
+        display: block;
+        margin: 0px;
+        margin-left: 36px + (@baseFontSize / 2);
+        margin-top: (36px - (@baseFontSize * 1.7)) / 2 + 3px;
+
+        color: @textColor;
+        font-size: @baseFontSize * 1.7;
+      }
+    }
+  }
+}
+
+.user-profile {
+  .user-profile-header {
+    .header-avatar {
+      border-radius: @baseBorderRadius;
+      float: left;
+      width: 80px;
+      height: 80px;
+      margin-top: -8px;
+    }
+
+    .header-side {
+      margin-left: 80px + @baseFontSize;
+
+      h1 {
+        margin-left: (@baseLineHeight / 3);
+      }
+    }
+  }
+
+  hr {
+    border: none;
+    border-top: 1px solid @grayLighter;
+    margin: @baseFontSize 0px;
+  }
+
+  .stat-header {
+    border-bottom: 1px solid darken(@bodyBackground, 10%);
+    margin-bottom: @baseFontSize;
+
+    .user-graph {
+      height: 40px;
+
+      svg {
+        position: relative;
+        left: 1px;
+        height: 100%;
+        width: 100%;
+      }
+    }
+
+    .help-text {
+      float: right;
+      margin-bottom: @baseLineHeight * -1;
+      position: relative;
+      top: 40px + @fontSizeSmall - 4px;
+
+      color: @grayLight;
+      font-size: @fontSizeSmall;
+    }
+
+    h2 {
+      margin-top: ((40px + @baseFontSize) * -1);
+      position: relative;
+      top: 4px;
+
+      text-shadow: 0px 0px 3px @bodyBackground;
+    }
+  }
+
+  .user-details {
+    .label {
+      font-size: @baseFontSize;
+    }
+  }
+
+  .user-list {
+    .user-cell {
+      overflow: auto;
+
+      .user-avatar {
+        float: left;
+
+        img {
+          border-radius: @baseBorderRadius;
+          width: 36px;
+          height: 36px; 
+        }
+      }
+
+      .user-name {
+        &:link, &:active, &:visited, &:hover {
+          display: block;
+          margin: 0px;
+          margin-left: 36px + (@baseFontSize / 2);
+          margin-top: (36px - (@baseFontSize * 1.7)) / 2 + 3px;
+
+          color: @textColor;
+          font-size: @baseFontSize * 1.7;
+        }
+      }
+    }
+  }
+
+  .content-list {
+    .media {
+      overflow: auto;
+
+      .media-object {
+        border-radius: @baseBorderRadius;
+        width: 52px;
+        height: 52px;
+      }
+
+      .media-body {
+        margin-left: 52px + @baseFontSize;
+
+        .post-preview {
+          &:link, &:active, &:visited, &:hover {
+            display: block;
+            margin-top: @baseFontSize / 2;
+
+            color: @textColor;
+            font-size: @baseFontSize * 1.2;
+            text-decoration: none;
+          }
+        }
+
+        .media-footer {
+          margin: 0px;
+
+          color: @grayLight;
+          font-size: @fontSizeMini;
+          font-weight: normal;
+
+          a {
+            color: @gray;
+          }
+        }
+      }
+    }
+  }
 }
 }

+ 78 - 78
static/cranefly/css/cranefly/report.less

@@ -1,79 +1,79 @@
-// Report view
-// ------------------------
-
-.report-view {
-  .report-wrapper {
-    background-color: @grayLighter;
-    background-image: -webkit-gradient(linear, 0 0, 100% 100%,
-                color-stop(.25, rgba(255, 255, 255, .2)), color-stop(.25, transparent),
-                color-stop(.5, transparent), color-stop(.5, rgba(255, 255, 255, .2)),
-                color-stop(.75, rgba(255, 255, 255, .2)), color-stop(.75, transparent),
-                to(transparent));
-    background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-              transparent 75%, transparent);
-    background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-              transparent 75%, transparent);
-    background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-              transparent 75%, transparent);
-    background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-              transparent 75%, transparent);
-    background-image: linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-              transparent 75%, transparent);
-    -webkit-background-size: 10px 10px;
-    -moz-background-size: 10px 10px;
-    background-size: 10px 10px;
-    .border-radius(6px);
-    padding: 8px;
-    margin-bottom: 8px;
-
-    .post-body {
-      margin-bottom: 0px;
-
-      .report-actions {
-        border-left: none !important;
-        float: left !important;
-        padding: 0px !important;
-
-        form:first-child .btn {
-          .border-radius(0px 0px 0px @baseBorderRadius) !important;
-        }
-
-        .btn {
-          .opacity(90) !important;
-          margin: 0px !important;
-          padding: 8px 12px !important;
-
-          color: @white !important;
-          font-weight: bold !important;
-
-          i {
-            background-image: url("@{iconWhiteSpritePath}");
-            position: relative;
-            top: 0px;
-          }
-
-          &:hover, &:active, &:focus {
-            .opacity(100) !important;
-
-            text-decoration: none !important;
-          }
-
-          &.btn-resolve {
-            background-color: darken(@green, 5%);
-            text-shadow: 0px 1px 0px darken(@green, 20%);
-          }
-
-          &.btn-bogus {
-            background-color: @grayDark;
-            text-shadow: 0px 1px 0px darken(@grayDark, 20%);
-          }
-        }
-      }
-    }
-  }
+// Report view
+// ------------------------
+
+.report-view {
+  .report-wrapper {
+    background-color: @grayLighter;
+    background-image: -webkit-gradient(linear, 0 0, 100% 100%,
+                color-stop(.25, rgba(255, 255, 255, .2)), color-stop(.25, transparent),
+                color-stop(.5, transparent), color-stop(.5, rgba(255, 255, 255, .2)),
+                color-stop(.75, rgba(255, 255, 255, .2)), color-stop(.75, transparent),
+                to(transparent));
+    background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+              transparent 75%, transparent);
+    background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+              transparent 75%, transparent);
+    background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+              transparent 75%, transparent);
+    background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+              transparent 75%, transparent);
+    background-image: linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+              transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+              transparent 75%, transparent);
+    -webkit-background-size: 10px 10px;
+    -moz-background-size: 10px 10px;
+    background-size: 10px 10px;
+    .border-radius(6px);
+    padding: 8px;
+    margin-bottom: 8px;
+
+    .post-body {
+      margin-bottom: 0px;
+
+      .report-actions {
+        border-left: none !important;
+        float: left !important;
+        padding: 0px !important;
+
+        form:first-child .btn {
+          .border-radius(0px 0px 0px @baseBorderRadius) !important;
+        }
+
+        .btn {
+          .opacity(90) !important;
+          margin: 0px !important;
+          padding: 8px 12px !important;
+
+          color: @white !important;
+          font-weight: bold !important;
+
+          i {
+            background-image: url("@{iconWhiteSpritePath}");
+            position: relative;
+            top: 0px;
+          }
+
+          &:hover, &:active, &:focus {
+            .opacity(100) !important;
+
+            text-decoration: none !important;
+          }
+
+          &.btn-resolve {
+            background-color: darken(@green, 5%);
+            text-shadow: 0px 1px 0px darken(@green, 20%);
+          }
+
+          &.btn-bogus {
+            background-color: @grayDark;
+            text-shadow: 0px 1px 0px darken(@grayDark, 20%);
+          }
+        }
+      }
+    }
+  }
 }
 }

+ 53 - 53
static/cranefly/css/cranefly/reports.less

@@ -1,54 +1,54 @@
-// Reports list
-// ------------------------
-
-.reports-list {
-  .thread-label {
-    overflow: visible;
-
-    .report-label {
-      .border-radius(3px);
-      float: right;
-      padding: 3px 8px;
-      position: relative;
-      right: -32px;
-      bottom: 2px;
-
-      color: @white;
-      font-weight: bold;
-
-      i {
-        background-image: url("@{iconWhiteSpritePath}");
-      }
-
-      &.report-open {
-        background-color: @orange;
-        text-shadow: 0px 1px 0px darken(@orange, 20%);
-      }
-
-      &.report-resolved {
-        background-color: @green;
-        text-shadow: 0px 1px 0px darken(@green, 20%);
-      }
-
-      &.report-bogus {
-        background-color: @gray;
-        text-shadow: 0px 1px 0px darken(@gray, 20%);
-      }
-    }
-  }
-
-  .thread-name {
-    .report-id {
-      color: @grayLight !important;
-    }
-  }
-
-  .thread-name, .thread-details {
-    margin-left: 0px !important;
-  }
-
-  .thread-icon {
-    display: none !important;
-    float: none;
-  }
+// Reports list
+// ------------------------
+
+.reports-list {
+  .thread-label {
+    overflow: visible;
+
+    .report-label {
+      .border-radius(3px);
+      float: right;
+      padding: 3px 8px;
+      position: relative;
+      right: -32px;
+      bottom: 2px;
+
+      color: @white;
+      font-weight: bold;
+
+      i {
+        background-image: url("@{iconWhiteSpritePath}");
+      }
+
+      &.report-open {
+        background-color: @orange;
+        text-shadow: 0px 1px 0px darken(@orange, 20%);
+      }
+
+      &.report-resolved {
+        background-color: @green;
+        text-shadow: 0px 1px 0px darken(@green, 20%);
+      }
+
+      &.report-bogus {
+        background-color: @gray;
+        text-shadow: 0px 1px 0px darken(@gray, 20%);
+      }
+    }
+  }
+
+  .thread-name {
+    .report-id {
+      color: @grayLight !important;
+    }
+  }
+
+  .thread-name, .thread-details {
+    margin-left: 0px !important;
+  }
+
+  .thread-icon {
+    display: none !important;
+    float: none;
+  }
 }
 }

+ 69 - 69
static/cranefly/css/cranefly/scaffolding.less

@@ -1,70 +1,70 @@
-// Layout classess
-// -------------------------
-
-html, body {
-  height: 100%;
-}
-
-#wrap {
-  min-height: 100%;
-  height: auto !important;
-  height: 100%;
-  margin: 0 auto ((@footerHeight * -1) - @baseFontSize + 2px);
-
-  .container-primary {
-    padding-top: @baseLineHeight;
-    padding-bottom: @footerHeight + (@baseLineHeight * 1.5);
-
-    .page-description {
-      margin-bottom: @baseLineHeight;
-    }
-
-    hr {
-      border: none;
-      border-top: 1px solid @grayLighter;
-    }
-  }
-}
-
-// Footer
-// -------------------------
-footer {
-  background-color: @footerBackground;
-  border-top: 1px solid @footerBorder;
-  height: @footerHeight;
-  padding: @paddingLarge;
-  padding-bottom: 0px;
-
-  .container {
-    hr {
-      border-bottom: 1px solid @footerBorder;
-      margin: (@baseLineHeight / 2) 0px;
-    }
-
-    .credits {
-      p {
-        margin-bottom: 0px;
-
-        color: @gray;
-        font-size: 90%;
-
-        a:link, a:active, a:visited, a:hover {
-          color: @gray;
-        }
-      }
-    }
-  }
-}
-
-// Selection
-// -------------------------
-::selection {
-  background: @orange;
-
-  color: @white;
-}
-::-moz-selection {
-  background: @orange;
-
-  color: @white;
+// Layout classess
+// -------------------------
+
+html, body {
+  height: 100%;
+}
+
+#wrap {
+  min-height: 100%;
+  height: auto !important;
+  height: 100%;
+  margin: 0 auto ((@footerHeight * -1) - @baseFontSize + 2px);
+
+  .container-primary {
+    padding-top: @baseLineHeight;
+    padding-bottom: @footerHeight + (@baseLineHeight * 1.5);
+
+    .page-description {
+      margin-bottom: @baseLineHeight;
+    }
+
+    hr {
+      border: none;
+      border-top: 1px solid @grayLighter;
+    }
+  }
+}
+
+// Footer
+// -------------------------
+footer {
+  background-color: @footerBackground;
+  border-top: 1px solid @footerBorder;
+  height: @footerHeight;
+  padding: @paddingLarge;
+  padding-bottom: 0px;
+
+  .container {
+    hr {
+      border-bottom: 1px solid @footerBorder;
+      margin: (@baseLineHeight / 2) 0px;
+    }
+
+    .credits {
+      p {
+        margin-bottom: 0px;
+
+        color: @gray;
+        font-size: 90%;
+
+        a:link, a:active, a:visited, a:hover {
+          color: @gray;
+        }
+      }
+    }
+  }
+}
+
+// Selection
+// -------------------------
+::selection {
+  background: @orange;
+
+  color: @white;
+}
+::-moz-selection {
+  background: @orange;
+
+  color: @white;
 }
 }

+ 105 - 105
static/cranefly/css/cranefly/search.less

@@ -1,106 +1,106 @@
-// Search forum
-// ------------------------
-
-.search-suggestion {
-  overflow: auto;
-
-  p, form {
-    float: left;
-  }
-
-  .lead {
-    color: lighten(@gray, 15%);
-  }
-
-  form {
-    .btn-link {
-      margin-top: 1px;
-
-      color: @bluePale;
-      font-size: @baseFontSize * 1.5;
-      font-style: italic;
-      font-weight: 200;
-      text-decoration: underline;
-
-      &:hover, &:active {
-        color: @bluePale;
-        text-decoration: underline !important;
-      }
-    }
-  }
-}
-
-.search-resume {
-  .muted {
-    color: lighten(@gray, 15%);
-
-    a {
-      color: @textColor;
-    }
-  }
-}
-
-.search-results {
-  .results-list {
-    .result {
-      border-bottom: 1px solid darken(@bodyBackground, 5%);
-      margin-bottom: (@baseLineHeight / 2);
-      padding-bottom: (@baseLineHeight / 2);
-
-      .result-avatar {
-        float: left;
-
-        img {
-          .border-radius(@baseBorderRadius);
-          width: 70px;
-          height: 70px;
-        }
-      }
-
-      .result-content {
-        margin-left: 70px + @baseFontSize;
-        h3 {
-          margin: 0px;
-
-          line-height: @baseLineHeight;
-
-          a:link, a:visited {
-            color: @gray;
-            font-weight: normal;
-            font-size: @baseFontSize * 1.3;
-            text-decoration: underline;
-          }
-
-          a:hover, a:active {
-            color: @textColor;
-          }
-        }
-
-        .post-preview {
-          margin: 0px;
-          margin-top: 6px;
-
-          color: @textColor;
-          font-size: @baseFontSize * 1.1;
-          line-height: @baseLineHeight;
-
-          strong {
-            color: @red;
-          }
-        }
-
-        .post-extra {
-          margin: 0px;
-
-          color: @grayLight;
-          font-size: @baseFontSize * 0.8;
-          line-height: @baseLineHeight;
-
-          a {
-            color: @textColor;
-          }
-        }
-      }
-    }
-  }
+// Search forum
+// ------------------------
+
+.search-suggestion {
+  overflow: auto;
+
+  p, form {
+    float: left;
+  }
+
+  .lead {
+    color: lighten(@gray, 15%);
+  }
+
+  form {
+    .btn-link {
+      margin-top: 1px;
+
+      color: @bluePale;
+      font-size: @baseFontSize * 1.5;
+      font-style: italic;
+      font-weight: 200;
+      text-decoration: underline;
+
+      &:hover, &:active {
+        color: @bluePale;
+        text-decoration: underline !important;
+      }
+    }
+  }
+}
+
+.search-resume {
+  .muted {
+    color: lighten(@gray, 15%);
+
+    a {
+      color: @textColor;
+    }
+  }
+}
+
+.search-results {
+  .results-list {
+    .result {
+      border-bottom: 1px solid darken(@bodyBackground, 5%);
+      margin-bottom: (@baseLineHeight / 2);
+      padding-bottom: (@baseLineHeight / 2);
+
+      .result-avatar {
+        float: left;
+
+        img {
+          .border-radius(@baseBorderRadius);
+          width: 70px;
+          height: 70px;
+        }
+      }
+
+      .result-content {
+        margin-left: 70px + @baseFontSize;
+        h3 {
+          margin: 0px;
+
+          line-height: @baseLineHeight;
+
+          a:link, a:visited {
+            color: @gray;
+            font-weight: normal;
+            font-size: @baseFontSize * 1.3;
+            text-decoration: underline;
+          }
+
+          a:hover, a:active {
+            color: @textColor;
+          }
+        }
+
+        .post-preview {
+          margin: 0px;
+          margin-top: 6px;
+
+          color: @textColor;
+          font-size: @baseFontSize * 1.1;
+          line-height: @baseLineHeight;
+
+          strong {
+            color: @red;
+          }
+        }
+
+        .post-extra {
+          margin: 0px;
+
+          color: @grayLight;
+          font-size: @baseFontSize * 0.8;
+          line-height: @baseLineHeight;
+
+          a {
+            color: @textColor;
+          }
+        }
+      }
+    }
+  }
 }
 }

+ 21 - 21
static/cranefly/css/cranefly/signin.less

@@ -1,22 +1,22 @@
-// Sign-in page
-// -------------------------
-
-.signin-help {
-  h2 {
-    margin-bottom: 0px;
-
-    color: @grayLight;
-    font-size: @fontSizeLarge;
-  }
-
-  ul {
-    margin-bottom: 0px;
-  }
-
-  a:link, a:visited {
-    .opacity(90);
-
-    color: @textColor;
-    font-weight: bold;
-  }
+// Sign-in page
+// -------------------------
+
+.signin-help {
+  h2 {
+    margin-bottom: 0px;
+
+    color: @grayLight;
+    font-size: @fontSizeLarge;
+  }
+
+  ul {
+    margin-bottom: 0px;
+  }
+
+  a:link, a:visited {
+    .opacity(90);
+
+    color: @textColor;
+    font-weight: bold;
+  }
 }
 }

+ 621 - 621
static/cranefly/css/cranefly/thread.less

@@ -1,622 +1,622 @@
-// Thread view
-// -------------------------
-
-.thread-buttons {
-  overflow: auto;
-
-  .pull-right {
-    margin-left: @baseFontSize;
-  }
-
-  .thread-signin-message {
-    float: right;
-
-    a:link, a:visited {
-      color: @textColor;
-    }
-  }
-}
-
-// Thread body styles
-.thread-body {
-  .post-wrapper {
-    // Post body
-    .post-body {
-      margin-bottom: @baseLineHeight;
-      overflow: auto;
-
-      .user-avatar {
-        border-radius: @borderRadiusLarge;
-        float: left;
-        width: 100px;
-        height: 100px;
-      }
-
-      .post-content {
-        background-color: @postBackground;
-        border: 1px solid @postBorder;
-        border-radius: @borderRadiusLarge;
-        margin-left: 100px + (@baseFontSize * 1.5);
-        min-height: 100px;
-        position: relative;
-
-        &:after, &:before {
-          right: 100%;
-          border: solid transparent;
-          content: "";
-          height: 0; width: 0;
-          position: absolute;
-          pointer-events: none;
-        }
-
-        &:after {
-          border-color: transparent;
-          border-right-color: @postBackground;
-          border-width: @fontSizeMini;
-          top: @baseFontSize;
-          margin-top: (@baseFontSize * -1) + @baseFontSize;
-        }
-
-        &:before {
-           border-color: transparent;
-           border-right-color: @postBorder;
-           border-width: @fontSizeMini + 1;
-           top: @baseFontSize;
-           margin-top: (@baseFontSize * -1) + @baseFontSize - 1px;
-        }
-
-        .post-header {
-          padding: (@baseFontSize / 2) @baseFontSize;
-          padding-bottom: 0px;
-
-          color: grayLighter;
-
-          .post-author {
-            color: @textColor;
-            font-weight: bold;
-          }
-
-          .post-author-label {
-            text-shadow: none;
-
-            &.post-label-guest {
-              background-color: @grayLighter;
-
-              color: @grayLight;
-            }
-          }
-
-          .separator {
-            color: @grayLighter;
-            font-size: @fontSizeLarge;
-            line-height: 5px;
-          }
-
-          .post-date {
-            color: @grayLight;
-          }
-
-          .post-changelog {
-            color: @grayLight;
-
-            &:hover, &:active {
-              color: @textColor;
-            }
-          }
-
-          .post-perma {
-            display: block;
-            float: right;
-            padding-left: 6px;
-
-            color: @grayLight;
-            font-weight: bold;
-
-            &:hover, &:active {
-              color: @textColor;
-            }
-          }
-
-          .post-checkbox {
-            float: right;
-            position: relative;
-            left: @baseFontSize - 4px;
-          }
-
-          .post-extra {
-            float: right;
-
-            .label {
-              margin-left: @baseFontSize / 4;
-
-              text-shadow: none;
-
-              &.label-purple {
-                background-color: #7e2ecf;
-              }
-            }
-          }
-        }
-
-        .post-message {
-          .markdown {
-            padding: @baseFontSize;
-          }
-
-          .post-signature {
-            border-top: 1px dotted darken(@postBackground, 25%);
-            .opacity(60);
-            margin: 0px @baseFontSize;
-
-            font-size: 80%;
-
-            .markdown {
-              padding: 0px;
-              padding-top: @baseLineHeight / 4;
-              padding-bottom: @baseFontSize;
-            }
-          }
-        }
-
-        .post-footer {
-          border-top: 1px solid @postBorder;
-          overflow: auto;
-
-          &:empty {
-            display: none;
-          }
-
-          .post-rating {
-            float: left;
-            overflow: auto;
-            padding: (@baseFontSize / 2) @baseFontSize;
-            border-right: 1px dotted @postBorder;
-
-            a {
-              color: @grayLight;
-
-              &:hover, a:active {
-                color: @textColor;
-              }
-            }
-
-            span {
-              float: left;
-
-              &.post-score {
-                color: @grayLight;
-                font-weight: bold;
-
-                &.post-score-good {
-                  color: @green;
-                }
-
-                &.post-score-bad {
-                  color: @red;
-                }
-              }
-
-              &.post-neutral, &.post-like, &.post-hate {
-                margin-left: (@baseFontSize / 4); 
-              }
-
-              &.post-neutral {
-                color: @grayLight;
-              }
-
-              &.post-like {
-                color: @green;
-              }
-
-              &.post-hate {
-                color: @red;
-              }
-            }
-
-            form {
-              float: left;
-              margin: 0px;
-              padding: 0px;
-
-              .btn-link {
-                float: right;
-                margin: 0px;
-                margin-left: (@baseFontSize / 4);
-                .opacity(100);
-                padding: 0px;
-
-                color: @grayLight;
-                font-weight: normal;
-
-                &:hover, &:active, &:focus {
-                  text-decoration: underline;
-                }
-
-                &.post-like {
-                  &:hover, &:active, &:focus, &:disabled {
-                    color: @green;
-                  }
-                }
-
-                &.post-hate {
-                  &:hover, &:active, &:focus, &:disabled {
-                    color: @red;
-                  }
-                }
-
-                &:disabled {
-                  &:hover, &:active, &:focus {
-                    text-decoration: none;
-                  }
-                }
-              }
-            }
-          }
-
-          .post-actions {
-            border-left: 1px dotted @postBorder;
-            float: right;
-            padding: (@baseFontSize / 2) @baseFontSize;
-
-            color: @grayLight;
-
-            a, span, form {
-              float: left;
-              overflow: auto;
-            }
-
-            form {
-              margin: 0px;
-              padding: 0px;
-
-              .btn {
-                float: right;
-                margin: 0px;
-                margin-left: @baseFontSize;
-                .opacity(100);
-                padding: 0px;
-
-                color: @grayLight;
-                font-weight: normal;
-
-                &:hover, &:active, &:focus {
-                  color: @red;
-                  text-decoration: underline;
-                }
-
-                &.btn-report {
-                  &:disabled {
-                    color: @green;
-                  }
-                }
-
-                &.btn-hide {
-                  &:hover, &:active, &:focus {
-                    color: @orange;
-                  }
-                }
-              }
-
-              &:first-child .btn {
-                margin-left: 0px;
-              }
-            }
-
-            a {
-              margin-left: @baseFontSize;
-
-              color: @grayLight;
-
-              &:hover, a:active {
-                color: @textColor;
-              }
-
-              &:first-child {
-                margin-left: 0px;
-              }
-
-              &.post-reply {
-                color: @gray;
-
-                &:hover, a:active {
-                  color: @blue;
-                }
-              }
-            }
-          }
-        }
-      }
-
-      &.post-muted {        
-        .user-avatar {
-          width: 50px;
-          height: 50px;
-          .opacity(75);
-        }
-
-        .post-content {
-          margin-left: 50px + (@baseFontSize * 1.5);
-          min-height: 0px;
-          .opacity(75);
-          padding: @baseFontSize;
-
-          .post-header {
-            float: right;
-            margin: 0px;
-            margin-top: ((@baseFontSize / 2) * -1);
-            margin-right: @baseFontSize * -1;
-
-            .post-header-compact {
-              float: left;
-              margin-right: @baseFontSize;
-            }
-          }
-
-          .post-message {
-            color: @grayLight;
-            font-size: @fontSizeLarge;
-
-            strong, a {
-              color: @textColor;
-              font-weight: normal;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  .post-checkpoints {
-    .post-checkpoint {
-      text-align: center;
-
-      margin-bottom: @baseLineHeight;
-
-      &.checkpoint-deleted {
-        .opacity(30);
-
-        &:hover {
-          .opacity(60);
-        }
-      }
-
-      hr {
-        background-color: @grayLight;
-        background-image: -webkit-gradient(linear, 0 0, 100% 100%,
-                    color-stop(.25, rgba(255, 255, 255, .2)), color-stop(.25, transparent),
-                    color-stop(.5, transparent), color-stop(.5, rgba(255, 255, 255, .2)),
-                    color-stop(.75, rgba(255, 255, 255, .2)), color-stop(.75, transparent),
-                    to(transparent));
-        background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-                  transparent 75%, transparent);
-        background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-                  transparent 75%, transparent);
-        background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-                  transparent 75%, transparent);
-        background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-                  transparent 75%, transparent);
-        background-image: linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
-                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
-                  transparent 75%, transparent);
-        -webkit-background-size: 10px 10px;
-        -moz-background-size: 10px 10px;
-        background-size: 10px 10px;
-        border: none;
-        height: 4px;
-        margin-bottom: (@baseLineHeight * -1) + (@baseFontSize / 2) + 1px;
-      }
-
-      span {
-        background-color: @bodyBackground;
-        padding: 0px @baseFontSize;
-
-        color: @grayLight;
-
-        a {
-          color: @textColor;
-        }
-
-        i {
-          .opacity(43);
-        }
-
-        form {
-          display: inline-block;
-          margin: 0px;
-          margin-top: -3px;
-          margin-left: @baseFontSize / 2;
-          padding: 0px;
-
-          .btn {
-            margin-top: -2px;
-            padding: 0px;
-
-            font-weight: normal;
-
-            &:active, &:hover {
-              text-decoration: underline;
-
-              &.btn-show, &.btn-hide {
-                color: @orange;
-              }
-
-              &.btn-delete {
-                color: @red;
-              }
-            }
-          }
-        }
-
-        form:first-of-type {
-          margin-left: @baseFontSize;
-        }
-      }
-    }
-  }
-}
-
-// Thread moderation
-.thread-moderation {
-  background-color: @categoryBackground;
-  border: 1px solid @categoryBorder;
-  border-radius: @baseBorderRadius;
-  .box-shadow(0px 0px 0px 3px @categoryShadow);
-  margin-bottom: @baseLineHeight;
-  overflow: auto;
-  padding: (@baseFontSize / 2);
-
-  form {
-    margin: 0px;
-  }
-}
-
-// Quick reply styles
-.thread-quick-reply {
-  overflow: auto;
-  margin-top: @baseLineHeight;
-
-  .user-avatar {
-    border-radius: @baseBorderRadius;
-    float: left;
-    width: 100px;
-    height: 100px;
-    overflow: visible;
-  }
-
-  .editor {
-    margin-left: 100px + (@baseFontSize * 1.5);
-    position: relative;
-
-    &:after, &:before {
-      right: 100%;
-      border: solid transparent;
-      content: "";
-      height: 0; width: 0;
-      position: absolute;
-      pointer-events: none;
-    }
-
-    &:after {
-      border-color: transparent;
-      border-right-color: @editorBackground;
-      border-width: @fontSizeMini;
-      top: @baseFontSize;
-      margin-top: (@baseFontSize * -1) + @baseFontSize;
-    }
-
-    &:before {
-       border-color: transparent;
-       border-right-color: darken(@editorBackground, 10%);
-       border-width: @fontSizeMini + 1;
-       top: @baseFontSize;
-       margin-top: (@baseFontSize * -1) + @baseFontSize - 1px;
-    }
-  }
-}
-
-// Thread participants list
-.thread-participants {
-  h3 {
-    margin: 0px;
-    margin-top: (@baseLineHeight - @baseFontSize) * -1;
-    padding: 0px;
-
-    color: @gray;
-    font-size: @fontSizeLarge;
-    font-weight: bold;
-  }
-
-  ul {
-    background-color: @white;
-    border: 1px solid darken(@bodyBackground, 10%);
-    border-radius: @baseBorderRadius;
-    margin: 0px;
-    margin-bottom: @baseLineHeight;
-    padding: 0px;
-
-    li {
-      border-bottom: 1px dotted darken(@bodyBackground, 10%);
-      margin: 0px;
-      padding: 6px 8px;
-
-      font-weight: bold;
-
-      img {
-        border-radius: @borderRadiusSmall;
-        width: 24px;
-        height: 24px;
-      }
-
-      a:link, a:active, a:visited, a:hover {
-        margin: 0px 4px;
-
-        color: @textColor;
-        font-weight: bold;
-      }
-
-      &:last-child {
-        border-bottom: none;
-      }
-
-      form {
-        float: right;
-        margin: 0px;
-        padding: 0px;
-
-        button {
-          padding-left: 5px;
-          padding-right: 5px;
-
-          i {
-            position: relative;
-            top: 1px;
-          }
-        }
-      }
-    }
-  }
-
-  h4 {
-    margin: 0px;
-    padding: 0px;
-
-    color: @gray;
-    font-size: @baseFontSize * 1.2;
-    font-weight: bold;
-  }
-
-  .no-participants {
-    margin-bottom: @baseLineHeight;
-  }
-
-  .invite-participant {
-    background-color: @white;
-    border: 1px solid darken(@bodyBackground, 15%);
-    border-radius: @baseBorderRadius;
-    margin-top: @baseLineHeight - @baseFontSize;
-    padding: 1px;
-
-    form {
-      margin: 0px;
-      padding: 0px;
-
-      input, button {
-        border: none;
-        background: none;
-        box-shadow: none;
-      }
-
-      input {
-        width: 70%;
-      }
-
-      button {
-        float: right;
-      }
-    }
-  }
+// Thread view
+// -------------------------
+
+.thread-buttons {
+  overflow: auto;
+
+  .pull-right {
+    margin-left: @baseFontSize;
+  }
+
+  .thread-signin-message {
+    float: right;
+
+    a:link, a:visited {
+      color: @textColor;
+    }
+  }
+}
+
+// Thread body styles
+.thread-body {
+  .post-wrapper {
+    // Post body
+    .post-body {
+      margin-bottom: @baseLineHeight;
+      overflow: auto;
+
+      .user-avatar {
+        border-radius: @borderRadiusLarge;
+        float: left;
+        width: 100px;
+        height: 100px;
+      }
+
+      .post-content {
+        background-color: @postBackground;
+        border: 1px solid @postBorder;
+        border-radius: @borderRadiusLarge;
+        margin-left: 100px + (@baseFontSize * 1.5);
+        min-height: 100px;
+        position: relative;
+
+        &:after, &:before {
+          right: 100%;
+          border: solid transparent;
+          content: "";
+          height: 0; width: 0;
+          position: absolute;
+          pointer-events: none;
+        }
+
+        &:after {
+          border-color: transparent;
+          border-right-color: @postBackground;
+          border-width: @fontSizeMini;
+          top: @baseFontSize;
+          margin-top: (@baseFontSize * -1) + @baseFontSize;
+        }
+
+        &:before {
+           border-color: transparent;
+           border-right-color: @postBorder;
+           border-width: @fontSizeMini + 1;
+           top: @baseFontSize;
+           margin-top: (@baseFontSize * -1) + @baseFontSize - 1px;
+        }
+
+        .post-header {
+          padding: (@baseFontSize / 2) @baseFontSize;
+          padding-bottom: 0px;
+
+          color: grayLighter;
+
+          .post-author {
+            color: @textColor;
+            font-weight: bold;
+          }
+
+          .post-author-label {
+            text-shadow: none;
+
+            &.post-label-guest {
+              background-color: @grayLighter;
+
+              color: @grayLight;
+            }
+          }
+
+          .separator {
+            color: @grayLighter;
+            font-size: @fontSizeLarge;
+            line-height: 5px;
+          }
+
+          .post-date {
+            color: @grayLight;
+          }
+
+          .post-changelog {
+            color: @grayLight;
+
+            &:hover, &:active {
+              color: @textColor;
+            }
+          }
+
+          .post-perma {
+            display: block;
+            float: right;
+            padding-left: 6px;
+
+            color: @grayLight;
+            font-weight: bold;
+
+            &:hover, &:active {
+              color: @textColor;
+            }
+          }
+
+          .post-checkbox {
+            float: right;
+            position: relative;
+            left: @baseFontSize - 4px;
+          }
+
+          .post-extra {
+            float: right;
+
+            .label {
+              margin-left: @baseFontSize / 4;
+
+              text-shadow: none;
+
+              &.label-purple {
+                background-color: #7e2ecf;
+              }
+            }
+          }
+        }
+
+        .post-message {
+          .markdown {
+            padding: @baseFontSize;
+          }
+
+          .post-signature {
+            border-top: 1px dotted darken(@postBackground, 25%);
+            .opacity(60);
+            margin: 0px @baseFontSize;
+
+            font-size: 80%;
+
+            .markdown {
+              padding: 0px;
+              padding-top: @baseLineHeight / 4;
+              padding-bottom: @baseFontSize;
+            }
+          }
+        }
+
+        .post-footer {
+          border-top: 1px solid @postBorder;
+          overflow: auto;
+
+          &:empty {
+            display: none;
+          }
+
+          .post-rating {
+            float: left;
+            overflow: auto;
+            padding: (@baseFontSize / 2) @baseFontSize;
+            border-right: 1px dotted @postBorder;
+
+            a {
+              color: @grayLight;
+
+              &:hover, a:active {
+                color: @textColor;
+              }
+            }
+
+            span {
+              float: left;
+
+              &.post-score {
+                color: @grayLight;
+                font-weight: bold;
+
+                &.post-score-good {
+                  color: @green;
+                }
+
+                &.post-score-bad {
+                  color: @red;
+                }
+              }
+
+              &.post-neutral, &.post-like, &.post-hate {
+                margin-left: (@baseFontSize / 4); 
+              }
+
+              &.post-neutral {
+                color: @grayLight;
+              }
+
+              &.post-like {
+                color: @green;
+              }
+
+              &.post-hate {
+                color: @red;
+              }
+            }
+
+            form {
+              float: left;
+              margin: 0px;
+              padding: 0px;
+
+              .btn-link {
+                float: right;
+                margin: 0px;
+                margin-left: (@baseFontSize / 4);
+                .opacity(100);
+                padding: 0px;
+
+                color: @grayLight;
+                font-weight: normal;
+
+                &:hover, &:active, &:focus {
+                  text-decoration: underline;
+                }
+
+                &.post-like {
+                  &:hover, &:active, &:focus, &:disabled {
+                    color: @green;
+                  }
+                }
+
+                &.post-hate {
+                  &:hover, &:active, &:focus, &:disabled {
+                    color: @red;
+                  }
+                }
+
+                &:disabled {
+                  &:hover, &:active, &:focus {
+                    text-decoration: none;
+                  }
+                }
+              }
+            }
+          }
+
+          .post-actions {
+            border-left: 1px dotted @postBorder;
+            float: right;
+            padding: (@baseFontSize / 2) @baseFontSize;
+
+            color: @grayLight;
+
+            a, span, form {
+              float: left;
+              overflow: auto;
+            }
+
+            form {
+              margin: 0px;
+              padding: 0px;
+
+              .btn {
+                float: right;
+                margin: 0px;
+                margin-left: @baseFontSize;
+                .opacity(100);
+                padding: 0px;
+
+                color: @grayLight;
+                font-weight: normal;
+
+                &:hover, &:active, &:focus {
+                  color: @red;
+                  text-decoration: underline;
+                }
+
+                &.btn-report {
+                  &:disabled {
+                    color: @green;
+                  }
+                }
+
+                &.btn-hide {
+                  &:hover, &:active, &:focus {
+                    color: @orange;
+                  }
+                }
+              }
+
+              &:first-child .btn {
+                margin-left: 0px;
+              }
+            }
+
+            a {
+              margin-left: @baseFontSize;
+
+              color: @grayLight;
+
+              &:hover, a:active {
+                color: @textColor;
+              }
+
+              &:first-child {
+                margin-left: 0px;
+              }
+
+              &.post-reply {
+                color: @gray;
+
+                &:hover, a:active {
+                  color: @blue;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      &.post-muted {        
+        .user-avatar {
+          width: 50px;
+          height: 50px;
+          .opacity(75);
+        }
+
+        .post-content {
+          margin-left: 50px + (@baseFontSize * 1.5);
+          min-height: 0px;
+          .opacity(75);
+          padding: @baseFontSize;
+
+          .post-header {
+            float: right;
+            margin: 0px;
+            margin-top: ((@baseFontSize / 2) * -1);
+            margin-right: @baseFontSize * -1;
+
+            .post-header-compact {
+              float: left;
+              margin-right: @baseFontSize;
+            }
+          }
+
+          .post-message {
+            color: @grayLight;
+            font-size: @fontSizeLarge;
+
+            strong, a {
+              color: @textColor;
+              font-weight: normal;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .post-checkpoints {
+    .post-checkpoint {
+      text-align: center;
+
+      margin-bottom: @baseLineHeight;
+
+      &.checkpoint-deleted {
+        .opacity(30);
+
+        &:hover {
+          .opacity(60);
+        }
+      }
+
+      hr {
+        background-color: @grayLight;
+        background-image: -webkit-gradient(linear, 0 0, 100% 100%,
+                    color-stop(.25, rgba(255, 255, 255, .2)), color-stop(.25, transparent),
+                    color-stop(.5, transparent), color-stop(.5, rgba(255, 255, 255, .2)),
+                    color-stop(.75, rgba(255, 255, 255, .2)), color-stop(.75, transparent),
+                    to(transparent));
+        background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+                  transparent 75%, transparent);
+        background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+                  transparent 75%, transparent);
+        background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+                  transparent 75%, transparent);
+        background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+                  transparent 75%, transparent);
+        background-image: linear-gradient(-45deg, rgba(255, 255, 255, .2) 25%, transparent 25%,
+                  transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%,
+                  transparent 75%, transparent);
+        -webkit-background-size: 10px 10px;
+        -moz-background-size: 10px 10px;
+        background-size: 10px 10px;
+        border: none;
+        height: 4px;
+        margin-bottom: (@baseLineHeight * -1) + (@baseFontSize / 2) + 1px;
+      }
+
+      span {
+        background-color: @bodyBackground;
+        padding: 0px @baseFontSize;
+
+        color: @grayLight;
+
+        a {
+          color: @textColor;
+        }
+
+        i {
+          .opacity(43);
+        }
+
+        form {
+          display: inline-block;
+          margin: 0px;
+          margin-top: -3px;
+          margin-left: @baseFontSize / 2;
+          padding: 0px;
+
+          .btn {
+            margin-top: -2px;
+            padding: 0px;
+
+            font-weight: normal;
+
+            &:active, &:hover {
+              text-decoration: underline;
+
+              &.btn-show, &.btn-hide {
+                color: @orange;
+              }
+
+              &.btn-delete {
+                color: @red;
+              }
+            }
+          }
+        }
+
+        form:first-of-type {
+          margin-left: @baseFontSize;
+        }
+      }
+    }
+  }
+}
+
+// Thread moderation
+.thread-moderation {
+  background-color: @categoryBackground;
+  border: 1px solid @categoryBorder;
+  border-radius: @baseBorderRadius;
+  .box-shadow(0px 0px 0px 3px @categoryShadow);
+  margin-bottom: @baseLineHeight;
+  overflow: auto;
+  padding: (@baseFontSize / 2);
+
+  form {
+    margin: 0px;
+  }
+}
+
+// Quick reply styles
+.thread-quick-reply {
+  overflow: auto;
+  margin-top: @baseLineHeight;
+
+  .user-avatar {
+    border-radius: @baseBorderRadius;
+    float: left;
+    width: 100px;
+    height: 100px;
+    overflow: visible;
+  }
+
+  .editor {
+    margin-left: 100px + (@baseFontSize * 1.5);
+    position: relative;
+
+    &:after, &:before {
+      right: 100%;
+      border: solid transparent;
+      content: "";
+      height: 0; width: 0;
+      position: absolute;
+      pointer-events: none;
+    }
+
+    &:after {
+      border-color: transparent;
+      border-right-color: @editorBackground;
+      border-width: @fontSizeMini;
+      top: @baseFontSize;
+      margin-top: (@baseFontSize * -1) + @baseFontSize;
+    }
+
+    &:before {
+       border-color: transparent;
+       border-right-color: darken(@editorBackground, 10%);
+       border-width: @fontSizeMini + 1;
+       top: @baseFontSize;
+       margin-top: (@baseFontSize * -1) + @baseFontSize - 1px;
+    }
+  }
+}
+
+// Thread participants list
+.thread-participants {
+  h3 {
+    margin: 0px;
+    margin-top: (@baseLineHeight - @baseFontSize) * -1;
+    padding: 0px;
+
+    color: @gray;
+    font-size: @fontSizeLarge;
+    font-weight: bold;
+  }
+
+  ul {
+    background-color: @white;
+    border: 1px solid darken(@bodyBackground, 10%);
+    border-radius: @baseBorderRadius;
+    margin: 0px;
+    margin-bottom: @baseLineHeight;
+    padding: 0px;
+
+    li {
+      border-bottom: 1px dotted darken(@bodyBackground, 10%);
+      margin: 0px;
+      padding: 6px 8px;
+
+      font-weight: bold;
+
+      img {
+        border-radius: @borderRadiusSmall;
+        width: 24px;
+        height: 24px;
+      }
+
+      a:link, a:active, a:visited, a:hover {
+        margin: 0px 4px;
+
+        color: @textColor;
+        font-weight: bold;
+      }
+
+      &:last-child {
+        border-bottom: none;
+      }
+
+      form {
+        float: right;
+        margin: 0px;
+        padding: 0px;
+
+        button {
+          padding-left: 5px;
+          padding-right: 5px;
+
+          i {
+            position: relative;
+            top: 1px;
+          }
+        }
+      }
+    }
+  }
+
+  h4 {
+    margin: 0px;
+    padding: 0px;
+
+    color: @gray;
+    font-size: @baseFontSize * 1.2;
+    font-weight: bold;
+  }
+
+  .no-participants {
+    margin-bottom: @baseLineHeight;
+  }
+
+  .invite-participant {
+    background-color: @white;
+    border: 1px solid darken(@bodyBackground, 15%);
+    border-radius: @baseBorderRadius;
+    margin-top: @baseLineHeight - @baseFontSize;
+    padding: 1px;
+
+    form {
+      margin: 0px;
+      padding: 0px;
+
+      input, button {
+        border: none;
+        background: none;
+        box-shadow: none;
+      }
+
+      input {
+        width: 70%;
+      }
+
+      button {
+        float: right;
+      }
+    }
+  }
 }
 }

+ 118 - 118
static/cranefly/css/cranefly/usercp.less

@@ -1,119 +1,119 @@
-// User Control Panel
-// -------------------------
-
-.usercp {
-  .usercp-tabs {
-    border: none;
-
-    li {
-      float: none;
-      display: block;
-      width: 100%;
-
-      a:link, a:visited {
-        border: none;
-        border-radius: @borderRadiusSmall 0px 0px @borderRadiusSmall;
-        float: none;
-        display: block;
-        width: 100%;
-
-        color: @gray;
-        font-weight: bold;
-      }
-
-      a:active, a:hover {
-        background-color: @bluePale;
-
-        color: @white;
-        text-shadow: 0px 1px 0px darken(@bluePale, 25%);
-      }
-
-      &.active {
-        a:link, a:visited, a:active, a:hover {
-          background-color: @red;
-
-          color: @white;
-          text-shadow: 0px 1px 0px darken(@red, 25%);
-        }
-      }
-    }
-  }
-
-  .usercp-action {
-    .form-container {
-      min-height: 180px;
-    }
-  }
-}
-
-// Avatar Edit
-// -------------------------
-.usercp-avatar-menu {
-  margin-bottom: 0px;
-
-  .media-object {
-    border-radius: @borderRadiusLarge;
-    margin-top: @baseFontSize;
-    width: 125px;
-    height: 125px;
-  }
-
-  .media-body {
-    margin-left: (@baseFontSize * 2) + 125px;
-
-    ul.unstyled {
-      margin-left: @baseFontSize * -1 + 1px;
-    }
-  }
-}
-
-.usercp-avatar-select {
-  .usercp-avatar-gallery {
-    margin: 0px (@baseFontSize * -1);
-    overflow: auto;
-    padding-bottom-bottom: @baseFontSize;
-
-    .usercp-avatar-select-form {
-      margin: 0px;
-      padding: 0px;
-      float: left;
-
-      .usercp-btn-avatar {
-        margin: @baseFontSize;
-        padding: 0px;
-
-        &, &:active, &:hover {
-          .opacity(100);
-
-          img {
-            .opacity(100);
-          }
-        }
-
-        &:active, &:hover {
-          img {
-            border-color: @linkColor;
-            .box-shadow(0px 0px 3px @linkColor);
-          }
-        }
-      } 
-    }
-  }
-}
-
-.usercp-avatar-crop {
-  .avatar-crop-preview {
-    border-radius: @borderRadiusLarge;
-    float: left;
-    width: @fontSizeLarge * 2.5;
-    height: @fontSizeLarge * 2.5;
-    margin-right: @baseFontSize;
-    overflow: hidden;
-  }
-
-  .avatar-crop-target {
-    img {
-      background-color: @white;
-    }
-  }
+// User Control Panel
+// -------------------------
+
+.usercp {
+  .usercp-tabs {
+    border: none;
+
+    li {
+      float: none;
+      display: block;
+      width: 100%;
+
+      a:link, a:visited {
+        border: none;
+        border-radius: @borderRadiusSmall 0px 0px @borderRadiusSmall;
+        float: none;
+        display: block;
+        width: 100%;
+
+        color: @gray;
+        font-weight: bold;
+      }
+
+      a:active, a:hover {
+        background-color: @bluePale;
+
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@bluePale, 25%);
+      }
+
+      &.active {
+        a:link, a:visited, a:active, a:hover {
+          background-color: @red;
+
+          color: @white;
+          text-shadow: 0px 1px 0px darken(@red, 25%);
+        }
+      }
+    }
+  }
+
+  .usercp-action {
+    .form-container {
+      min-height: 180px;
+    }
+  }
+}
+
+// Avatar Edit
+// -------------------------
+.usercp-avatar-menu {
+  margin-bottom: 0px;
+
+  .media-object {
+    border-radius: @borderRadiusLarge;
+    margin-top: @baseFontSize;
+    width: 125px;
+    height: 125px;
+  }
+
+  .media-body {
+    margin-left: (@baseFontSize * 2) + 125px;
+
+    ul.unstyled {
+      margin-left: @baseFontSize * -1 + 1px;
+    }
+  }
+}
+
+.usercp-avatar-select {
+  .usercp-avatar-gallery {
+    margin: 0px (@baseFontSize * -1);
+    overflow: auto;
+    padding-bottom-bottom: @baseFontSize;
+
+    .usercp-avatar-select-form {
+      margin: 0px;
+      padding: 0px;
+      float: left;
+
+      .usercp-btn-avatar {
+        margin: @baseFontSize;
+        padding: 0px;
+
+        &, &:active, &:hover {
+          .opacity(100);
+
+          img {
+            .opacity(100);
+          }
+        }
+
+        &:active, &:hover {
+          img {
+            border-color: @linkColor;
+            .box-shadow(0px 0px 3px @linkColor);
+          }
+        }
+      } 
+    }
+  }
+}
+
+.usercp-avatar-crop {
+  .avatar-crop-preview {
+    border-radius: @borderRadiusLarge;
+    float: left;
+    width: @fontSizeLarge * 2.5;
+    height: @fontSizeLarge * 2.5;
+    margin-right: @baseFontSize;
+    overflow: hidden;
+  }
+
+  .avatar-crop-target {
+    img {
+      background-color: @white;
+    }
+  }
 }
 }

+ 30 - 30
static/cranefly/css/cranefly/watchedthreads.less

@@ -1,31 +1,31 @@
-// Watched threads
-// -------------------------
-
-.watched-threads {
-  .thread-last-reply {
-    border-left: none !important;
-    padding-left: 0px !important;
-  }
-
-  .thread-options {
-    float: right;
-    overflow: auto;
-    position: relative;
-    top: 8px;
-
-    form {
-      display: inline-block;
-      float: left;
-      margin: 0px;
-      padding: 0px;
-      overflow: auto;
-
-      .btn {
-        float: right;
-        padding: 3px 5px;
-        padding-bottom: 0px;
-        margin-left: @baseFontSize + 2px;
-      }
-    }
-  }
+// Watched threads
+// -------------------------
+
+.watched-threads {
+  .thread-last-reply {
+    border-left: none !important;
+    padding-left: 0px !important;
+  }
+
+  .thread-options {
+    float: right;
+    overflow: auto;
+    position: relative;
+    top: 8px;
+
+    form {
+      display: inline-block;
+      float: left;
+      margin: 0px;
+      padding: 0px;
+      overflow: auto;
+
+      .btn {
+        float: right;
+        padding: 3px 5px;
+        padding-bottom: 0px;
+        margin-left: @baseFontSize + 2px;
+      }
+    }
+  }
 }
 }

+ 50 - 50
static/cranefly/css/ranks.less

@@ -1,50 +1,50 @@
-// Ranks styles
-// -------------------------
-
-// .rank-team
-.index-rank-team ul {
-  li {
-    .label {
-      background-color: @red;
-
-      color: @white;
-      text-shadow: 0px 1px 0px darken(@red, 35%);
-    }
-  }
-}
-
-.post-label-team {
-  background-color: @red;
-}
-
-// .rank-mvp
-.index-rank-mvp ul {
-  li {
-    .label {
-      background-color: @blue;
-
-      color: @white;
-      text-shadow: 0px 1px 0px darken(@blue, 35%);
-    }
-  }
-}
-
-.post-label-mvp {
-  background-color: @blue;
-}
-
-// .rank-top
-.index-rank-top ul {
-  li {
-    .label {
-      background-color: darken(@orange, 25%);
-
-      color: @white;
-      text-shadow: 0px 1px 0px darken(@orange, 35%);
-    }
-  }
-}
-
-.post-label-top {
-  background-color: @orange;
-}
+// Ranks styles
+// -------------------------
+
+// .rank-team
+.index-rank-team ul {
+  li {
+    .label {
+      background-color: @red;
+
+      color: @white;
+      text-shadow: 0px 1px 0px darken(@red, 35%);
+    }
+  }
+}
+
+.post-label-team {
+  background-color: @red;
+}
+
+// .rank-mvp
+.index-rank-mvp ul {
+  li {
+    .label {
+      background-color: @blue;
+
+      color: @white;
+      text-shadow: 0px 1px 0px darken(@blue, 35%);
+    }
+  }
+}
+
+.post-label-mvp {
+  background-color: @blue;
+}
+
+// .rank-top
+.index-rank-top ul {
+  li {
+    .label {
+      background-color: darken(@orange, 25%);
+
+      color: @white;
+      text-shadow: 0px 1px 0px darken(@orange, 35%);
+    }
+  }
+}
+
+.post-label-top {
+  background-color: @orange;
+}

+ 232 - 232
static/cranefly/js/cranefly.js

@@ -1,233 +1,233 @@
-$(function () {
-  // Register tooltips
-  $('.tooltip-top').tooltip({placement: 'top', container: 'body'})
-  $('.tooltip-bottom').tooltip({placement: 'bottom', container: 'body'})
-  $('.tooltip-left').tooltip({placement: 'left', container: 'body'})
-  $('.tooltip-right').tooltip({placement: 'right', container: 'body'})
-  
-  // Register popovers
-  $('.popover-top').popover({placement: 'top'})
-  $('.popover-bottom').popover({placement: 'bottom'})
-  $('.popover-left').popover({placement: 'left'})
-  $('.popover-right').popover({placement: 'right'})
-
-  // Dont fire popovers on touch devices
-  $("[class^='tooltip-']").on('show', function (e) {
-    if ('ontouchstart' in document.documentElement) {
-      e.preventDefault();
-    }
-  });
-  
-  // Start all dropdowns
-  $('.dropdown-toggle').dropdown()
-  
-  // Dont hide clickable dropdowns
-  $('.dropdown-clickable').on('click', function (e) {
-    e.stopPropagation()
-  });
-
-  // Fancy user nav activation
-  $('#fancy-user-nav').show();
-
-  // Search form extension
-  var nav_search_form = $('#navbar-search');
-  nav_search_form.click(function() {
-    nav_search_form.addClass('open');
-  });
-
-  $('html').click(function() {
-    nav_search_form.removeClass('open');
-  });
-
-  nav_search_form.click(function(event) {
-    event.stopPropagation();
-  });
-  
-  // Checkbox Group Master
-  $('input.checkbox-master').live('click', function(){
-    if($(this).is(':checked')){
-      $('input.checkbox-member').attr("checked" ,"checked");
-    }
-    else
-    {
-      $('input.checkbox-member').removeAttr('checked');
-    }
-  });
-  
-  // Checkbox Group Member
-  $('input.checkbox-member').live('click', function(){
-    if(!$(this).is(':checked')){
-      $('input.checkbox-master').removeAttr('checked');
-    }
-  });
-  
-  // Check Confirmation on links
-  $('a.confirm').live('click', function(){
-    var decision = confirm(jQuery.data(this, 'jsconfirm'));
-    return decision
-  });
-  
-  // Check Confirmation on forms
-  $('form.confirm').live('submit', function(){
-    data = $(this).data();
-    var decision = confirm(data.jsconfirm);
-    return decision
-  });
-  
-  // Show go back link?
-  if (document.referrer
-      && document.referrer.indexOf(location.protocol + "//" + location.host) === 0
-      && document.referrer != document.url) {
-    $('.go-back').show();
-  }
-
-  // Go back one page
-  $('.go-back').on('click', function (e) {
-      history.go(-1)
-      return false;
-  })
-})
-
-function EnhancePostsMD() {
-  $(function () {
-    // Add labels to images
-    $('.markdown.js-extra img').not('.emoji').each(function() {
-      $(this).addClass('img-rounded');
-      if ($(this).attr('alt').length > 0 && $(this).attr('alt') != $(this).attr('src')) {
-        $(this).attr('title', $(this).attr('alt'));
-        $(this).tooltip({placement: 'top', container: 'body'});
-      }
-    });
-
-    // Automagically turn links into players
-    var players = new Array();
-    $('.markdown.js-extra').each(function() {
-      var post_players = 0;
-      $(this).find('a').each(function() {
-        match = link2player($.trim($(this).text()));
-        if (match && $.inArray(match, players) == -1 && players.length < 16 && post_players < 4) {
-          players.push(match);
-          post_players ++;
-          $(this).replaceWith(match);
-        }
-      });
-    });
-  });
-}
-
-// Turn link to player
-function link2player(link_href) {
-  // Youtube link
-  var re = /watch\?v=((\w|-)+)/;
-  if (re.test(link_href)) {
-    media_url = link_href.match(re);
-    return '<iframe width="480" height="360" src="http://www.youtube.com/embed/' + media_url[1] + '" frameborder="0" allowfullscreen></iframe>';
-  }
-
-  // Youtube feature=embed
-  var re = /watch\?feature=player_embedded&v=((\w|-)+)/;
-  if (re.test(link_href)) {
-    media_url = link_href.match(re);
-    return '<iframe width="480" height="360" src="http://www.youtube.com/embed/' + media_url[1] + '" frameborder="0" allowfullscreen></iframe>';
-  }
-
-  // Youtube embed with start time
-  var re = /youtu.be\/((\w|-)+)\?t=([A-Za-z0-9]+)/;
-  if (re.test(link_href)) {
-    media_url = link_href.match(re);
-    media_minutes = media_url[2].match(/([0-9]+)m/);
-    media_seconds = media_url[2].match(/([0-9]+)s/);
-    media_url[2] = 0;
-    if (media_minutes) { media_url[2] += (media_minutes[1] - 0) * 60; }
-    if (media_seconds) { media_url[2] += (media_seconds[1] - 0); }
-    return '<iframe width="480" height="360" src="http://www.youtube.com/embed/' + media_url[1] + '?start=' + media_url[2] + '" frameborder="0" allowfullscreen></iframe>';
-  }
-  
-  // Youtube embed
-  var re = /youtu.be\/((\w|-)+)/;
-  if (re.test(link_href)) {
-    media_url = link_href.match(re);
-    return '<iframe width="480" height="360" src="http://www.youtube.com/embed/' + media_url[1] + '" frameborder="0" allowfullscreen></iframe>';
-  }
-
-  // Vimeo link
-  var re = /vimeo.com\/([0-9]+)/;
-  if (re.test(link_href)) {
-    media_url = link_href.match(re);
-    return '<iframe src="http://player.vimeo.com/video/' + media_url[1] + '?color=CF402E" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>';
-  }
-
-  // No link
-  return false;
-}
-
-// Ajax: Post votes
-$(function() {
-  $('.post-rating-actions').each(function() {
-    var action_parent = this;
-    var csrf_token = $(this).find('input[name="_csrf_token"]').val();
-    $(this).find('form').submit(function() {
-      var form = this;
-      $.post(this.action, {'_csrf_token': csrf_token}, "json").done(function(data, textStatus, jqXHR) {
-        // Reset stuff and set classess
-        $(action_parent).find('.post-score').removeClass('post-score-good post-score-bad');
-        if (data.score_total > 0) {
-          $(action_parent).find('.post-score-total').addClass('post-score-good');
-        } else if (data.score_total < 0) {
-          $(action_parent).find('.post-score-total').addClass('post-score-bad');
-        } 
-        if (data.score_upvotes > 0) {
-          $(action_parent).find('.post-score-upvotes').addClass('post-score-good');
-        }
-        if (data.score_downvotes > 0) {
-          $(action_parent).find('.post-score-downvotes').addClass('post-score-bad');
-        }
-
-        // Set votes
-        $(action_parent).find('.post-score-total').text(data.score_total);
-        $(action_parent).find('.post-score-upvotes').text(data.score_upvotes);
-        $(action_parent).find('.post-score-downvotes').text(data.score_downvotes);
-
-        // Disable and enable forms
-        if (data.user_vote == 1) {
-          $(action_parent).find('.form-upvote button').attr("disabled", "disabled");
-          $(action_parent).find('.form-downvote button').removeAttr("disabled");
-        } else {
-          $(action_parent).find('.form-upvote button').removeAttr("disabled");
-          $(action_parent).find('.form-downvote button').attr("disabled", "disabled");
-        }
-      }).fail(function() {
-        $(form).unbind();
-        $(form).trigger('submit');
-      });
-      return false;
-    });
-  });
-});
-
-// Ajax: Post reports
-$(function() {
-  $('.form-report').each(function() {
-    var action_parent = this;
-    var csrf_token = $(this).find('input[name="_csrf_token"]').val();
-    var button = $(this).find('button');
-    $(this).submit(function() {
-      var form = this;
-      $.post(form.action, {'_csrf_token': csrf_token}, "json").done(function(data, textStatus, jqXHR) {        
-        $(button).text(l_post_reported);
-        $(button).tooltip('destroy');
-        $(button).attr("title", data.message);
-        $(button).tooltip({placement: 'top', container: 'body'});
-        $(button).tooltip("show");
-        $(button).attr("disabled", "disabled");
-        setTimeout(function() {
-          $(button).tooltip('hide');
-        }, 2500);
-      }).fail(function() {
-        $(form).unbind();
-        $(form).trigger('submit');
-      });
-      return false;
-    });
-  });
+$(function () {
+  // Register tooltips
+  $('.tooltip-top').tooltip({placement: 'top', container: 'body'})
+  $('.tooltip-bottom').tooltip({placement: 'bottom', container: 'body'})
+  $('.tooltip-left').tooltip({placement: 'left', container: 'body'})
+  $('.tooltip-right').tooltip({placement: 'right', container: 'body'})
+  
+  // Register popovers
+  $('.popover-top').popover({placement: 'top'})
+  $('.popover-bottom').popover({placement: 'bottom'})
+  $('.popover-left').popover({placement: 'left'})
+  $('.popover-right').popover({placement: 'right'})
+
+  // Dont fire popovers on touch devices
+  $("[class^='tooltip-']").on('show', function (e) {
+    if ('ontouchstart' in document.documentElement) {
+      e.preventDefault();
+    }
+  });
+  
+  // Start all dropdowns
+  $('.dropdown-toggle').dropdown()
+  
+  // Dont hide clickable dropdowns
+  $('.dropdown-clickable').on('click', function (e) {
+    e.stopPropagation()
+  });
+
+  // Fancy user nav activation
+  $('#fancy-user-nav').show();
+
+  // Search form extension
+  var nav_search_form = $('#navbar-search');
+  nav_search_form.click(function() {
+    nav_search_form.addClass('open');
+  });
+
+  $('html').click(function() {
+    nav_search_form.removeClass('open');
+  });
+
+  nav_search_form.click(function(event) {
+    event.stopPropagation();
+  });
+  
+  // Checkbox Group Master
+  $('input.checkbox-master').live('click', function(){
+    if($(this).is(':checked')){
+      $('input.checkbox-member').attr("checked" ,"checked");
+    }
+    else
+    {
+      $('input.checkbox-member').removeAttr('checked');
+    }
+  });
+  
+  // Checkbox Group Member
+  $('input.checkbox-member').live('click', function(){
+    if(!$(this).is(':checked')){
+      $('input.checkbox-master').removeAttr('checked');
+    }
+  });
+  
+  // Check Confirmation on links
+  $('a.confirm').live('click', function(){
+    var decision = confirm(jQuery.data(this, 'jsconfirm'));
+    return decision
+  });
+  
+  // Check Confirmation on forms
+  $('form.confirm').live('submit', function(){
+    data = $(this).data();
+    var decision = confirm(data.jsconfirm);
+    return decision
+  });
+  
+  // Show go back link?
+  if (document.referrer
+      && document.referrer.indexOf(location.protocol + "//" + location.host) === 0
+      && document.referrer != document.url) {
+    $('.go-back').show();
+  }
+
+  // Go back one page
+  $('.go-back').on('click', function (e) {
+      history.go(-1)
+      return false;
+  })
+})
+
+function EnhancePostsMD() {
+  $(function () {
+    // Add labels to images
+    $('.markdown.js-extra img').not('.emoji').each(function() {
+      $(this).addClass('img-rounded');
+      if ($(this).attr('alt').length > 0 && $(this).attr('alt') != $(this).attr('src')) {
+        $(this).attr('title', $(this).attr('alt'));
+        $(this).tooltip({placement: 'top', container: 'body'});
+      }
+    });
+
+    // Automagically turn links into players
+    var players = new Array();
+    $('.markdown.js-extra').each(function() {
+      var post_players = 0;
+      $(this).find('a').each(function() {
+        match = link2player($.trim($(this).text()));
+        if (match && $.inArray(match, players) == -1 && players.length < 16 && post_players < 4) {
+          players.push(match);
+          post_players ++;
+          $(this).replaceWith(match);
+        }
+      });
+    });
+  });
+}
+
+// Turn link to player
+function link2player(link_href) {
+  // Youtube link
+  var re = /watch\?v=((\w|-)+)/;
+  if (re.test(link_href)) {
+    media_url = link_href.match(re);
+    return '<iframe width="480" height="360" src="http://www.youtube.com/embed/' + media_url[1] + '" frameborder="0" allowfullscreen></iframe>';
+  }
+
+  // Youtube feature=embed
+  var re = /watch\?feature=player_embedded&v=((\w|-)+)/;
+  if (re.test(link_href)) {
+    media_url = link_href.match(re);
+    return '<iframe width="480" height="360" src="http://www.youtube.com/embed/' + media_url[1] + '" frameborder="0" allowfullscreen></iframe>';
+  }
+
+  // Youtube embed with start time
+  var re = /youtu.be\/((\w|-)+)\?t=([A-Za-z0-9]+)/;
+  if (re.test(link_href)) {
+    media_url = link_href.match(re);
+    media_minutes = media_url[2].match(/([0-9]+)m/);
+    media_seconds = media_url[2].match(/([0-9]+)s/);
+    media_url[2] = 0;
+    if (media_minutes) { media_url[2] += (media_minutes[1] - 0) * 60; }
+    if (media_seconds) { media_url[2] += (media_seconds[1] - 0); }
+    return '<iframe width="480" height="360" src="http://www.youtube.com/embed/' + media_url[1] + '?start=' + media_url[2] + '" frameborder="0" allowfullscreen></iframe>';
+  }
+  
+  // Youtube embed
+  var re = /youtu.be\/((\w|-)+)/;
+  if (re.test(link_href)) {
+    media_url = link_href.match(re);
+    return '<iframe width="480" height="360" src="http://www.youtube.com/embed/' + media_url[1] + '" frameborder="0" allowfullscreen></iframe>';
+  }
+
+  // Vimeo link
+  var re = /vimeo.com\/([0-9]+)/;
+  if (re.test(link_href)) {
+    media_url = link_href.match(re);
+    return '<iframe src="http://player.vimeo.com/video/' + media_url[1] + '?color=CF402E" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>';
+  }
+
+  // No link
+  return false;
+}
+
+// Ajax: Post votes
+$(function() {
+  $('.post-rating-actions').each(function() {
+    var action_parent = this;
+    var csrf_token = $(this).find('input[name="_csrf_token"]').val();
+    $(this).find('form').submit(function() {
+      var form = this;
+      $.post(this.action, {'_csrf_token': csrf_token}, "json").done(function(data, textStatus, jqXHR) {
+        // Reset stuff and set classess
+        $(action_parent).find('.post-score').removeClass('post-score-good post-score-bad');
+        if (data.score_total > 0) {
+          $(action_parent).find('.post-score-total').addClass('post-score-good');
+        } else if (data.score_total < 0) {
+          $(action_parent).find('.post-score-total').addClass('post-score-bad');
+        } 
+        if (data.score_upvotes > 0) {
+          $(action_parent).find('.post-score-upvotes').addClass('post-score-good');
+        }
+        if (data.score_downvotes > 0) {
+          $(action_parent).find('.post-score-downvotes').addClass('post-score-bad');
+        }
+
+        // Set votes
+        $(action_parent).find('.post-score-total').text(data.score_total);
+        $(action_parent).find('.post-score-upvotes').text(data.score_upvotes);
+        $(action_parent).find('.post-score-downvotes').text(data.score_downvotes);
+
+        // Disable and enable forms
+        if (data.user_vote == 1) {
+          $(action_parent).find('.form-upvote button').attr("disabled", "disabled");
+          $(action_parent).find('.form-downvote button').removeAttr("disabled");
+        } else {
+          $(action_parent).find('.form-upvote button').removeAttr("disabled");
+          $(action_parent).find('.form-downvote button').attr("disabled", "disabled");
+        }
+      }).fail(function() {
+        $(form).unbind();
+        $(form).trigger('submit');
+      });
+      return false;
+    });
+  });
+});
+
+// Ajax: Post reports
+$(function() {
+  $('.form-report').each(function() {
+    var action_parent = this;
+    var csrf_token = $(this).find('input[name="_csrf_token"]').val();
+    var button = $(this).find('button');
+    $(this).submit(function() {
+      var form = this;
+      $.post(form.action, {'_csrf_token': csrf_token}, "json").done(function(data, textStatus, jqXHR) {        
+        $(button).text(l_post_reported);
+        $(button).tooltip('destroy');
+        $(button).attr("title", data.message);
+        $(button).tooltip({placement: 'top', container: 'body'});
+        $(button).tooltip("show");
+        $(button).attr("disabled", "disabled");
+        setTimeout(function() {
+          $(button).tooltip('hide');
+        }, 2500);
+      }).fail(function() {
+        $(form).unbind();
+        $(form).trigger('submit');
+      });
+      return false;
+    });
+  });
 });
 });

+ 171 - 171
static/cranefly/js/editor.js

@@ -1,172 +1,172 @@
-// Basic editor functions
-function storeCaret(ftext) {    
-  if (ftext.createTextRange) {
-    ftext.caretPos = document.selection.createRange().duplicate();
-  }
-}
-
-function SelectionRange(start, end) {
-  this.start = start;
-  this.end = end;
-}
-
-function getSelection(textId) {
-  ctrl = document.getElementById(textId);
-  if (document.selection) {
-    ctrl.focus();
-    var range = document.selection.createRange();
-    var length = range.text.length;
-    range.moveStart('character', -ctrl.value.length);
-    return new SelectionRange(range.text.length - length, range.text.length);
-  } else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
-    return new SelectionRange(ctrl.selectionStart, ctrl.selectionEnd);
-  }
-}
-
-function getSelectionText(textId) {
-  var ctrl = document.getElementById(textId);
-  var text = ctrl.value;
-  myRange = getSelection(textId);
-  return $.trim(text.substring(myRange.start, myRange.end));
-}
-
-function setSelection(textId, SelectionRange) {
-  ctrl = document.getElementById(textId);
-  if (ctrl.setSelectionRange) {
-    ctrl.focus();
-    ctrl.setSelectionRange(SelectionRange.start, SelectionRange.end);
-  } else if (ctrl.createTextRange) {
-    var range = ctrl.createTextRange();
-    range.collapse(true);
-    range.moveStart('character', SelectionRange.start);
-    range.moveEnd('character', SelectionRange.end);
-    range.select();
-  }
-}
-
-function _makeWrap(textId, myRange, wrap_start, wrap_end) {
-  var ctrl = document.getElementById(textId);
-  var text = ctrl.value;
-  var startText = text.substring(0, myRange.start) + wrap_start;
-  var middleText = text.substring(myRange.start, myRange.end);
-  var endText = wrap_end + text.substring(myRange.end);
-  ctrl.value = startText + middleText + endText;
-  setSelection(textId, new SelectionRange(startText.length, startText.length + middleText.length));
-}
-
-function makeWrap(textId, wrap_start, wrap_end) {
-  _makeWrap(textId, getSelection(textId), wrap_start, wrap_end);
-}
-
-function _makeReplace(textId, myRange, replacement) {
-  var ctrl = document.getElementById(textId);
-  var text = ctrl.value;
-  var startText = text.substring(0, myRange.start);
-  var middleText = text.substring(myRange.start, myRange.end);
-  var endText = text.substring(myRange.end);
-  ctrl.value = text.substring(0, myRange.start) + replacement + text.substring(myRange.end);
-  setSelection(textId, new SelectionRange(startText.length + middleText.length, startText.length + middleText.length));
-}
-
-function makeReplace(textId, replacement) {
-  _makeReplace(textId, getSelection(textId), replacement);
-}
-
-var url_pattern = new RegExp('^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$','i');
-function is_url(str) {
-  return url_pattern.test($.trim(str));
-}
-
-function extractor(query) {
-    var result = /([^\s]+)$/.exec(query);
-    if(result && result[1])
-        return result[1].trim();
-    return '';
-}
-
-// Small and nice editor functionality
-$(function() {
-  $('.editor-tools').fadeIn(600);
-  $('.editor').each(function() {
-    // Get textarea stuff
-    var textarea = $(this).find('textarea');
-    var textarea_id = $(textarea).attr('id');
-    
-    // Do we have emojis?
-    if (ed_emojis.length > 1) {
-      textarea.atwho({
-        at: ":",
-        tpl: ed_emoji_tpl,
-        data: ed_emojis_list
-      });
-    }
-
-    // Handle buttons
-    $('.editor-bold').click(function() {
-      makeWrap(textarea_id, '**', '**');
-      return false;
-    });
-    
-    $('.editor-emphasis').click(function() {
-      makeWrap(textarea_id, '*', '*');
-      return false;
-    });
-    
-    $('.editor-link').click(function() {
-      var selection = $.trim(getSelectionText(textarea_id));
-      if (is_url(selection)) {
-        var link_url = $.trim(prompt(ed_lang_enter_link_url, selection));
-        selection = false;
-      } else {
-        var link_url = $.trim(prompt(ed_lang_enter_link_url));
-      }
-
-      if (is_url(link_url)) {
-        if (selection) {
-          var link_label = $.trim(prompt(ed_lang_enter_link_label, selection));
-        } else {
-          var link_label = $.trim(prompt(ed_lang_enter_link_label));
-        }
-
-        if (link_label.length > 0) {
-          makeReplace(textarea_id, '[' + link_label + '](' + link_url + ')');
-        } else {
-          makeReplace(textarea_id, '<' + link_url + '>');
-        }
-      }
-
-      return false;
-    });
-    
-    $('.editor-image').click(function() {
-      var selection = $.trim(getSelectionText(textarea_id));
-      if (is_url(selection)) {
-        var image_url = $.trim(prompt(ed_lang_enter_image_url, selection));
-        selection = false;
-      } else {
-        var image_url = $.trim(prompt(ed_lang_enter_image_url));
-      }
-
-      if (is_url(image_url)) {
-        if (selection) {
-          var image_label = $.trim(prompt(ed_lang_enter_image_label, selection));
-        } else {
-          var image_label = $.trim(prompt(ed_lang_enter_image_label));
-        }
-
-        if (image_label.length > 0) {
-          makeReplace(textarea_id, '![' + image_label + '](' + image_url + ')');
-        } else {
-          makeReplace(textarea_id, '!(' + image_url + ')');
-        }
-      }
-
-      return false;
-    });
-    
-    $('.editor-hr').click(function() {
-      makeReplace(textarea_id, '\r\n\r\n- - - - -\r\n\r\n');
-      return false;
-    });
-  });
+// Basic editor functions
+function storeCaret(ftext) {    
+  if (ftext.createTextRange) {
+    ftext.caretPos = document.selection.createRange().duplicate();
+  }
+}
+
+function SelectionRange(start, end) {
+  this.start = start;
+  this.end = end;
+}
+
+function getSelection(textId) {
+  ctrl = document.getElementById(textId);
+  if (document.selection) {
+    ctrl.focus();
+    var range = document.selection.createRange();
+    var length = range.text.length;
+    range.moveStart('character', -ctrl.value.length);
+    return new SelectionRange(range.text.length - length, range.text.length);
+  } else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
+    return new SelectionRange(ctrl.selectionStart, ctrl.selectionEnd);
+  }
+}
+
+function getSelectionText(textId) {
+  var ctrl = document.getElementById(textId);
+  var text = ctrl.value;
+  myRange = getSelection(textId);
+  return $.trim(text.substring(myRange.start, myRange.end));
+}
+
+function setSelection(textId, SelectionRange) {
+  ctrl = document.getElementById(textId);
+  if (ctrl.setSelectionRange) {
+    ctrl.focus();
+    ctrl.setSelectionRange(SelectionRange.start, SelectionRange.end);
+  } else if (ctrl.createTextRange) {
+    var range = ctrl.createTextRange();
+    range.collapse(true);
+    range.moveStart('character', SelectionRange.start);
+    range.moveEnd('character', SelectionRange.end);
+    range.select();
+  }
+}
+
+function _makeWrap(textId, myRange, wrap_start, wrap_end) {
+  var ctrl = document.getElementById(textId);
+  var text = ctrl.value;
+  var startText = text.substring(0, myRange.start) + wrap_start;
+  var middleText = text.substring(myRange.start, myRange.end);
+  var endText = wrap_end + text.substring(myRange.end);
+  ctrl.value = startText + middleText + endText;
+  setSelection(textId, new SelectionRange(startText.length, startText.length + middleText.length));
+}
+
+function makeWrap(textId, wrap_start, wrap_end) {
+  _makeWrap(textId, getSelection(textId), wrap_start, wrap_end);
+}
+
+function _makeReplace(textId, myRange, replacement) {
+  var ctrl = document.getElementById(textId);
+  var text = ctrl.value;
+  var startText = text.substring(0, myRange.start);
+  var middleText = text.substring(myRange.start, myRange.end);
+  var endText = text.substring(myRange.end);
+  ctrl.value = text.substring(0, myRange.start) + replacement + text.substring(myRange.end);
+  setSelection(textId, new SelectionRange(startText.length + middleText.length, startText.length + middleText.length));
+}
+
+function makeReplace(textId, replacement) {
+  _makeReplace(textId, getSelection(textId), replacement);
+}
+
+var url_pattern = new RegExp('^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$','i');
+function is_url(str) {
+  return url_pattern.test($.trim(str));
+}
+
+function extractor(query) {
+    var result = /([^\s]+)$/.exec(query);
+    if(result && result[1])
+        return result[1].trim();
+    return '';
+}
+
+// Small and nice editor functionality
+$(function() {
+  $('.editor-tools').fadeIn(600);
+  $('.editor').each(function() {
+    // Get textarea stuff
+    var textarea = $(this).find('textarea');
+    var textarea_id = $(textarea).attr('id');
+    
+    // Do we have emojis?
+    if (ed_emojis.length > 1) {
+      textarea.atwho({
+        at: ":",
+        tpl: ed_emoji_tpl,
+        data: ed_emojis_list
+      });
+    }
+
+    // Handle buttons
+    $('.editor-bold').click(function() {
+      makeWrap(textarea_id, '**', '**');
+      return false;
+    });
+    
+    $('.editor-emphasis').click(function() {
+      makeWrap(textarea_id, '*', '*');
+      return false;
+    });
+    
+    $('.editor-link').click(function() {
+      var selection = $.trim(getSelectionText(textarea_id));
+      if (is_url(selection)) {
+        var link_url = $.trim(prompt(ed_lang_enter_link_url, selection));
+        selection = false;
+      } else {
+        var link_url = $.trim(prompt(ed_lang_enter_link_url));
+      }
+
+      if (is_url(link_url)) {
+        if (selection) {
+          var link_label = $.trim(prompt(ed_lang_enter_link_label, selection));
+        } else {
+          var link_label = $.trim(prompt(ed_lang_enter_link_label));
+        }
+
+        if (link_label.length > 0) {
+          makeReplace(textarea_id, '[' + link_label + '](' + link_url + ')');
+        } else {
+          makeReplace(textarea_id, '<' + link_url + '>');
+        }
+      }
+
+      return false;
+    });
+    
+    $('.editor-image').click(function() {
+      var selection = $.trim(getSelectionText(textarea_id));
+      if (is_url(selection)) {
+        var image_url = $.trim(prompt(ed_lang_enter_image_url, selection));
+        selection = false;
+      } else {
+        var image_url = $.trim(prompt(ed_lang_enter_image_url));
+      }
+
+      if (is_url(image_url)) {
+        if (selection) {
+          var image_label = $.trim(prompt(ed_lang_enter_image_label, selection));
+        } else {
+          var image_label = $.trim(prompt(ed_lang_enter_image_label));
+        }
+
+        if (image_label.length > 0) {
+          makeReplace(textarea_id, '![' + image_label + '](' + image_url + ')');
+        } else {
+          makeReplace(textarea_id, '!(' + image_url + ')');
+        }
+      }
+
+      return false;
+    });
+    
+    $('.editor-hr').click(function() {
+      makeReplace(textarea_id, '\r\n\r\n- - - - -\r\n\r\n');
+      return false;
+    });
+  });
 });
 });

+ 74 - 74
templates/500.html

@@ -1,75 +1,75 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title>Don't Panic!</title>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <style type="text/css">
-	    /*!
-		 * Bootstrap v2.0.4
-		 *
-		 * Copyright 2012 Twitter, Inc
-		 * Licensed under the Apache License v2.0
-		 * http://www.apache.org/licenses/LICENSE-2.0
-		 *
-		 * Designed and built with all the love in the world @twitter by @mdo and @fat.
-		 */
-		.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
-		.clearfix:after{clear:both;}
-		.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;}
-		.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
-		html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
-		a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
-		a:hover,a:active{outline:0;}
-		img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
-		#map_canvas img{max-width:none;}
-		body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#E1E2E3;background-color:#292B2C;text-shadow:1px 1px 0px #000}
-		a{color:#2FB8CD;text-decoration:none;}
-		a:hover{color:#4DD9EF;text-decoration:underline;}
-		.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
-		.row:after{clear:both;}
-		[class*="span"]{float:left;margin-left:20px;}
-		.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
-		.span8{width:620px;}
-		.container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
-		.container:after{clear:both;}
-		p{margin:0 0 9px;}p small{font-size:11px;color:#999999;}
-		.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
-		h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
-		h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
-		h2{margin-top:32px;font-size:24px;line-height:36px;}h2 small{font-size:18px;}
-		@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.762430939%;*margin-left:2.709239449638298%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:99.999999993%;*width:99.9468085036383%;} .row-fluid .span11{width:91.436464082%;*width:91.38327259263829%;} .row-fluid .span10{width:82.87292817100001%;*width:82.8197366816383%;} .row-fluid .span9{width:74.30939226%;*width:74.25620077063829%;} .row-fluid .span8{width:65.74585634900001%;*width:65.6926648596383%;} .row-fluid .span7{width:57.182320438000005%;*width:57.129128948638304%;} .row-fluid .span6{width:48.618784527%;*width:48.5655930376383%;} .row-fluid .span5{width:40.055248616%;*width:40.0020571266383%;} .row-fluid .span4{width:31.491712705%;*width:31.4385212156383%;} .row-fluid .span3{width:22.928176794%;*width:22.874985304638297%;} .row-fluid .span2{width:14.364640883%;*width:14.311449393638298%;} .row-fluid .span1{width:5.801104972%;*width:5.747913482638298%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.564102564%;*margin-left:2.510911074638298%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.45299145300001%;*width:91.3997999636383%;} .row-fluid .span10{width:82.905982906%;*width:82.8527914166383%;} .row-fluid .span9{width:74.358974359%;*width:74.30578286963829%;} .row-fluid .span8{width:65.81196581200001%;*width:65.7587743226383%;} .row-fluid .span7{width:57.264957265%;*width:57.2117657756383%;} .row-fluid .span6{width:48.717948718%;*width:48.6647572286383%;} .row-fluid .span5{width:40.170940171000005%;*width:40.117748681638304%;} .row-fluid .span4{width:31.623931624%;*width:31.5707401346383%;} .row-fluid .span3{width:23.076923077%;*width:23.0237315876383%;} .row-fluid .span2{width:14.529914530000001%;*width:14.4767230406383%;} .row-fluid .span1{width:5.982905983%;*width:5.929714493638298%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;} .row-fluid .thumbnails{margin-left:0;}}
-		.page-head{background-color:#0C0D0E;border-bottom:1px solid #36393A;padding:64px 0px}
-		.page-message{padding-top:8px}
-		footer{margin-top:24px;color:#666;text-shadow:1px 1px 0px #1E1F20}
-		footer a:link, footer a:active, footer a:visited{color:#125B66}
-		footer a:hover{color:#1E7E8C}
-    </style>
-  </head>
-  <body>
-  <div class="page-head">
-  	<div class="container">
-  	  <div class="row">
-  	  	<div class="span8 offset2">
-          <h1>Don't Panic!<br><small>Misago has crashed when trying to handle your request. :C</small></h1>
-        </div>
-      </div>
-    </div>
-  </div>
-  <div class="page-message">
-  	<div class="container">
-  	  <div class="row">
-  	  	<div class="span8 offset2">
-          <h2>What should I do now?</h2>
-          <p>The problem may be temporary, so let&acute;s start with trying to <a href="javascript:location.reload(true)">refresh the page</a>.</p>
-          <p>If you still see this page, that means problem is little bigger than we tought and our monkeys will need some time to fix it.</p>
-          <p>Please visit us again later,<br><strong>Thanks and see you soon!</strong></p>
-          <footer>
-            <p>This page has been generated by <a href="http://misago-project.org">Misago forum software</a></p>
-          </footer>
-        </div>
-      </div>
-    </div>
-  </div>
-  </body>
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Don't Panic!</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <style type="text/css">
+	    /*!
+		 * Bootstrap v2.0.4
+		 *
+		 * Copyright 2012 Twitter, Inc
+		 * Licensed under the Apache License v2.0
+		 * http://www.apache.org/licenses/LICENSE-2.0
+		 *
+		 * Designed and built with all the love in the world @twitter by @mdo and @fat.
+		 */
+		.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
+		.clearfix:after{clear:both;}
+		.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;}
+		.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
+		html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
+		a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+		a:hover,a:active{outline:0;}
+		img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
+		#map_canvas img{max-width:none;}
+		body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#E1E2E3;background-color:#292B2C;text-shadow:1px 1px 0px #000}
+		a{color:#2FB8CD;text-decoration:none;}
+		a:hover{color:#4DD9EF;text-decoration:underline;}
+		.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
+		.row:after{clear:both;}
+		[class*="span"]{float:left;margin-left:20px;}
+		.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+		.span8{width:620px;}
+		.container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
+		.container:after{clear:both;}
+		p{margin:0 0 9px;}p small{font-size:11px;color:#999999;}
+		.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
+		h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
+		h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
+		h2{margin-top:32px;font-size:24px;line-height:36px;}h2 small{font-size:18px;}
+		@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.762430939%;*margin-left:2.709239449638298%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:99.999999993%;*width:99.9468085036383%;} .row-fluid .span11{width:91.436464082%;*width:91.38327259263829%;} .row-fluid .span10{width:82.87292817100001%;*width:82.8197366816383%;} .row-fluid .span9{width:74.30939226%;*width:74.25620077063829%;} .row-fluid .span8{width:65.74585634900001%;*width:65.6926648596383%;} .row-fluid .span7{width:57.182320438000005%;*width:57.129128948638304%;} .row-fluid .span6{width:48.618784527%;*width:48.5655930376383%;} .row-fluid .span5{width:40.055248616%;*width:40.0020571266383%;} .row-fluid .span4{width:31.491712705%;*width:31.4385212156383%;} .row-fluid .span3{width:22.928176794%;*width:22.874985304638297%;} .row-fluid .span2{width:14.364640883%;*width:14.311449393638298%;} .row-fluid .span1{width:5.801104972%;*width:5.747913482638298%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.564102564%;*margin-left:2.510911074638298%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.45299145300001%;*width:91.3997999636383%;} .row-fluid .span10{width:82.905982906%;*width:82.8527914166383%;} .row-fluid .span9{width:74.358974359%;*width:74.30578286963829%;} .row-fluid .span8{width:65.81196581200001%;*width:65.7587743226383%;} .row-fluid .span7{width:57.264957265%;*width:57.2117657756383%;} .row-fluid .span6{width:48.717948718%;*width:48.6647572286383%;} .row-fluid .span5{width:40.170940171000005%;*width:40.117748681638304%;} .row-fluid .span4{width:31.623931624%;*width:31.5707401346383%;} .row-fluid .span3{width:23.076923077%;*width:23.0237315876383%;} .row-fluid .span2{width:14.529914530000001%;*width:14.4767230406383%;} .row-fluid .span1{width:5.982905983%;*width:5.929714493638298%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;} .row-fluid .thumbnails{margin-left:0;}}
+		.page-head{background-color:#0C0D0E;border-bottom:1px solid #36393A;padding:64px 0px}
+		.page-message{padding-top:8px}
+		footer{margin-top:24px;color:#666;text-shadow:1px 1px 0px #1E1F20}
+		footer a:link, footer a:active, footer a:visited{color:#125B66}
+		footer a:hover{color:#1E7E8C}
+    </style>
+  </head>
+  <body>
+  <div class="page-head">
+  	<div class="container">
+  	  <div class="row">
+  	  	<div class="span8 offset2">
+          <h1>Don't Panic!<br><small>Misago has crashed when trying to handle your request. :C</small></h1>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="page-message">
+  	<div class="container">
+  	  <div class="row">
+  	  	<div class="span8 offset2">
+          <h2>What should I do now?</h2>
+          <p>The problem may be temporary, so let&acute;s start with trying to <a href="javascript:location.reload(true)">refresh the page</a>.</p>
+          <p>If you still see this page, that means problem is little bigger than we tought and our monkeys will need some time to fix it.</p>
+          <p>Please visit us again later,<br><strong>Thanks and see you soon!</strong></p>
+          <footer>
+            <p>This page has been generated by <a href="http://misago-project.org">Misago forum software</a></p>
+          </footer>
+        </div>
+      </div>
+    </div>
+  </div>
+  </body>
 </html>
 </html>

+ 44 - 44
templates/_email/base.html

@@ -1,44 +1,44 @@
-{%- set style_link = "style=\"color: #08C;\"" -%}
-{%- set style_p = "style=\"padding: 0px; margin: 16px 0px;\"" -%}
-{%- set style_well = "style=\"background-color: #F0F0F0; border-radius: 3px; margin: 24px 0px; padding: 12px 16px; font-size: 16px;\"" -%}
-{%- set style_well_big = "style=\"background-color: #F0F0F0; border-radius: 3px; margin: 24px 0px; padding: 12px 16px; font-size: 18px; text-align: center;\"" -%}
-{%- set style_button = "style=\"background-color: #08C; border-radius: 3px; color: #FFF; display: block; text-align: center; margin: 24px 0px; padding: 12px 0px; text-decoration: none; font-weight: bold; font-size: 18px;\"" -%}
-<!DOCTYPE html>
-<html lang="{{ LANGUAGE_CODE }}">
-  <head>
-    <meta charset="utf-8">
-  </head>
-  <body>
-    <table width="100%">
-      <tbody>
-        <tr>
-          <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px;">
-            <table width="600" align="center">
-              <tbody>
-                <tr>
-                  <td style="padding-bottom: 8px;">
-                  	<h1 style="border-bottom: 1px solid #DDD; margin-bottom: 24px; padding: 12px 0px; font-size: 28px;">
-                  		{% trans username=user.username %}Hey, {{ username }}!{% endtrans %}
-                  		<div style="margin-top: 6px; font-size: 18px; color: #999;">{% block title %}{% endblock %}<div>
-                  	</h1>
-                  	{% block content %}{% endblock %}
-                  </td>
-                </tr>
-              </tbody>
-            </table>
-            <table width="600" align="center">
-              <tbody>
-                <tr>
-                  <td style="border-top: 1px solid #DDD; padding: 12px 0px; color: #BBB;">{% if settings.email_footnote %}
-                  	{{ settings.email_footnote|safe }}{% else %}
-                  	{% trans board_name=settings.board_name %}This e-mail was sent from {{ board_name }}.{% endtrans %}<br><a href="{{ board_address }}" {{ style_link|safe }}>{{ board_address }}</a>{% endif %}
-                  </td>
-                </tr>
-              </tbody>
-            </table>
-          </td>
-        </tr>
-      </tbody>
-    </table>
-  </body>
-</html>
+{%- set style_link = "style=\"color: #08C;\"" -%}
+{%- set style_p = "style=\"padding: 0px; margin: 16px 0px;\"" -%}
+{%- set style_well = "style=\"background-color: #F0F0F0; border-radius: 3px; margin: 24px 0px; padding: 12px 16px; font-size: 16px;\"" -%}
+{%- set style_well_big = "style=\"background-color: #F0F0F0; border-radius: 3px; margin: 24px 0px; padding: 12px 16px; font-size: 18px; text-align: center;\"" -%}
+{%- set style_button = "style=\"background-color: #08C; border-radius: 3px; color: #FFF; display: block; text-align: center; margin: 24px 0px; padding: 12px 0px; text-decoration: none; font-weight: bold; font-size: 18px;\"" -%}
+<!DOCTYPE html>
+<html lang="{{ LANGUAGE_CODE }}">
+  <head>
+    <meta charset="utf-8">
+  </head>
+  <body>
+    <table width="100%">
+      <tbody>
+        <tr>
+          <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px;">
+            <table width="600" align="center">
+              <tbody>
+                <tr>
+                  <td style="padding-bottom: 8px;">
+                  	<h1 style="border-bottom: 1px solid #DDD; margin-bottom: 24px; padding: 12px 0px; font-size: 28px;">
+                  		{% trans username=user.username %}Hey, {{ username }}!{% endtrans %}
+                  		<div style="margin-top: 6px; font-size: 18px; color: #999;">{% block title %}{% endblock %}<div>
+                  	</h1>
+                  	{% block content %}{% endblock %}
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+            <table width="600" align="center">
+              <tbody>
+                <tr>
+                  <td style="border-top: 1px solid #DDD; padding: 12px 0px; color: #BBB;">{% if settings.email_footnote %}
+                  	{{ settings.email_footnote|safe }}{% else %}
+                  	{% trans board_name=settings.board_name %}This e-mail was sent from {{ board_name }}.{% endtrans %}<br><a href="{{ board_address }}" {{ style_link|safe }}>{{ board_address }}</a>{% endif %}
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </body>
+</html>

+ 9 - 9
templates/_email/base.txt

@@ -1,10 +1,10 @@
-{% trans username=user.username %}Hey, {{ username }}!{% endtrans %}
-{% block title %}{% endblock %}
-========================================
-{% block content %}{% endblock %}
-
-----------------------------------------
-{% if settings.email_footnote_plain -%}
-{{ settings.email_footnote_plain }}{% else -%}
-{% trans board_name=settings.board_name %}This e-mail was sent from {{ board_name }}.{% endtrans %}
+{% trans username=user.username %}Hey, {{ username }}!{% endtrans %}
+{% block title %}{% endblock %}
+========================================
+{% block content %}{% endblock %}
+
+----------------------------------------
+{% if settings.email_footnote_plain -%}
+{{ settings.email_footnote_plain }}{% else -%}
+{% trans board_name=settings.board_name %}This e-mail was sent from {{ board_name }}.{% endtrans %}
 {{ settings.board_name }} - {{ board_address }}{% endif %}
 {{ settings.board_name }} - {{ board_address }}{% endif %}

+ 8 - 8
templates/_email/private_thread_invite.html

@@ -1,9 +1,9 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans %}You've been invited to private thread{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has invited you to participate in private thread "{{ thread }}".{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}You can see this thread by clicking link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}" {{ style_link|safe }}>{{ board_address }}{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}</a>
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans %}You've been invited to private thread{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has invited you to participate in private thread "{{ thread }}".{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}You can see this thread by clicking link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}" {{ style_link|safe }}>{{ board_address }}{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}</a>
 {% endblock %}
 {% endblock %}

+ 9 - 9
templates/_email/private_thread_invite.txt

@@ -1,10 +1,10 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans %}You've been invited to private thread{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has invited you to participate in private thread "{{ thread }}".{% endtrans %}
-
-{% trans %}You can see this thread by clicking link below:{% endtrans %}
-{{ board_address }}{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans %}You've been invited to private thread{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has invited you to participate in private thread "{{ thread }}".{% endtrans %}
+
+{% trans %}You can see this thread by clicking link below:{% endtrans %}
+{{ board_address }}{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}
 {% endblock %}
 {% endblock %}

+ 8 - 8
templates/_email/private_thread_reply_notification.html

@@ -1,9 +1,9 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to private thread "{{ thread }}" that you are watching.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}To go to this reply follow the link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" {{ style_link|safe }}>{{ board_address }}{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}</a>
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to private thread "{{ thread }}" that you are watching.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}To go to this reply follow the link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" {{ style_link|safe }}>{{ board_address }}{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}</a>
 {% endblock %}
 {% endblock %}

+ 9 - 9
templates/_email/private_thread_reply_notification.txt

@@ -1,10 +1,10 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to private thread "{{ thread }}" that you are watching.{% endtrans %}
-
-{% trans %}To go to this reply follow the link below:{% endtrans %}
-{{ board_address }}{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to private thread "{{ thread }}" that you are watching.{% endtrans %}
+
+{% trans %}To go to this reply follow the link below:{% endtrans %}
+{{ board_address }}{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}
 {% endblock %}
 {% endblock %}

+ 8 - 8
templates/_email/report_reply_notification.html

@@ -1,9 +1,9 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to report "{{ thread }}" that you are watching.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}To go to this reply follow the link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" {{ style_link|safe }}>{{ board_address }}{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}</a>
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to report "{{ thread }}" that you are watching.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}To go to this reply follow the link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" {{ style_link|safe }}>{{ board_address }}{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}</a>
 {% endblock %}
 {% endblock %}

+ 9 - 9
templates/_email/report_reply_notification.txt

@@ -1,10 +1,10 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to report "{{ thread }}" that you are watching.{% endtrans %}
-
-{% trans %}To go to this reply follow the link below:{% endtrans %}
-{{ board_address }}{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to report "{{ thread }}" that you are watching.{% endtrans %}
+
+{% trans %}To go to this reply follow the link below:{% endtrans %}
+{{ board_address }}{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}
 {% endblock %}
 {% endblock %}

+ 8 - 8
templates/_email/thread_reply_notification.html

@@ -1,9 +1,9 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to thread "{{ thread }}" that you are watching.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}To go to this reply follow the link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" {{ style_link|safe }}>{{ board_address }}{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}</a>
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to thread "{{ thread }}" that you are watching.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}To go to this reply follow the link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" {{ style_link|safe }}>{{ board_address }}{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}</a>
 {% endblock %}
 {% endblock %}

+ 9 - 9
templates/_email/thread_reply_notification.txt

@@ -1,10 +1,10 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to thread "{{ thread }}" that you are watching.{% endtrans %}
-
-{% trans %}To go to this reply follow the link below:{% endtrans %}
-{{ board_address }}{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans %}New reply notification{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username, author=author.username, thread=thread.name %}{{ username }}, you are receiving this message because {{ author }} has replied to thread "{{ thread }}" that you are watching.{% endtrans %}
+
+{% trans %}To go to this reply follow the link below:{% endtrans %}
+{{ board_address }}{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}
 {% endblock %}
 {% endblock %}

+ 6 - 6
templates/_email/users/activation/admin.html

@@ -1,7 +1,7 @@
-{% extends "_email/base.html" %}
-
-{% block content %}
-{{ super() }}
-
-<p {{ style_p|safe }}>{% trans %}Your account will remain inactive until Board Administrator accepts it. Depending on number of new registrations this may take few minutes or few days. Thanks for your patience!{% endtrans %}</p>
+{% extends "_email/base.html" %}
+
+{% block content %}
+{{ super() }}
+
+<p {{ style_p|safe }}>{% trans %}Your account will remain inactive until Board Administrator accepts it. Depending on number of new registrations this may take few minutes or few days. Thanks for your patience!{% endtrans %}</p>
 {% endblock %}
 {% endblock %}

+ 6 - 6
templates/_email/users/activation/admin.txt

@@ -1,7 +1,7 @@
-{% extends "_email/users/activation/none.txt" %}
-
-{% block content %}
-{{ super() }}
-
-{% trans %}Your account will remain inactive until Board Administrator accepts it. Depending on number of new registrations this may take few minutes or few days. Thanks for your patience!{% endtrans %}
+{% extends "_email/users/activation/none.txt" %}
+
+{% block content %}
+{{ super() }}
+
+{% trans %}Your account will remain inactive until Board Administrator accepts it. Depending on number of new registrations this may take few minutes or few days. Thanks for your patience!{% endtrans %}
 {% endblock %}
 {% endblock %}

+ 10 - 10
templates/_email/users/activation/admin_done.html

@@ -1,11 +1,11 @@
-{% extends "_email/users/activation/none.html" %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has activated your account.{% endtrans %}</p>
-
-<p {{ style_p|safe }}>{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_button|safe }}>{% trans %}Sign In{% endtrans %}</a>
-
-<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_link|safe }}>{{ board_address }}{{ url('sign_in') }}</a>
+{% extends "_email/users/activation/none.html" %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has activated your account.{% endtrans %}</p>
+
+<p {{ style_p|safe }}>{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_button|safe }}>{% trans %}Sign In{% endtrans %}</a>
+
+<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_link|safe }}>{{ board_address }}{{ url('sign_in') }}</a>
 {% endblock %}
 {% endblock %}

+ 7 - 7
templates/_email/users/activation/admin_done.txt

@@ -1,8 +1,8 @@
-{% extends "_email/users/activation/none.txt" %}
-
-{% block content %}
-{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has activated your account.{% endtrans %}
-
-{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}
-{{ board_address }}{{ url('sign_in') }}
+{% extends "_email/users/activation/none.txt" %}
+
+{% block content %}
+{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has activated your account.{% endtrans %}
+
+{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}
+{{ board_address }}{{ url('sign_in') }}
 {% endblock %}
 {% endblock %}

+ 11 - 11
templates/_email/users/activation/invalidated.html

@@ -1,12 +1,12 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Account Activation on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has requested you to revalidate your e-mail address.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}To reactivate your account click the link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_button|safe }}>{% trans %}Activate my account!{% endtrans %}</a>
-
-<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}</a>
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Account Activation on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has requested you to revalidate your e-mail address.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}To reactivate your account click the link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_button|safe }}>{% trans %}Activate my account!{% endtrans %}</a>
+
+<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}</a>
 {% endblock %}
 {% endblock %}

+ 9 - 9
templates/_email/users/activation/invalidated.txt

@@ -1,10 +1,10 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Account Activation on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has requested you to revalidate your e-mail address.{% endtrans %}
-
-{% trans %}To reactivate your account click the link below:{% endtrans %}
-{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Account Activation on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has requested you to revalidate your e-mail address.{% endtrans %}
+
+{% trans %}To reactivate your account click the link below:{% endtrans %}
+{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}
 {% endblock %}
 {% endblock %}

+ 15 - 15
templates/_email/users/activation/none.html

@@ -1,16 +1,16 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Welcome aboard {{ board_name }}!{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have used this email address to sign up on our forums.{% endtrans %}</p>
-
-<div {{ style_well|safe }}>
-  <p {{ style_p|safe }}><b>{% trans %}Username{% endtrans %}:</b><br>{{ user.username }}</p>
-  <p {{ style_p|safe }}><b>{% trans %}E-mail{% endtrans %}:</b><br>{{ user.email }}</p>{% if settings.password_in_email %}
-  <p {{ style_p|safe }}><b>{% trans %}Password{% endtrans %}:</b><br>{{ password }}</p>{% endif %}
-</div>
-{% if settings.password_in_email %}
-<p {{ style_p|safe }}>{% trans %}This is only time you will receive your current password. For security reasons we don't store members passwords as they are entered on registration, instead we encrypt them in unreversible manner to keep them safe.{% endtrans %}</p>
-{% endif %}
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Welcome aboard {{ board_name }}!{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have used this email address to sign up on our forums.{% endtrans %}</p>
+
+<div {{ style_well|safe }}>
+  <p {{ style_p|safe }}><b>{% trans %}Username{% endtrans %}:</b><br>{{ user.username }}</p>
+  <p {{ style_p|safe }}><b>{% trans %}E-mail{% endtrans %}:</b><br>{{ user.email }}</p>{% if settings.password_in_email %}
+  <p {{ style_p|safe }}><b>{% trans %}Password{% endtrans %}:</b><br>{{ password }}</p>{% endif %}
+</div>
+{% if settings.password_in_email %}
+<p {{ style_p|safe }}>{% trans %}This is only time you will receive your current password. For security reasons we don't store members passwords as they are entered on registration, instead we encrypt them in unreversible manner to keep them safe.{% endtrans %}</p>
+{% endif %}
 {% endblock %}
 {% endblock %}

+ 14 - 14
templates/_email/users/activation/none.txt

@@ -1,15 +1,15 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Welcome aboard {{ board_name }}!{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username %}{{ username }}, you are receiving this message because you have used this email address to sign up on our forums.{% endtrans %}
-
-Your Account Data
------------------
-{% trans %}Username{% endtrans %}:    {{ user.username }}
-{% trans %}E-mail{% endtrans %}:    {{ user.email }}{% if settings.password_in_email %}
-{% trans %}Password{% endtrans %}:    {{ password }}
-
-{% trans %}This is only time you will receive your current password. Due to security reasons we don't store members passwords as they are entered on registration, instead we encrypt those password in un-reversible manner to keep them safe.{% endtrans %}{% endif %}{% block activation_instructions %}{% endblock %}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Welcome aboard {{ board_name }}!{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username %}{{ username }}, you are receiving this message because you have used this email address to sign up on our forums.{% endtrans %}
+
+Your Account Data
+-----------------
+{% trans %}Username{% endtrans %}:    {{ user.username }}
+{% trans %}E-mail{% endtrans %}:    {{ user.email }}{% if settings.password_in_email %}
+{% trans %}Password{% endtrans %}:    {{ password }}
+
+{% trans %}This is only time you will receive your current password. Due to security reasons we don't store members passwords as they are entered on registration, instead we encrypt those password in un-reversible manner to keep them safe.{% endtrans %}{% endif %}{% block activation_instructions %}{% endblock %}
 {% endblock %}
 {% endblock %}

+ 11 - 11
templates/_email/users/activation/resend.html

@@ -1,12 +1,12 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Account Activation on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested new activation e-mail.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}To activate your account, click the link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_button|safe }}>{% trans %}Activate my account!{% endtrans %}</a>
-
-<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}</a>
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Account Activation on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested new activation e-mail.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}To activate your account, click the link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_button|safe }}>{% trans %}Activate my account!{% endtrans %}</a>
+
+<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}</a>
 {% endblock %}
 {% endblock %}

+ 9 - 9
templates/_email/users/activation/resend.txt

@@ -1,10 +1,10 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Account Activation on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested new activation e-mail.{% endtrans %}
-
-{% trans %}To activate your account, click the link below:{% endtrans %}
-{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Account Activation on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested new activation e-mail.{% endtrans %}
+
+{% trans %}To activate your account, click the link below:{% endtrans %}
+{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}
 {% endblock %}
 {% endblock %}

+ 11 - 11
templates/_email/users/activation/user.html

@@ -1,12 +1,12 @@
-{% extends "_email/users/activation/none.html" %}
-
-{% block content %}
-{{ super() }}
-
-<p {{ style_p|safe }}>{% trans %}We require our members to prove validity of e-mail address used during registration. To prove that you are owner of e-mail address used to create this account, click the link below:{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}To activate your account, click the link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_button|safe }}>{% trans %}Activate my account!{% endtrans %}</a>
-
-<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}</a>
+{% extends "_email/users/activation/none.html" %}
+
+{% block content %}
+{{ super() }}
+
+<p {{ style_p|safe }}>{% trans %}We require our members to prove validity of e-mail address used during registration. To prove that you are owner of e-mail address used to create this account, click the link below:{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}To activate your account, click the link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_button|safe }}>{% trans %}Activate my account!{% endtrans %}</a>
+
+<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}</a>
 {% endblock %}
 {% endblock %}

+ 7 - 7
templates/_email/users/activation/user.txt

@@ -1,8 +1,8 @@
-{% extends "_email/users/activation/none.txt" %}
-
-{% block content %}
-{{ super() }}
-
-{% trans %}We require our members to prove validity of e-mail address used during registration. To prove that you are owner of e-mail address used to create this account, click the link below:{% endtrans %}
-{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}
+{% extends "_email/users/activation/none.txt" %}
+
+{% block content %}
+{{ super() }}
+
+{% trans %}We require our members to prove validity of e-mail address used during registration. To prove that you are owner of e-mail address used to create this account, click the link below:{% endtrans %}
+{{ board_address }}{{ url('activate', username=user.username_slug, user=user.id, token=user.token) }}
 {% endblock %}
 {% endblock %}

+ 12 - 12
templates/_email/users/new_credentials.html

@@ -1,13 +1,13 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Activate new Sign-In Credentials on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have changed your acount's sign-in credentials.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}To confirm that you want to change your account's sign-in credentials with new ones click the link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('usercp_credentials_activate', token=token) }}" {{ style_button|safe }}>{% trans %}Activate New Credentials{% endtrans %}</a>
-
-<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('usercp_credentials_activate', token=token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('usercp_credentials_activate', token=token) }}</a>
-
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Activate new Sign-In Credentials on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have changed your acount's sign-in credentials.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}To confirm that you want to change your account's sign-in credentials with new ones click the link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('usercp_credentials_activate', token=token) }}" {{ style_button|safe }}>{% trans %}Activate New Credentials{% endtrans %}</a>
+
+<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('usercp_credentials_activate', token=token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('usercp_credentials_activate', token=token) }}</a>
+
 {% endblock %}
 {% endblock %}

+ 9 - 9
templates/_email/users/new_credentials.txt

@@ -1,10 +1,10 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Activate new Sign-In Credentials on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username %}{{ username }}, you are receiving this message because you have changed your acount's sign-in credentials.{% endtrans %}
-
-{% trans %}To confirm that you want to change your account's sign-in credentials with new ones click the link below:{% endtrans %}
-{{ board_address }}{{ url('usercp_credentials_activate', token=token) }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Activate new Sign-In Credentials on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username %}{{ username }}, you are receiving this message because you have changed your acount's sign-in credentials.{% endtrans %}
+
+{% trans %}To confirm that you want to change your account's sign-in credentials with new ones click the link below:{% endtrans %}
+{{ board_address }}{{ url('usercp_credentials_activate', token=token) }}
 {% endblock %}
 {% endblock %}

+ 6 - 6
templates/_email/users/newsletter.html

@@ -1,7 +1,7 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{{ subject }}{% endblock %}
-
-{% block content %}
-{{ content_html|safe }}
+{% extends "_email/base.html" %}
+
+{% block title %}{{ subject }}{% endblock %}
+
+{% block content %}
+{{ content_html|safe }}
 {% endblock %}
 {% endblock %}

+ 6 - 6
templates/_email/users/newsletter.txt

@@ -1,7 +1,7 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{{ subject }}{% endblock %}
-
-{% block content %}
-{{ content_plain }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{{ subject }}{% endblock %}
+
+{% block content %}
+{{ content_plain }}
 {% endblock %}
 {% endblock %}

+ 14 - 14
templates/_email/users/password/confirm.html

@@ -1,15 +1,15 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Confirm New Password Request on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}To confirm that you want to reset your account's password with new one click the link below:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('reset_password', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_button|safe }}>{% trans %}Reset my password{% endtrans %}</a>
-
-<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('reset_password', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('reset_password', username=user.username_slug, user=user.id, token=user.token) }}</a>
-
-<p {{ style_p|safe }}>{% trans %}Your new password will be sent back to you in next message once you click confirmation link.{% endtrans %}</p>
-
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Confirm New Password Request on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}To confirm that you want to reset your account's password with new one click the link below:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('reset_password', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_button|safe }}>{% trans %}Reset my password{% endtrans %}</a>
+
+<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('reset_password', username=user.username_slug, user=user.id, token=user.token) }}" {{ style_link|safe }}>{{ board_address }}{{ url('reset_password', username=user.username_slug, user=user.id, token=user.token) }}</a>
+
+<p {{ style_p|safe }}>{% trans %}Your new password will be sent back to you in next message once you click confirmation link.{% endtrans %}</p>
+
 {% endblock %}
 {% endblock %}

+ 11 - 11
templates/_email/users/password/confirm.txt

@@ -1,12 +1,12 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Confirm New Password Request on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}
-
-{% trans %}To confirm that you want to reset your account's password with new one click the link below:{% endtrans %}
-{{ board_address }}{{ url('reset_password', username=user.username_slug, user=user.id, token=user.token) }}
-
-{% trans %}Your new password will be sent back to you in next message once you click confirmation link.{% endtrans %}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Confirm New Password Request on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}
+
+{% trans %}To confirm that you want to reset your account's password with new one click the link below:{% endtrans %}
+{{ board_address }}{{ url('reset_password', username=user.username_slug, user=user.id, token=user.token) }}
+
+{% trans %}Your new password will be sent back to you in next message once you click confirmation link.{% endtrans %}
 {% endblock %}
 {% endblock %}

+ 15 - 15
templates/_email/users/password/new.html

@@ -1,16 +1,16 @@
-{% extends "_email/base.html" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Your New Password on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}Your new password:{% endtrans %}</p>
-<div {{ style_well_big|safe }}>
-  {{ password }}
-</div>
-<p {{ style_p|safe }}>{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_button|safe }}>{% trans %}Sign In{% endtrans %}</a>
-
-<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_link|safe }}>{{ board_address }}{{ url('sign_in') }}</a>
+{% extends "_email/base.html" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Your New Password on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}Your new password:{% endtrans %}</p>
+<div {{ style_well_big|safe }}>
+  {{ password }}
+</div>
+<p {{ style_p|safe }}>{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_button|safe }}>{% trans %}Sign In{% endtrans %}</a>
+
+<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_link|safe }}>{{ board_address }}{{ url('sign_in') }}</a>
 {% endblock %}
 {% endblock %}

+ 11 - 11
templates/_email/users/password/new.txt

@@ -1,12 +1,12 @@
-{% extends "_email/base.txt" %}
-
-{% block title %}{% trans board_name=settings.board_name %}Your New Password on {{ board_name }}{% endtrans %}{% endblock %}
-
-{% block content %}
-{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}
-
-{% trans %}Your new password:{% endtrans %} {{ password }}
-
-{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}
-{{ board_address }}{{ url('sign_in') }}
+{% extends "_email/base.txt" %}
+
+{% block title %}{% trans board_name=settings.board_name %}Your New Password on {{ board_name }}{% endtrans %}{% endblock %}
+
+{% block content %}
+{% trans username=user.username %}{{ username }}, you are receiving this message because you have requested for new password to be generated and set on your account.{% endtrans %}
+
+{% trans %}Your new password:{% endtrans %} {{ password }}
+
+{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}
+{{ board_address }}{{ url('sign_in') }}
 {% endblock %}
 {% endblock %}

+ 13 - 13
templates/_email/users/password/new_admin.html

@@ -1,14 +1,14 @@
-{% extends "_email/users/password/new.html" %}
-
-{% block content %}
-<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has reset your account's password with new one.{% endtrans %}</p>
-<p {{ style_p|safe }}>{% trans %}Your new password:{% endtrans %}</p>
-<div {{ style_well_big|safe }}>
-  {{ password }}
-</div>
-<p {{ style_p|safe }}>{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_button|safe }}>{% trans %}Sign In{% endtrans %}</a>
-
-<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
-<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_link|safe }}>{{ board_address }}{{ url('sign_in') }}</a>
+{% extends "_email/users/password/new.html" %}
+
+{% block content %}
+<p {{ style_p|safe }}>{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has reset your account's password with new one.{% endtrans %}</p>
+<p {{ style_p|safe }}>{% trans %}Your new password:{% endtrans %}</p>
+<div {{ style_well_big|safe }}>
+  {{ password }}
+</div>
+<p {{ style_p|safe }}>{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_button|safe }}>{% trans %}Sign In{% endtrans %}</a>
+
+<p {{ style_p|safe }}>{% trans %}If the above link is not clickable, copy and paste this link into your web browser's address bar:{% endtrans %}</p>
+<a href="{{ board_address }}{{ url('sign_in') }}" {{ style_link|safe }}>{{ board_address }}{{ url('sign_in') }}</a>
 {% endblock %}
 {% endblock %}

+ 9 - 9
templates/_email/users/password/new_admin.txt

@@ -1,10 +1,10 @@
-{% extends "_email/users/password/new.txt" %}
-
-{% block content %}
-{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has reset your account's password with new one.{% endtrans %}
-
-{% trans %}Your new password:{% endtrans %} {{ password }}
-
-{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}
-{{ board_address }}{{ url('sign_in') }}
+{% extends "_email/users/password/new.txt" %}
+
+{% block content %}
+{% trans username=user.username %}{{ username }}, you are receiving this message because board administrator has reset your account's password with new one.{% endtrans %}
+
+{% trans %}Your new password:{% endtrans %} {{ password }}
+
+{% trans %}You can sign in to your account using new password by following this link:{% endtrans %}
+{{ board_address }}{{ url('sign_in') }}
 {% endblock %}
 {% endblock %}

+ 213 - 213
templates/_forms.html

@@ -1,214 +1,214 @@
-{# Render whole form macro #}
-{%- macro form_widget(form, horizontal=false, width=12) -%}
-<fieldset class="first{% if form.fieldsets|length == 0 %} last{% endif %}">
-  {{ form_hidden_widget(form) }}
-  {% for fieldset in form.fieldsets %}{% if fieldset.legend %}
-  <legend><div>{{ fieldset.legend }}{% if fieldset.help %} <span>{{ fieldset.help }}</span>{% endif %}</div></legend>{% endif %}
-  {% for field in fieldset.fields %}
-    {{ row_widget(field, horizontal=horizontal, width=width) }}
-  {% endfor %}
-</fieldset>{% if not fieldset.last %}
-<fieldset{% if loop.revindex0 == 1 %} class="last"{% endif %}>{% endif %}{% endfor %}
-{%- endmacro -%}
-
-{# Render hidden fields macro #}
-{%- macro form_hidden_widget(form) -%}
-  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">{% for field in form.hidden %}
-  <input type="hidden" name="{{ field.id }}" value="{{ field.value }}">{% endfor %}
-{%- endmacro -%}
-
-{# Render form row macro #}
-{%- macro row_widget(field, horizontal=false, width=12) -%}
-  <div class="control-group{% if field.errors %} error{% endif %}">{% if field.label %}
-    <label class="control-label" for="{{ field.html_id }}">{{ field.label }}:</label>{% endif %}{% if field.nested %}
-    <div class="controls controls-nested">
-    	<div class="row">
-      {% for subfield in field.nested %}                    	
-    	  <div class="span{{ widthratio(subfield.width, 100, width) }}">{{ field_widget(subfield, horizontal=horizontal, width=width, nested=true) }}</div>
-      {% endfor %}
-      </div>{% for error in field.errors %}
-      <p class="help-block" style="font-weight: bold;">{{ error }}</p>{% endfor %}{% if field.help_text %}
-      <p class="help-block">{{ field.help_text }}</p>{% endif %}
-    </div>{% else %}
-    <div class="controls">
-      {{ field_widget(field, horizontal=horizontal, width=width) }}{% for error in field.errors %}
-      <p class="help-block" style="font-weight: bold;">{{ error }}</p>{% endfor %}{% if field.help_text %}
-      <p class="help-block">{{ field.help_text }}</p>{% endif %}
-    </div>{% endif %}
-  </div>
-{%- endmacro -%}
-
-{# Render form field macro #}
-{%- macro field_widget(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-
-{%- if field.widget == "checkbox" -%}
-{{ input_checkbox(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "date" -%}
-{{ input_date(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif %}
-
-{%- if field.widget == "file_clearable" -%}
-{{ input_file_clearable(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "forumTos" -%}
-{{ input_forum_tos(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "recaptcha" -%}
-{{ input_recaptcha(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "radio_select" -%}
-{{ input_radio_select(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "select" -%}
-{{ input_select(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "checkbox_select_multiple" -%}
-{{ input_checkbox_select_multiple(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "text" -%}
-{{ input_text(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "textarea" -%}
-{{ input_textarea(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif -%}
-
-{%- if field.widget == "yes_no_switch" -%}
-{{ input_yes_no_switch(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
-{%- endif %}
-{%- endmacro -%}
-
-
-{# Render form field attributes macro #}
-{%- macro field_attrs(attrs={}, extras=[]) -%}
-{% for attribute in attrs %} {{ attribute }}="{{ attrs[attribute] }}"{% endfor %}{% for extra in extras %} {{ extra }}{% endfor %}
-{%- endmacro -%}
-
-
-{# Render form field class attribute macro #}
-{%- macro field_classes(classes=[]) -%}
-{% if classes %} class="{% for class in classes %}{% if not loop.first %} {% endif %}{{ class }}{% endfor %}"{% endif %}
-{%- endmacro -%}
-
-
-{# Checkbox input #}
-{%- macro input_checkbox(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-<label class="checkbox">
-  <input type="checkbox" name="{{ field.html_name }}" id="{{ field.html_id }}" value="1"{% if field.value %} checked="checked"{% endif %}>
-  {% if field.inline is defined %}{{ field.inline }}{% else %}{{ field.label }}{% endif %}
-</label>
-{%- endmacro -%}
-
-{# Forum Terms of Service input #}
-{%- macro input_forum_tos(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-<label class="checkbox">
-  <input type="checkbox" name="{{ field.html_name }}" id="{{ field.html_id }}" value="1"{% if field.value %} checked="checked"{% endif %}>
-  {% trans forum_tos=make_tos()|safe %}I have read and accept this forums {{forum_tos}}.{% endtrans %}
-</label>
-{%- endmacro -%}
-{%- macro make_tos() -%}
-<a href="{% if settings.tos_url %}{{ settings.tos_url }}{% else %}{{ url('tos') }}{% endif %}">{% if settings.tos_title %}{{ settings.tos_title }}{% else %}{% trans %}Terms of Service{% endtrans %}{% endif %}</a>
-{%- endmacro -%}
-
-{# Date input #}
-{%- macro input_date(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-{%- do field.attrs.update(attrs) -%}
-{%- if horizontal -%}
-  {%- do classes.append('span' ~ (widthratio(field.width, 100, width) - 2)) -%}
-{%- else -%}
-  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
-{%- endif -%}
-<input type="text"{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}{% if field.has_value %} value="{{ field.value }}"{% endif %}>
-{%- endmacro -%}
-
-{# Multiple Checkbox input #}
-{%- macro input_checkbox_select_multiple(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-{%- do field.attrs.update(attrs) -%}
-{%- do classes.append('select-multiple') -%}
-<div{{ field_classes(classes) }}>{% for choice in field.choices %}
-  <label class="checkbox">
-    <input type="checkbox" name="{{ field.html_name }}" id="{{ field.html_id }}_{{ choice[0] }}" value="{{ choice[0] }}"{% if field.value and choice[0] in field.value %} checked="checked"{% endif %}>
-    {{ choice[1] }}
-  </label>{% endfor %}
-</div>
-{%- endmacro -%}
-
-
-{# File Upload input #}
-{%- macro input_file_clearable(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-<input type="file" name="{{ field.html_name }}" id="{{ field.html_id }}" > 
-{%- endmacro -%}
-
-
-{# Recaptcha input #}
-{%- macro input_recaptcha(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-{{ field.attrs.html|safe }}
-{%- endmacro -%}
-
-
-{# RadioSelect input #}
-{%- macro input_radio_select(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-{%- do field.attrs.update(attrs) -%}
-{%- do classes.append('radio-group') -%}
-<div{{ field_classes(classes) }}>{% for choice in field.choices %}
-  <label class="radio">
-    <input type="radio" name="{{ field.html_name }}" id="{{ field.html_id }}{{ choice[0] }}" value="{{ choice[0] }}"{% if field.value == choice[0] %} checked="checked"{% endif %}>
-    {{ choice[1] }}
-  </label>{% endfor %}
-</div>
-{%- endmacro -%}
-
-
-{# Select input #}
-{%- macro input_select(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-{%- do field.attrs.update(attrs) -%}
-{%- if horizontal %}
-  {%- do classes.append('span' ~ (widthratio(field.width, 100, width) - 2)) -%}
-{%- else -%}
-  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
-{%- endif -%}
-<select{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}>{% for choice in field.choices %}
-  <option value="{{ choice[0] }}"{% if field.value == choice[0] %} selected="selected"{% endif %}>{{ choice[1] }}</option>{% endfor %}
-</select>
-{%- endmacro -%}
-
-
-{# Text/password input #}
-{%- macro input_text(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-{%- do field.attrs.update(attrs) -%}
-{%- if horizontal -%}
-  {%- do classes.append('span' ~ (widthratio(field.width, 100, width) - 2)) -%}
-{%- else -%}
-  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
-{%- endif -%}
-<input{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}{% if field.attrs.type != 'password' and field.has_value %} value="{{ field.value }}"{% endif %}>
-{%- endmacro -%}
-
-
-{# Textarea input #}
-{%- macro input_textarea(field, attrs={'rows': 4}, classes=[], horizontal=false, width=12, nested=false) -%}
-{%- do field.attrs.update(attrs) -%}
-{%- if horizontal -%}
-  {%- do classes.append('span' ~ (widthratio(field.width, 100, width) - 2)) -%}
-{%- else -%}
-  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
-{%- endif -%}
-<textarea{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}>{% if field.has_value %}{{ field.value }}{% endif %}</textarea>
-{%- endmacro -%}
-
-{# YesNoSwitch input #}
-{%- macro input_yes_no_switch(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
-{%- do field.attrs.update(attrs) -%}
-{%- do classes.append('yes-no-switch') -%}
-<div{{ field_classes(classes) }} id="{{ field.html_id }}_div">
-  <input name="{{ field.html_name }}" id="{{ field.html_id }}" type="checkbox" value="1"{% if field.value %} checked="checked"{% endif %}>
-</div>
+{# Render whole form macro #}
+{%- macro form_widget(form, horizontal=false, width=12) -%}
+<fieldset class="first{% if form.fieldsets|length == 0 %} last{% endif %}">
+  {{ form_hidden_widget(form) }}
+  {% for fieldset in form.fieldsets %}{% if fieldset.legend %}
+  <legend><div>{{ fieldset.legend }}{% if fieldset.help %} <span>{{ fieldset.help }}</span>{% endif %}</div></legend>{% endif %}
+  {% for field in fieldset.fields %}
+    {{ row_widget(field, horizontal=horizontal, width=width) }}
+  {% endfor %}
+</fieldset>{% if not fieldset.last %}
+<fieldset{% if loop.revindex0 == 1 %} class="last"{% endif %}>{% endif %}{% endfor %}
+{%- endmacro -%}
+
+{# Render hidden fields macro #}
+{%- macro form_hidden_widget(form) -%}
+  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">{% for field in form.hidden %}
+  <input type="hidden" name="{{ field.id }}" value="{{ field.value }}">{% endfor %}
+{%- endmacro -%}
+
+{# Render form row macro #}
+{%- macro row_widget(field, horizontal=false, width=12) -%}
+  <div class="control-group{% if field.errors %} error{% endif %}">{% if field.label %}
+    <label class="control-label" for="{{ field.html_id }}">{{ field.label }}:</label>{% endif %}{% if field.nested %}
+    <div class="controls controls-nested">
+    	<div class="row">
+      {% for subfield in field.nested %}                    	
+    	  <div class="span{{ widthratio(subfield.width, 100, width) }}">{{ field_widget(subfield, horizontal=horizontal, width=width, nested=true) }}</div>
+      {% endfor %}
+      </div>{% for error in field.errors %}
+      <p class="help-block" style="font-weight: bold;">{{ error }}</p>{% endfor %}{% if field.help_text %}
+      <p class="help-block">{{ field.help_text }}</p>{% endif %}
+    </div>{% else %}
+    <div class="controls">
+      {{ field_widget(field, horizontal=horizontal, width=width) }}{% for error in field.errors %}
+      <p class="help-block" style="font-weight: bold;">{{ error }}</p>{% endfor %}{% if field.help_text %}
+      <p class="help-block">{{ field.help_text }}</p>{% endif %}
+    </div>{% endif %}
+  </div>
+{%- endmacro -%}
+
+{# Render form field macro #}
+{%- macro field_widget(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+
+{%- if field.widget == "checkbox" -%}
+{{ input_checkbox(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "date" -%}
+{{ input_date(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif %}
+
+{%- if field.widget == "file_clearable" -%}
+{{ input_file_clearable(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "forumTos" -%}
+{{ input_forum_tos(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "recaptcha" -%}
+{{ input_recaptcha(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "radio_select" -%}
+{{ input_radio_select(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "select" -%}
+{{ input_select(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "checkbox_select_multiple" -%}
+{{ input_checkbox_select_multiple(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "text" -%}
+{{ input_text(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "textarea" -%}
+{{ input_textarea(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif -%}
+
+{%- if field.widget == "yes_no_switch" -%}
+{{ input_yes_no_switch(field, attrs=attrs, classes=[], horizontal=horizontal, width=width, nested=nested) }}
+{%- endif %}
+{%- endmacro -%}
+
+
+{# Render form field attributes macro #}
+{%- macro field_attrs(attrs={}, extras=[]) -%}
+{% for attribute in attrs %} {{ attribute }}="{{ attrs[attribute] }}"{% endfor %}{% for extra in extras %} {{ extra }}{% endfor %}
+{%- endmacro -%}
+
+
+{# Render form field class attribute macro #}
+{%- macro field_classes(classes=[]) -%}
+{% if classes %} class="{% for class in classes %}{% if not loop.first %} {% endif %}{{ class }}{% endfor %}"{% endif %}
+{%- endmacro -%}
+
+
+{# Checkbox input #}
+{%- macro input_checkbox(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+<label class="checkbox">
+  <input type="checkbox" name="{{ field.html_name }}" id="{{ field.html_id }}" value="1"{% if field.value %} checked="checked"{% endif %}>
+  {% if field.inline is defined %}{{ field.inline }}{% else %}{{ field.label }}{% endif %}
+</label>
+{%- endmacro -%}
+
+{# Forum Terms of Service input #}
+{%- macro input_forum_tos(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+<label class="checkbox">
+  <input type="checkbox" name="{{ field.html_name }}" id="{{ field.html_id }}" value="1"{% if field.value %} checked="checked"{% endif %}>
+  {% trans forum_tos=make_tos()|safe %}I have read and accept this forums {{forum_tos}}.{% endtrans %}
+</label>
+{%- endmacro -%}
+{%- macro make_tos() -%}
+<a href="{% if settings.tos_url %}{{ settings.tos_url }}{% else %}{{ url('tos') }}{% endif %}">{% if settings.tos_title %}{{ settings.tos_title }}{% else %}{% trans %}Terms of Service{% endtrans %}{% endif %}</a>
+{%- endmacro -%}
+
+{# Date input #}
+{%- macro input_date(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- if horizontal -%}
+  {%- do classes.append('span' ~ (widthratio(field.width, 100, width) - 2)) -%}
+{%- else -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
+{%- endif -%}
+<input type="text"{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}{% if field.has_value %} value="{{ field.value }}"{% endif %}>
+{%- endmacro -%}
+
+{# Multiple Checkbox input #}
+{%- macro input_checkbox_select_multiple(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- do classes.append('select-multiple') -%}
+<div{{ field_classes(classes) }}>{% for choice in field.choices %}
+  <label class="checkbox">
+    <input type="checkbox" name="{{ field.html_name }}" id="{{ field.html_id }}_{{ choice[0] }}" value="{{ choice[0] }}"{% if field.value and choice[0] in field.value %} checked="checked"{% endif %}>
+    {{ choice[1] }}
+  </label>{% endfor %}
+</div>
+{%- endmacro -%}
+
+
+{# File Upload input #}
+{%- macro input_file_clearable(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+<input type="file" name="{{ field.html_name }}" id="{{ field.html_id }}" > 
+{%- endmacro -%}
+
+
+{# Recaptcha input #}
+{%- macro input_recaptcha(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{{ field.attrs.html|safe }}
+{%- endmacro -%}
+
+
+{# RadioSelect input #}
+{%- macro input_radio_select(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- do classes.append('radio-group') -%}
+<div{{ field_classes(classes) }}>{% for choice in field.choices %}
+  <label class="radio">
+    <input type="radio" name="{{ field.html_name }}" id="{{ field.html_id }}{{ choice[0] }}" value="{{ choice[0] }}"{% if field.value == choice[0] %} checked="checked"{% endif %}>
+    {{ choice[1] }}
+  </label>{% endfor %}
+</div>
+{%- endmacro -%}
+
+
+{# Select input #}
+{%- macro input_select(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- if horizontal %}
+  {%- do classes.append('span' ~ (widthratio(field.width, 100, width) - 2)) -%}
+{%- else -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
+{%- endif -%}
+<select{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}>{% for choice in field.choices %}
+  <option value="{{ choice[0] }}"{% if field.value == choice[0] %} selected="selected"{% endif %}>{{ choice[1] }}</option>{% endfor %}
+</select>
+{%- endmacro -%}
+
+
+{# Text/password input #}
+{%- macro input_text(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- if horizontal -%}
+  {%- do classes.append('span' ~ (widthratio(field.width, 100, width) - 2)) -%}
+{%- else -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
+{%- endif -%}
+<input{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}{% if field.attrs.type != 'password' and field.has_value %} value="{{ field.value }}"{% endif %}>
+{%- endmacro -%}
+
+
+{# Textarea input #}
+{%- macro input_textarea(field, attrs={'rows': 4}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- if horizontal -%}
+  {%- do classes.append('span' ~ (widthratio(field.width, 100, width) - 2)) -%}
+{%- else -%}
+  {%- do classes.append('span' ~ widthratio(field.width, 100, width)) -%}
+{%- endif -%}
+<textarea{{ field_attrs(field.attrs) }}{{ field_classes(classes) }}>{% if field.has_value %}{{ field.value }}{% endif %}</textarea>
+{%- endmacro -%}
+
+{# YesNoSwitch input #}
+{%- macro input_yes_no_switch(field, attrs={}, classes=[], horizontal=false, width=12, nested=false) -%}
+{%- do field.attrs.update(attrs) -%}
+{%- do classes.append('yes-no-switch') -%}
+<div{{ field_classes(classes) }} id="{{ field.html_id }}_div">
+  <input name="{{ field.html_name }}" id="{{ field.html_id }}" type="checkbox" value="1"{% if field.value %} checked="checked"{% endif %}>
+</div>
 {%- endmacro -%}
 {%- endmacro -%}

+ 43 - 43
templates/admin/admin/acl_form.html

@@ -1,44 +1,44 @@
-{% extends "admin/admin/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block action_body %}
-<form action="{{ link }}" method="post">
-  {{ form_theme.form_hidden_widget(form) }}
-  {% for fieldset in form.fieldsets %}
-  <table class="table table-striped">
-    <thead>
-      <tr>
-        <th class="lead">{{ fieldset.legend }}</th>
-        <th>{% trans %}Permission{% endtrans %}</th>
-      </tr>
-    </thead>
-    <tbody>
-      {% for field in fieldset.fields %}
-      <tr>
-        <td>
-          <strong>{{ field.label }}</strong>{% if field.help_text %}<div class="muted">{{ field.help_text }}</div>{% endif %}
-        </td>
-        <td class="span4">
-          {{ form_theme.field_widget(field, width=4) }}
-        </td>
-      </tr>
-      {% endfor %}
-    </tbody>
-  </table>
-  {% endfor %}
-  <div class="form-actions">
-  	{% block form_actions %}
-  	{% block form_submit %}
-  	<button name="save" type="submit" class="btn btn-primary">{{ action.submit_button }}</button>
-  	{% endblock %}
-  	{% if action.get_edit_link -%}
-  	<button name="save_edit" type="submit" class="btn btn-warning">{% trans %}Save and Edit{% endtrans %}</button>
-  	{%- endif %}
-  	{% if action.get_new_link -%}
-  	<button name="save_new" type="submit" class="btn btn-success">{% trans %}Save and Add Another{% endtrans %}</button>
-  	{%- endif %}
-  	{% if fallback %}<a href="{{ fallback }}" class="btn">{% trans %}Cancel{% endtrans %}</a>{% endif %}
-  	{% endblock %}
-  </div>
-</form>
+{% extends "admin/admin/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block action_body %}
+<form action="{{ link }}" method="post">
+  {{ form_theme.form_hidden_widget(form) }}
+  {% for fieldset in form.fieldsets %}
+  <table class="table table-striped">
+    <thead>
+      <tr>
+        <th class="lead">{{ fieldset.legend }}</th>
+        <th>{% trans %}Permission{% endtrans %}</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for field in fieldset.fields %}
+      <tr>
+        <td>
+          <strong>{{ field.label }}</strong>{% if field.help_text %}<div class="muted">{{ field.help_text }}</div>{% endif %}
+        </td>
+        <td class="span4">
+          {{ form_theme.field_widget(field, width=4) }}
+        </td>
+      </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+  {% endfor %}
+  <div class="form-actions">
+  	{% block form_actions %}
+  	{% block form_submit %}
+  	<button name="save" type="submit" class="btn btn-primary">{{ action.submit_button }}</button>
+  	{% endblock %}
+  	{% if action.get_edit_link -%}
+  	<button name="save_edit" type="submit" class="btn btn-warning">{% trans %}Save and Edit{% endtrans %}</button>
+  	{%- endif %}
+  	{% if action.get_new_link -%}
+  	<button name="save_new" type="submit" class="btn btn-success">{% trans %}Save and Add Another{% endtrans %}</button>
+  	{%- endif %}
+  	{% if fallback %}<a href="{{ fallback }}" class="btn">{% trans %}Cancel{% endtrans %}</a>{% endif %}
+  	{% endblock %}
+  </div>
+</form>
 {% endblock %}
 {% endblock %}

+ 53 - 53
templates/admin/admin/form.html

@@ -1,53 +1,53 @@
-{% extends "admin/admin/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block action_body %}
-<form action="{{ link }}" method="post">
-  {% if tabbed %}
-  {{ form_theme.form_hidden_widget(form) }}
-  <ul class="nav nav-tabs" id="form-tabs">{% for fieldset in form.fieldsets %}
-    <li{% if loop.first %} class="active"{% endif%}><a href="#form-tab-{{ loop.index }}" data-toggle="tab">{{ fieldset.legend }}</a></li>{% endfor %}
-  </ul>
-  <div class="tab-content">
-    {% for fieldset in form.fieldsets %}
-    <div class="tab-pane{% if loop.first %} active{% endif%}" id="form-tab-{{ loop.index }}">
-      <fieldset>
-        <legend>{{ fieldset.legend }}</legend>{% for field in fieldset.fields %}
-          {{ form_theme.row_widget(field) }}{% endfor %}
-      </fieldset>
-    </div>
-    {% endfor %}
-  </div>
-  {% else %}
-  {{ form_theme.form_widget(form) }}
-  {% endif %}
-  <div class="form-actions">
-  	{% block form_actions %}
-  	{% block form_submit %}
-  	<button name="save" type="submit" class="btn btn-primary">{{ action.submit_button }}</button>
-  	{% endblock %}
-  	{% if action.get_edit_link -%}
-  	<button name="save_edit" type="submit" class="btn btn-warning">{% trans %}Save and Edit{% endtrans %}</button>
-  	{%- endif %}
-  	{% if action.get_new_link -%}
-  	<button name="save_new" type="submit" class="btn btn-success">{% trans %}Save and Add Another{% endtrans %}</button>
-  	{%- endif %}
-  	{% if fallback %}<a href="{{ fallback }}" class="btn">{% trans %}Cancel{% endtrans %}</a>{% endif %}
-  	{% endblock %}
-  </div>
-</form>
-{% endblock %}
-
-{% block javascripts %}
-{{ super() }}
-{% if tabbed %}
-    <script>
-      $(function () {
-        $('#form-tabs a').click(function (e) {
-          e.preventDefault();
-          $(this).tab('show');
-        });
-      });
-    </script>
-{% endif %}
-{% endblock %}
+{% extends "admin/admin/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block action_body %}
+<form action="{{ link }}" method="post">
+  {% if tabbed %}
+  {{ form_theme.form_hidden_widget(form) }}
+  <ul class="nav nav-tabs" id="form-tabs">{% for fieldset in form.fieldsets %}
+    <li{% if loop.first %} class="active"{% endif%}><a href="#form-tab-{{ loop.index }}" data-toggle="tab">{{ fieldset.legend }}</a></li>{% endfor %}
+  </ul>
+  <div class="tab-content">
+    {% for fieldset in form.fieldsets %}
+    <div class="tab-pane{% if loop.first %} active{% endif%}" id="form-tab-{{ loop.index }}">
+      <fieldset>
+        <legend>{{ fieldset.legend }}</legend>{% for field in fieldset.fields %}
+          {{ form_theme.row_widget(field) }}{% endfor %}
+      </fieldset>
+    </div>
+    {% endfor %}
+  </div>
+  {% else %}
+  {{ form_theme.form_widget(form) }}
+  {% endif %}
+  <div class="form-actions">
+  	{% block form_actions %}
+  	{% block form_submit %}
+  	<button name="save" type="submit" class="btn btn-primary">{{ action.submit_button }}</button>
+  	{% endblock %}
+  	{% if action.get_edit_link -%}
+  	<button name="save_edit" type="submit" class="btn btn-warning">{% trans %}Save and Edit{% endtrans %}</button>
+  	{%- endif %}
+  	{% if action.get_new_link -%}
+  	<button name="save_new" type="submit" class="btn btn-success">{% trans %}Save and Add Another{% endtrans %}</button>
+  	{%- endif %}
+  	{% if fallback %}<a href="{{ fallback }}" class="btn">{% trans %}Cancel{% endtrans %}</a>{% endif %}
+  	{% endblock %}
+  </div>
+</form>
+{% endblock %}
+
+{% block javascripts %}
+{{ super() }}
+{% if tabbed %}
+    <script>
+      $(function () {
+        $('#form-tabs a').click(function (e) {
+          e.preventDefault();
+          $(this).tab('show');
+        });
+      });
+    </script>
+{% endif %}
+{% endblock %}

+ 30 - 30
templates/admin/admin/layout.html

@@ -1,30 +1,30 @@
-{% extends "admin/layout.html" %}
-{% from "admin/macros.html" import page_title, draw_message %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block title %}{% if admin.actions[0].id != action.id and action.name -%}
-{% if target %}{{ page_title(parent=action.name, title=target) }}{% else %}{{ page_title(title=action.name) }}{% endif %}
-{%- else -%}
-{{ page_title(title=admin.name) }}
-{%- endif %}{% endblock %}
-
-{% block content %}
-<div class="page-header{% if admin.actions|length > 1 %} tabs-header{% endif %}">
-  <h1>{{ admin.name }}{% if admin.help %} <small>{{ admin.help }}</small>{% endif %}</h1>
-  {% if admin.actions|length > 1 %}<ul class="nav nav-tabs">{% for item in admin.actions %}
-  	<li{% if action.id == item.id %} class="active"{% endif %}><a href="{{ url(item.link) }}"{% if action.id != item.id and item.help %} class="tooltip-bottom" title="{{ item.help }}"{% endif %}>{{ item.name }}</a></li>{% endfor %}
-  </ul>{% endif %}
-</div>
-{% if admin.actions[0].id != action.id -%}
-<h2>{% if target %}{{ target }} <small>{{ action.name }}</small>{% else %}{{ action.name }}{% if action.help %} <small>{{ action.help }}</small>{% endif %}{% endif %}</h2>
-{% endif %}
-{% for message in messages_log %}
-{{ draw_message(message, 'alert-form') }}
-{% endfor %}
-{% if message %}
-{{ draw_message(message, 'alert-form') }}
-{% endif %}
-{% block page_help %}{% endblock %}
-{% block action_body %}
-{% endblock %}
-{% endblock %}
+{% extends "admin/layout.html" %}
+{% from "admin/macros.html" import page_title, draw_message %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{% if admin.actions[0].id != action.id and action.name -%}
+{% if target %}{{ page_title(parent=action.name, title=target) }}{% else %}{{ page_title(title=action.name) }}{% endif %}
+{%- else -%}
+{{ page_title(title=admin.name) }}
+{%- endif %}{% endblock %}
+
+{% block content %}
+<div class="page-header{% if admin.actions|length > 1 %} tabs-header{% endif %}">
+  <h1>{{ admin.name }}{% if admin.help %} <small>{{ admin.help }}</small>{% endif %}</h1>
+  {% if admin.actions|length > 1 %}<ul class="nav nav-tabs">{% for item in admin.actions %}
+  	<li{% if action.id == item.id %} class="active"{% endif %}><a href="{{ url(item.link) }}"{% if action.id != item.id and item.help %} class="tooltip-bottom" title="{{ item.help }}"{% endif %}>{{ item.name }}</a></li>{% endfor %}
+  </ul>{% endif %}
+</div>
+{% if admin.actions[0].id != action.id -%}
+<h2>{% if target %}{{ target }} <small>{{ action.name }}</small>{% else %}{{ action.name }}{% if action.help %} <small>{{ action.help }}</small>{% endif %}{% endif %}</h2>
+{% endif %}
+{% for message in messages_log %}
+{{ draw_message(message, 'alert-form') }}
+{% endfor %}
+{% if message %}
+{{ draw_message(message, 'alert-form') }}
+{% endif %}
+{% block page_help %}{% endblock %}
+{% block action_body %}
+{% endblock %}
+{% endblock %}

+ 148 - 148
templates/admin/admin/list.html

@@ -1,148 +1,148 @@
-{% extends "admin/admin/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block action_body %}
-{%- if search_form %}
-<div class="row">
-  <div class="span9">{% endif -%}
-{% if items|length > 0 -%}
-<table class="table table-striped">
-  <thead>
-    <tr>{% block table_head scoped %}{% for column in action.columns %}
-      <th{{ th_class(column, sorting, sorting_method) }}>
-      {%- if column[0] in sorting %}<a href="
-      {%- if column[0] == sorting_method[0]-%}
-      {% if sorting_method[1] %}{{ link ~ query(sort=column[0],dir=0) }}{% else %}{{ link ~ query(sort=column[0],dir=1) }}{% endif %}
-      {%- else -%}
-      {{ link ~ query(sort=column[0],dir=sorting[column[0]]) }}
-      {%- endif %}">{{ column[1] }}</a>{% else %}{{ column[1] }}{% endif %}</th>{% endfor %}{% endblock %}
-      {% if not action.hide_actions%}<th>{% trans %}Actions{% endtrans %}</th>{% endif -%}
-      {% if list_form %}<th class="check-cell"><label class="checkbox"><input type="checkbox" class="checkbox-master"></label></th>{% endif -%}
-    </tr>
-  </thead>
-  <tbody>{% for item in items %}
-    <tr>{% block table_row scoped %}{% for column in action.columns %}
-      <td{% if loop.first %} class="lead-cell{% if column[3] is defined %} span{{ widthratio(column[3], 100, 12) }}{% endif %}"{% elif column[3] is defined %} class="span{{ widthratio(column[3], 100, 12) }}"{% endif %}>{{ item[column[0]] }}</td>{% endfor %}{% endblock %}
-      {%- if not action.hide_actions%}
-      <td class="span2">
-      	{%- set item_actions = action.get_item_actions(item) -%}
-      	{% if item_actions -%}
-      	<ul class="list-actions">
-      	  {% for action in item_actions %}
-      	  <li>{% if action.post -%}
-      	  <form action="{{ action.link }}" method="post"{% if action.prompt %} class="confirm" data-jsconfirm="{{ action.prompt }}"{% endif %}>
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      	    <button type="submit" class="tooltip-top" title="{{ action.name }}"><i class="icon-{{ action.icon }}"></i></button>
-      	  </form>
-      	  {%- else -%}
-      	  <a href="{{ action.link }}" class="tooltip-top{% if action.prompt %} confirm{% endif %}"{% if action.prompt %} data-jsconfirm="{{ action.prompt }}"{% endif %} title="{{ action.name }}"><i class="icon-{{ action.icon }}"></i></a>
-      	  {%- endif %}</li>
-      	  {% endfor %}
-      	</ul>
-      	{%- else -%}
-      	<em>{% trans %}Not available{% endtrans %}</em>
-      	{%- endif %}
-      </td>{% endif %}
-      {% if list_form %}<td class="check-cell"><label class="checkbox"><input form="list_form" name="{{ list_form.fields['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ item.pk }}"{% if list_form.fields['list_items']['has_value'] and item.pk in list_form.fields['list_items']['value'] %} checked="checked"{% endif %}></label></td>{% endif %}
-    </tr>{% endfor %}
-  </tbody>
-</table>
-<div class="form-actions table-footer">
-  {% if table_form %}
-  <form id="table_form" class="form-inline table-actions-right pull-left" action="{{ link }}" method="POST">
-    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-    <input type="hidden" name="origin" value="table">
-    <button type="submit" class="btn btn-primary">{{ action.table_form_button }}</button>
-  </form>
-  {% endif %}
-  {% if pagination and (pagination['prev'] > 0 or pagination['next'] > 0) %}
-  <ul class="pager pull-left">
-    {%- if pagination['prev'] > 0 %}<li><a href="{{ action.get_pagination_link(pagination['prev']) }}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ action.get_pagination_link(pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-  <div class="table-count pull-left">{%- trans current_page=pagination['page'], pages=pagination['total'] -%}
-  Page {{ current_page }} of {{ pages }}
-  {%- endtrans -%}</div>{% else %}
-  <div class="table-count pull-left">{% trans count=items_total, total=items_total|intcomma, shown=items_shown|intcomma -%}Showing one item
-{%- pluralize -%}
-Showing {{ shown }} of {{ total }} items
-{%- endtrans %}</div>{% endif %}
-  {% if list_form -%}
-  <form id="list_form" class="form-inline pull-right" action="{{ link }}" method="POST">
-    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-    <input type="hidden" name="origin" value="list">
-    {{ form_theme.input_select(list_form.fields['list_action'],width=3) }}
-    <button type="submit" class="btn btn-primary">{% trans %}Go{% endtrans %}</button>
-  </form>
-{%- endif %}
-</div>
-{%- else -%}
-<div class="alert alert-{% if action.is_filtering %}error{% else %}info{% endif %} alert-form">
-  <div class="alert-icon"><span><i class="icon-{% if action.is_filtering %}remove{% else %}info-sign{% endif %} icon-white"></i></span></div>
-  <p>{% if action.is_filtering %}{{ action.empty_search_message }}{% else %}{{ action.empty_message }}{% endif %}</p>
-</div>
-{% endif -%}
-{%- if search_form %}
-  </div>
-  <div class="span3 side-search">
-  	<h4>{% if search_form.fieldsets[0].legend %}{{ search_form.fieldsets[0].legend }}{% else %}{% trans %}Search Items{% endtrans %}{% endif %}</h4>
-    <form id="search_form" action="{{ link }}" method="post">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <input type="hidden" name="origin" value="search">
-      {% for field in search_form.fieldsets[0].fields %}
-      {{ form_theme.row_widget(field, width=3) }}
-      {% endfor %}
-    </form>
-    <div class="form-actions">
-      <div class="row">
-        <button type="submit" form="search_form" class="btn btn-primary span1"><i class="icon-search icon-white"></i></button>{% if action.is_filtering %}
-        <button type="submit" form="search_clear" class="btn btn-inverse span1 offset1"><i class="icon-remove icon-white"></i></button>{% endif %}
-      </div>
-    </div>
-  </div>
-</div>{% if action.is_filtering %}
-<form id="search_clear" class="form-inline" action="{{ link }}" method="post">
-  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-  <input type="hidden" name="origin" value="clear">
-</form>{% endif %}{% endif -%}
-{% endblock %}
-
-{% block javascripts -%}
-{{ super() }}
-{%- if list_form %}
-  <script type="text/javascript">
-    $(function () {
-      $('#list_form').submit(function() {
-        if ($('.check-cell[]:checked').length == 0) {
-          alert("{{ action.nothing_checked_message }}");
-          return false;
-        }
-        {%- for item in action.actions %}{% if item.2 %}
-        if ($('#id_list_action').val() == '{{ item.0 }}') {
-          var decision = confirm("{{ item.2 }}");
-          return decision;
-        }
-        {%- endif %}{% endfor %}
-        return true;
-      });
-    });
-  </script>{% endif %}
-{%- endblock %}
-
-{#- COLUMN CLASS -#}
-{%- macro th_class(column, sorting, sorting_method) -%}
-{%- if column[2] or column[0] in sorting %} class="
-{#- COLUMN WIDTH (ex. "span4") -#}
-{%- if column[2] %}span{{ widthratio(column[2], 100, 12) }}{% endif -%}
-{#- SEPARATOR -#}
-{%- if column[2] and column[0] in sorting %} {% endif -%}
-{#- COLUMN SORTING -#}
-{%- if column[0] in sorting -%}table-sort sort-
-{%- if column[0] == sorting_method[0] -%}
-active-{% if sorting_method[1] %}asc{% else %}desc{% endif -%}
-{%- else -%}
-{%- if sorting[column[0]] -%}asc{% else %}desc{% endif -%}
-{%- endif -%}
-{%- endif -%}
-"{%- endif -%}
-{%- endmacro -%}
+{% extends "admin/admin/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block action_body %}
+{%- if search_form %}
+<div class="row">
+  <div class="span9">{% endif -%}
+{% if items|length > 0 -%}
+<table class="table table-striped">
+  <thead>
+    <tr>{% block table_head scoped %}{% for column in action.columns %}
+      <th{{ th_class(column, sorting, sorting_method) }}>
+      {%- if column[0] in sorting %}<a href="
+      {%- if column[0] == sorting_method[0]-%}
+      {% if sorting_method[1] %}{{ link ~ query(sort=column[0],dir=0) }}{% else %}{{ link ~ query(sort=column[0],dir=1) }}{% endif %}
+      {%- else -%}
+      {{ link ~ query(sort=column[0],dir=sorting[column[0]]) }}
+      {%- endif %}">{{ column[1] }}</a>{% else %}{{ column[1] }}{% endif %}</th>{% endfor %}{% endblock %}
+      {% if not action.hide_actions%}<th>{% trans %}Actions{% endtrans %}</th>{% endif -%}
+      {% if list_form %}<th class="check-cell"><label class="checkbox"><input type="checkbox" class="checkbox-master"></label></th>{% endif -%}
+    </tr>
+  </thead>
+  <tbody>{% for item in items %}
+    <tr>{% block table_row scoped %}{% for column in action.columns %}
+      <td{% if loop.first %} class="lead-cell{% if column[3] is defined %} span{{ widthratio(column[3], 100, 12) }}{% endif %}"{% elif column[3] is defined %} class="span{{ widthratio(column[3], 100, 12) }}"{% endif %}>{{ item[column[0]] }}</td>{% endfor %}{% endblock %}
+      {%- if not action.hide_actions%}
+      <td class="span2">
+      	{%- set item_actions = action.get_item_actions(item) -%}
+      	{% if item_actions -%}
+      	<ul class="list-actions">
+      	  {% for action in item_actions %}
+      	  <li>{% if action.post -%}
+      	  <form action="{{ action.link }}" method="post"{% if action.prompt %} class="confirm" data-jsconfirm="{{ action.prompt }}"{% endif %}>
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      	    <button type="submit" class="tooltip-top" title="{{ action.name }}"><i class="icon-{{ action.icon }}"></i></button>
+      	  </form>
+      	  {%- else -%}
+      	  <a href="{{ action.link }}" class="tooltip-top{% if action.prompt %} confirm{% endif %}"{% if action.prompt %} data-jsconfirm="{{ action.prompt }}"{% endif %} title="{{ action.name }}"><i class="icon-{{ action.icon }}"></i></a>
+      	  {%- endif %}</li>
+      	  {% endfor %}
+      	</ul>
+      	{%- else -%}
+      	<em>{% trans %}Not available{% endtrans %}</em>
+      	{%- endif %}
+      </td>{% endif %}
+      {% if list_form %}<td class="check-cell"><label class="checkbox"><input form="list_form" name="{{ list_form.fields['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ item.pk }}"{% if list_form.fields['list_items']['has_value'] and item.pk in list_form.fields['list_items']['value'] %} checked="checked"{% endif %}></label></td>{% endif %}
+    </tr>{% endfor %}
+  </tbody>
+</table>
+<div class="form-actions table-footer">
+  {% if table_form %}
+  <form id="table_form" class="form-inline table-actions-right pull-left" action="{{ link }}" method="POST">
+    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+    <input type="hidden" name="origin" value="table">
+    <button type="submit" class="btn btn-primary">{{ action.table_form_button }}</button>
+  </form>
+  {% endif %}
+  {% if pagination and (pagination['prev'] > 0 or pagination['next'] > 0) %}
+  <ul class="pager pull-left">
+    {%- if pagination['prev'] > 0 %}<li><a href="{{ action.get_pagination_link(pagination['prev']) }}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ action.get_pagination_link(pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+  <div class="table-count pull-left">{%- trans current_page=pagination['page'], pages=pagination['total'] -%}
+  Page {{ current_page }} of {{ pages }}
+  {%- endtrans -%}</div>{% else %}
+  <div class="table-count pull-left">{% trans count=items_total, total=items_total|intcomma, shown=items_shown|intcomma -%}Showing one item
+{%- pluralize -%}
+Showing {{ shown }} of {{ total }} items
+{%- endtrans %}</div>{% endif %}
+  {% if list_form -%}
+  <form id="list_form" class="form-inline pull-right" action="{{ link }}" method="POST">
+    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+    <input type="hidden" name="origin" value="list">
+    {{ form_theme.input_select(list_form.fields['list_action'],width=3) }}
+    <button type="submit" class="btn btn-primary">{% trans %}Go{% endtrans %}</button>
+  </form>
+{%- endif %}
+</div>
+{%- else -%}
+<div class="alert alert-{% if action.is_filtering %}error{% else %}info{% endif %} alert-form">
+  <div class="alert-icon"><span><i class="icon-{% if action.is_filtering %}remove{% else %}info-sign{% endif %} icon-white"></i></span></div>
+  <p>{% if action.is_filtering %}{{ action.empty_search_message }}{% else %}{{ action.empty_message }}{% endif %}</p>
+</div>
+{% endif -%}
+{%- if search_form %}
+  </div>
+  <div class="span3 side-search">
+  	<h4>{% if search_form.fieldsets[0].legend %}{{ search_form.fieldsets[0].legend }}{% else %}{% trans %}Search Items{% endtrans %}{% endif %}</h4>
+    <form id="search_form" action="{{ link }}" method="post">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <input type="hidden" name="origin" value="search">
+      {% for field in search_form.fieldsets[0].fields %}
+      {{ form_theme.row_widget(field, width=3) }}
+      {% endfor %}
+    </form>
+    <div class="form-actions">
+      <div class="row">
+        <button type="submit" form="search_form" class="btn btn-primary span1"><i class="icon-search icon-white"></i></button>{% if action.is_filtering %}
+        <button type="submit" form="search_clear" class="btn btn-inverse span1 offset1"><i class="icon-remove icon-white"></i></button>{% endif %}
+      </div>
+    </div>
+  </div>
+</div>{% if action.is_filtering %}
+<form id="search_clear" class="form-inline" action="{{ link }}" method="post">
+  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+  <input type="hidden" name="origin" value="clear">
+</form>{% endif %}{% endif -%}
+{% endblock %}
+
+{% block javascripts -%}
+{{ super() }}
+{%- if list_form %}
+  <script type="text/javascript">
+    $(function () {
+      $('#list_form').submit(function() {
+        if ($('.check-cell[]:checked').length == 0) {
+          alert("{{ action.nothing_checked_message }}");
+          return false;
+        }
+        {%- for item in action.actions %}{% if item.2 %}
+        if ($('#id_list_action').val() == '{{ item.0 }}') {
+          var decision = confirm("{{ item.2 }}");
+          return decision;
+        }
+        {%- endif %}{% endfor %}
+        return true;
+      });
+    });
+  </script>{% endif %}
+{%- endblock %}
+
+{#- COLUMN CLASS -#}
+{%- macro th_class(column, sorting, sorting_method) -%}
+{%- if column[2] or column[0] in sorting %} class="
+{#- COLUMN WIDTH (ex. "span4") -#}
+{%- if column[2] %}span{{ widthratio(column[2], 100, 12) }}{% endif -%}
+{#- SEPARATOR -#}
+{%- if column[2] and column[0] in sorting %} {% endif -%}
+{#- COLUMN SORTING -#}
+{%- if column[0] in sorting -%}table-sort sort-
+{%- if column[0] == sorting_method[0] -%}
+active-{% if sorting_method[1] %}asc{% else %}desc{% endif -%}
+{%- else -%}
+{%- if sorting[column[0]] -%}asc{% else %}desc{% endif -%}
+{%- endif -%}
+{%- endif -%}
+"{%- endif -%}
+{%- endmacro -%}

+ 14 - 14
templates/admin/bans/list.html

@@ -1,14 +1,14 @@
-{% extends "admin/admin/list.html" %}
-
-{% block table_row scoped %}
-  <td class="lead-cell">
-  	<strong>{{ item.ban }}</strong> <span class="label {% if item.test == 0 -%}label-inverse">{% trans %}Username and E-mail{% endtrans %}
-  	{%- elif item.test == 1 -%}label-important">{% trans %}Username{% endtrans %}
-  	{%- elif item.test == 2 -%}label-warning">{% trans %}E-mail{% endtrans %}
-  	{%- else -%}label-info">{% trans %}IP Address{% endtrans %}
-  	{%- endif %}</span>
-  </td>
-  <td>
-  	{% if item.expires %}{{ item.expires|date }}{% else %}<em>{% trans %}Permanent{% endtrans %}</em>{% endif %}
-  </td>
-{% endblock %}
+{% extends "admin/admin/list.html" %}
+
+{% block table_row scoped %}
+  <td class="lead-cell">
+  	<strong>{{ item.ban }}</strong> <span class="label {% if item.test == 0 -%}label-inverse">{% trans %}Username and E-mail{% endtrans %}
+  	{%- elif item.test == 1 -%}label-important">{% trans %}Username{% endtrans %}
+  	{%- elif item.test == 2 -%}label-warning">{% trans %}E-mail{% endtrans %}
+  	{%- else -%}label-info">{% trans %}IP Address{% endtrans %}
+  	{%- endif %}</span>
+  </td>
+  <td>
+  	{% if item.expires %}{{ item.expires|date }}{% else %}<em>{% trans %}Permanent{% endtrans %}</em>{% endif %}
+  </td>
+{% endblock %}

+ 16 - 16
templates/admin/base.html

@@ -1,17 +1,17 @@
-{% from "admin/macros.html" import page_title -%}
-<!DOCTYPE html>
-<html lang="{{ LANGUAGE_CODE }}">
-  <head>
-    <meta charset="utf-8">
-    <title>{% block title %}{{ page_title() }}{% endblock %}</title>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link href="{{ STATIC_URL }}admin/css/admin.css" rel="stylesheet">{% block stylesheets %}{% endblock %}
-  </head>
-  <body{% block body_class %}{% endblock %}>
-  	{% block body %}{% endblock %}
-  	<script src="{{ STATIC_URL }}admin/js/jquery-1.7.2.min.js"></script>
-  	<script src="{{ STATIC_URL }}admin/js/bootstrap.min.js"></script>
-  	<script src="{{ STATIC_URL }}admin/js/jquery.toggle.buttons.js"></script>
-  	<script src="{{ STATIC_URL }}admin/js/admin.js"></script>{% block javascripts %}{% endblock %}
-  </body>
+{% from "admin/macros.html" import page_title -%}
+<!DOCTYPE html>
+<html lang="{{ LANGUAGE_CODE }}">
+  <head>
+    <meta charset="utf-8">
+    <title>{% block title %}{{ page_title() }}{% endblock %}</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link href="{{ STATIC_URL }}admin/css/admin.css" rel="stylesheet">{% block stylesheets %}{% endblock %}
+  </head>
+  <body{% block body_class %}{% endblock %}>
+  	{% block body %}{% endblock %}
+  	<script src="{{ STATIC_URL }}admin/js/jquery-1.7.2.min.js"></script>
+  	<script src="{{ STATIC_URL }}admin/js/bootstrap.min.js"></script>
+  	<script src="{{ STATIC_URL }}admin/js/jquery.toggle.buttons.js"></script>
+  	<script src="{{ STATIC_URL }}admin/js/admin.js"></script>{% block javascripts %}{% endblock %}
+  </body>
 </html>
 </html>

+ 7 - 7
templates/admin/clients/list.html

@@ -1,7 +1,7 @@
-{% extends "admin/admin/list.html" %}
-
-{% block table_row scoped %}
-  <td class="lead-cell">
-  	<strong>{{ _(item.theme) }}</strong> <span class="muted">{{ ', '.join(item.useragents.splitlines()) }}</span>
-  </td>
-{% endblock%}
+{% extends "admin/admin/list.html" %}
+
+{% block table_row scoped %}
+  <td class="lead-cell">
+  	<strong>{{ _(item.theme) }}</strong> <span class="muted">{{ ', '.join(item.useragents.splitlines()) }}</span>
+  </td>
+{% endblock%}

+ 22 - 22
templates/admin/error403.html

@@ -1,23 +1,23 @@
-{% extends "admin/layout.html" %}
-
-{% block title %}{% trans %}Permission denied{% endtrans %} - {{ settings.board_name }}{% endblock %}
-      
-{% block content %}
-<div class="row">
-  <div class="span6 offset3">
-    <div class="page-header">
-      <h2>{% trans %}Permission denied{% endtrans %} <small>{% trans %}Error 403{% endtrans %}</small></h2>
-    </div>
-    {% block message %}
-    {% if message %}
-    <p class="lead">{{ message }}</p>
-    {% else %}
-    <p class="lead">{% trans %}You don't have permission to see this page.{% endtrans %}</p>
-    {% endif %}
-    {% endblock %}
-    <ul class="unstyled">
-      <li><i class="icon-arrow-left"></i> <a href="#" class="go-back">{% trans %}Return to previous page{% endtrans %}</a></li>
-    </ul>
-  </div>
-</div>
+{% extends "admin/layout.html" %}
+
+{% block title %}{% trans %}Permission denied{% endtrans %} - {{ settings.board_name }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="page-header">
+      <h2>{% trans %}Permission denied{% endtrans %} <small>{% trans %}Error 403{% endtrans %}</small></h2>
+    </div>
+    {% block message %}
+    {% if message %}
+    <p class="lead">{{ message }}</p>
+    {% else %}
+    <p class="lead">{% trans %}You don't have permission to see this page.{% endtrans %}</p>
+    {% endif %}
+    {% endblock %}
+    <ul class="unstyled">
+      <li><i class="icon-arrow-left"></i> <a href="#" class="go-back">{% trans %}Return to previous page{% endtrans %}</a></li>
+    </ul>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 22 - 22
templates/admin/error404.html

@@ -1,23 +1,23 @@
-{% extends "admin/layout.html" %}
-
-{% block title %}{% trans %}Page not found{% endtrans %} - {{ settings.board_name }}{% endblock %}
-      
-{% block content %}
-<div class="row">
-  <div class="span6 offset3">
-    <div class="page-header">
-      <h2>{% trans %}Page not found{% endtrans %} <small>{% trans %}Error 404{% endtrans %}</small></h2>
-    </div>
-    {% block message %}
-    {% if message %}
-    <p class="lead">{{ message }}</p>
-    {% else %}
-    <p class="lead">{% trans %}The page you are looking for could not be found.{% endtrans %}</p>
-    {% endif %}
-    {% endblock %}
-    <ul class="unstyled">
-      <li><i class="icon-arrow-left"></i> <a href="#" class="go-back">{% trans %}Return to previous page{% endtrans %}</a></li>
-    </ul>
-  </div>
-</div>
+{% extends "admin/layout.html" %}
+
+{% block title %}{% trans %}Page not found{% endtrans %} - {{ settings.board_name }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="page-header">
+      <h2>{% trans %}Page not found{% endtrans %} <small>{% trans %}Error 404{% endtrans %}</small></h2>
+    </div>
+    {% block message %}
+    {% if message %}
+    <p class="lead">{{ message }}</p>
+    {% else %}
+    <p class="lead">{% trans %}The page you are looking for could not be found.{% endtrans %}</p>
+    {% endif %}
+    {% endblock %}
+    <ul class="unstyled">
+      <li><i class="icon-arrow-left"></i> <a href="#" class="go-back">{% trans %}Return to previous page{% endtrans %}</a></li>
+    </ul>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 5 - 5
templates/admin/forums/delete.html

@@ -1,5 +1,5 @@
-{% extends "admin/admin/form.html" %}
-
-{% block form_submit %}
-<button name="save" type="submit" class="btn btn-danger">{% trans %}Delete Forum{% endtrans %}</button>
-{% endblock %}
+{% extends "admin/admin/form.html" %}
+
+{% block form_submit %}
+<button name="save" type="submit" class="btn btn-danger">{% trans %}Delete Forum{% endtrans %}</button>
+{% endblock %}

+ 17 - 17
templates/admin/forums/list.html

@@ -1,17 +1,17 @@
-{% extends "admin/admin/list.html" %}
-
-{% block table_row scoped %}
-  <td class="lead-cell" style="padding-left: {{ 8 + ((item.level - 1) * 24) }}px;">
-  	<i class="icon-{{ forum_icon(item.type) }}"></i> <strong>{{ item.name }}</strong>
-  </td>
-{% endblock%}
-
-{% macro forum_icon(forum_type) -%}
-{%- if forum_type == 'category' -%}
-folder-open
-{%- elif forum_type == 'forum' -%}
-list
-{%- else -%}
-globe
-{%- endif -%}
-{%- endmacro %}
+{% extends "admin/admin/list.html" %}
+
+{% block table_row scoped %}
+  <td class="lead-cell" style="padding-left: {{ 8 + ((item.level - 1) * 24) }}px;">
+  	<i class="icon-{{ forum_icon(item.type) }}"></i> <strong>{{ item.name }}</strong>
+  </td>
+{% endblock%}
+
+{% macro forum_icon(forum_type) -%}
+{%- if forum_type == 'category' -%}
+folder-open
+{%- elif forum_type == 'forum' -%}
+list
+{%- else -%}
+globe
+{%- endif -%}
+{%- endmacro %}

+ 64 - 64
templates/admin/index.html

@@ -1,65 +1,65 @@
-{% extends "admin/layout.html" %}
-{% from "admin/macros.html" import page_title %}
-
-{% block title %}{{ page_title(title=_('Admin Home')) }}{% endblock %}
-
-{% block content %}
-<div class="page-header">
-  <h1>{% trans %}Admin Home{% endtrans %} <small>{% trans %}Misago {{version}}{% endtrans %}</small></h1>
-</div>
-
-{% if monitor.users_inactive|int > 0 %}
-<div class="alert alert-info alert-form">
-  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
-  <p><a href="{{ url('admin_users_inactive') }}">{%- trans count=monitor.users_inactive|int, total=monitor.users_inactive|int|intcomma -%}
-  There is one inactive user.
-  {%- pluralize -%}
-  There are {{ total }} inactive users.
-  {%- endtrans -%}</a></p>
-</div>
-{% endif %}
-
-<div class="row">
-  <div class="span8">
-  	
-  	<h2>{% trans count=admins|length, total=admins|length|intcomma -%}
-One Administrator Online
-{%- pluralize -%}
-{{ total }} Administrators Online
-{%- endtrans %}</h2>
-    <table class="table table-striped table-users list-tiny">
-      <tbody>
-        {% for session in admins %}    	
-        <tr>
-          <td>
-              <a href="{{ url('user', username=session.user.username_slug, user=session.user.pk) }}"><img src="{{ session.user.get_avatar(22) }}" class="avatar" alt=""> <strong>{{ session.user.username }}</strong></a>
-              <div class="muted" style="float: right;">{% trans start=session.start|timesince, ip=session.ip %}started {{ start }} ago from {{ ip }}{% endtrans %} <span class="info-popover tooltip-top" title="{% trans last=session.last|timesince %}Last click was {{ last }} ago{% endtrans %}"><i class="icon-time"></i></span></div>
-          </td>
-        </tr>{% endfor %}
-      </tbody>
-    </table>
-        
-  </div>
-  <div class="span4">
-  	
-  	<h3>{% trans %}Board Statistics{% endtrans %}</h3>
-    <table class="table table-striped">
-      <tbody>
-      	<tr>
-      	  <td class="span2 stat-title"><strong>{% trans %}Users{% endtrans %}</strong></td>
-      	  <td>{{ users|intcomma }}</td>
-      	</tr>
-      	<tr>
-      	  <td class="span2 stat-title"><strong>{% trans %}Posts{% endtrans %}</strong></td>
-      	  <td>{{ posts|intcomma }}</td>
-      	</tr>
-      	<tr>
-      	  <td class="span2 stat-title"><strong>{% trans %}Threads{% endtrans %}</strong></td>
-      	  <td>{{ threads|intcomma }}</td>
-      	</tr>
-      </tbody>
-    </table>
-    
-  </div>
-</div>
+{% extends "admin/layout.html" %}
+{% from "admin/macros.html" import page_title %}
+
+{% block title %}{{ page_title(title=_('Admin Home')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Admin Home{% endtrans %} <small>{% trans %}Misago {{version}}{% endtrans %}</small></h1>
+</div>
+
+{% if monitor.users_inactive|int > 0 %}
+<div class="alert alert-info alert-form">
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><a href="{{ url('admin_users_inactive') }}">{%- trans count=monitor.users_inactive|int, total=monitor.users_inactive|int|intcomma -%}
+  There is one inactive user.
+  {%- pluralize -%}
+  There are {{ total }} inactive users.
+  {%- endtrans -%}</a></p>
+</div>
+{% endif %}
+
+<div class="row">
+  <div class="span8">
+  	
+  	<h2>{% trans count=admins|length, total=admins|length|intcomma -%}
+One Administrator Online
+{%- pluralize -%}
+{{ total }} Administrators Online
+{%- endtrans %}</h2>
+    <table class="table table-striped table-users list-tiny">
+      <tbody>
+        {% for session in admins %}    	
+        <tr>
+          <td>
+              <a href="{{ url('user', username=session.user.username_slug, user=session.user.pk) }}"><img src="{{ session.user.get_avatar(22) }}" class="avatar" alt=""> <strong>{{ session.user.username }}</strong></a>
+              <div class="muted" style="float: right;">{% trans start=session.start|timesince, ip=session.ip %}started {{ start }} ago from {{ ip }}{% endtrans %} <span class="info-popover tooltip-top" title="{% trans last=session.last|timesince %}Last click was {{ last }} ago{% endtrans %}"><i class="icon-time"></i></span></div>
+          </td>
+        </tr>{% endfor %}
+      </tbody>
+    </table>
+        
+  </div>
+  <div class="span4">
+  	
+  	<h3>{% trans %}Board Statistics{% endtrans %}</h3>
+    <table class="table table-striped">
+      <tbody>
+      	<tr>
+      	  <td class="span2 stat-title"><strong>{% trans %}Users{% endtrans %}</strong></td>
+      	  <td>{{ users|intcomma }}</td>
+      	</tr>
+      	<tr>
+      	  <td class="span2 stat-title"><strong>{% trans %}Posts{% endtrans %}</strong></td>
+      	  <td>{{ posts|intcomma }}</td>
+      	</tr>
+      	<tr>
+      	  <td class="span2 stat-title"><strong>{% trans %}Threads{% endtrans %}</strong></td>
+      	  <td>{{ threads|intcomma }}</td>
+      	</tr>
+      </tbody>
+    </table>
+    
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 45 - 45
templates/admin/layout.html

@@ -1,45 +1,45 @@
-{% extends "admin/base.html" %}
-{% from "admin/macros.html" import messages_list %}
-
-{% block body %}
-<div id="page-top" class="navbar navbar-static-top navbar-sections">
-  <div class="navbar-inner">
-    <div class="container">
-      <a class="brand" href="{{ url(admin_index) }}">Misago <span>{{ version }}</span></a>
-      <ul class="nav">{% for section in sections %}
-      	<li{% if section.is_active and not exception_response %} class="active"{% endif %}><a href="{{ url(section.link) }}"><i class="icon-{{ section.icon }}"></i> {{ section.name }}</a></li>{% endfor %}
-      </ul>
-      <form class="navbar-form user-signout pull-right" action="{{ url('admin_sign_out') }}" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn btn-link"><i class="icon-off"></i> {% trans %}Sign Out{% endtrans %}</button></form>
-      <ul class="nav pull-right">
-      	<li><a href="{{ url('index') }}"><i class="icon-home"></i> {% trans %}Forums Index{% endtrans %}</a></li>
-      </ul>
-      <div class="user-profile pull-right">
-        <img src="{{ user.get_avatar(28) }}" class="avatar-small" alt=""> {{ user.username }}
-      </div>
-    </div>
-  </div>
-</div>{% if not exception_response%}
-<div class="navbar navbar-static-top navbar-inverse navbar-actions">
-  <div class="navbar-inner">
-    <div class="container">
-      <ul class="nav">{% for action in actions %}
-      	<li{% if action.is_active %} class="active"{% elif action.help %} class="tooltip-bottom" title="{{ action.help }}"{% endif %}><a href="{{ url(action.link) }}"><i class="icon-{{ action.icon }}"></i> {{ action.name }}</a></li>{% endfor %}
-      </ul>
-    </div>
-  </div>
-</div>{% endif %}
-<div class="container body-container">
-  
-  {% if messages %}
-  <div class="alerts-global">
-  	{{ messages_list(messages) }}
-  </div>{% endif %}
-  {% block content %}
-  {% endblock %}
-  
-  <footer>
-  	Powered by <a href="http://misago-project.org">Misago forum software</a> by Rafał Pitoń
-  	<a href="#page-top" class="go-to-top"><i class="icon-arrow-up"></i>{% trans %}Go to Top{% endtrans %}</a>
-  </footer>
-</div>
-{% endblock %}
+{% extends "admin/base.html" %}
+{% from "admin/macros.html" import messages_list %}
+
+{% block body %}
+<div id="page-top" class="navbar navbar-static-top navbar-sections">
+  <div class="navbar-inner">
+    <div class="container">
+      <a class="brand" href="{{ url(admin_index) }}">Misago <span>{{ version }}</span></a>
+      <ul class="nav">{% for section in sections %}
+      	<li{% if section.is_active and not exception_response %} class="active"{% endif %}><a href="{{ url(section.link) }}"><i class="icon-{{ section.icon }}"></i> {{ section.name }}</a></li>{% endfor %}
+      </ul>
+      <form class="navbar-form user-signout pull-right" action="{{ url('admin_sign_out') }}" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn btn-link"><i class="icon-off"></i> {% trans %}Sign Out{% endtrans %}</button></form>
+      <ul class="nav pull-right">
+      	<li><a href="{{ url('index') }}"><i class="icon-home"></i> {% trans %}Forums Index{% endtrans %}</a></li>
+      </ul>
+      <div class="user-profile pull-right">
+        <img src="{{ user.get_avatar(28) }}" class="avatar-small" alt=""> {{ user.username }}
+      </div>
+    </div>
+  </div>
+</div>{% if not exception_response%}
+<div class="navbar navbar-static-top navbar-inverse navbar-actions">
+  <div class="navbar-inner">
+    <div class="container">
+      <ul class="nav">{% for action in actions %}
+      	<li{% if action.is_active %} class="active"{% elif action.help %} class="tooltip-bottom" title="{{ action.help }}"{% endif %}><a href="{{ url(action.link) }}"><i class="icon-{{ action.icon }}"></i> {{ action.name }}</a></li>{% endfor %}
+      </ul>
+    </div>
+  </div>
+</div>{% endif %}
+<div class="container body-container">
+  
+  {% if messages %}
+  <div class="alerts-global">
+  	{{ messages_list(messages) }}
+  </div>{% endif %}
+  {% block content %}
+  {% endblock %}
+  
+  <footer>
+  	Powered by <a href="http://misago-project.org">Misago forum software</a> by Rafał Pitoń
+  	<a href="#page-top" class="go-to-top"><i class="icon-arrow-up"></i>{% trans %}Go to Top{% endtrans %}</a>
+  </footer>
+</div>
+{% endblock %}

+ 17 - 17
templates/admin/layout_compact.html

@@ -1,18 +1,18 @@
-{% extends "admin/base.html" %}
-
-{% block body_class %} class="layout-compact"{% endblock %}
-
-{% block body %}
-    <div class="container">
-      <div class="row">
-      	<div class="span4 offset4">
-		  <h1>{% block header %}{% endblock %}</h1>
-		</div>
-      	<div class="span4 offset4">
-      	  <div class="block-bold">
-      	    {% block content %}{% endblock %}
-      	  </div>
-      	</div>
-      </div>
-    </div>
+{% extends "admin/base.html" %}
+
+{% block body_class %} class="layout-compact"{% endblock %}
+
+{% block body %}
+    <div class="container">
+      <div class="row">
+      	<div class="span4 offset4">
+		  <h1>{% block header %}{% endblock %}</h1>
+		</div>
+      	<div class="span4 offset4">
+      	  <div class="block-bold">
+      	    {% block content %}{% endblock %}
+      	  </div>
+      	</div>
+      </div>
+    </div>
 {% endblock %}
 {% endblock %}

+ 25 - 25
templates/admin/macros.html

@@ -1,25 +1,25 @@
-{% macro page_title(title='', parent='', page=0) -%}
-{% if parent %}{{ parent }}: {% endif %}{% if title %}{{ title }}{% if page > 1 %} ({% trans page=page %}{{ page }} page{% endtrans %}){% endif %} - {% endif %}{% trans %}Misago Admin{% endtrans %}
-{%- endmacro %}
-
-{# Messages list marco #}
-{% macro messages_list(messages) %}{% if messages %}<div class="alerts-list">{% for message in messages %}
-  {{ draw_message(message) }}
-{% endfor %}</div>{% endif %}
-{% endmacro %}
-
-{# Render single message #}
-{% macro draw_message(message, class='') %}
-  <div class="alert alert-{{ message.type }}{% if class %} {{ class }}{% endif %}">
-  	{{ draw_message_icon(message) }} <p><strong>{{ message.message }}</strong></p>
-  </div>
-{%- endmacro %}
-
-{# Render single message #}
-{% macro draw_message_icon(message) -%}
-  	<div class="alert-icon"><span><i class="icon-{% if message.type == 'error' -%}remove
-  		{%- elif message.type == 'success' -%}ok
-  		{%- elif message.type == 'info' -%}info-sign
-  		{%- else -%}warning-sign
-  		{%- endif %} icon-white"></i></span></div>
-{%- endmacro %}
+{% macro page_title(title='', parent='', page=0) -%}
+{% if parent %}{{ parent }}: {% endif %}{% if title %}{{ title }}{% if page > 1 %} ({% trans page=page %}{{ page }} page{% endtrans %}){% endif %} - {% endif %}{% trans %}Misago Admin{% endtrans %}
+{%- endmacro %}
+
+{# Messages list marco #}
+{% macro messages_list(messages) %}{% if messages %}<div class="alerts-list">{% for message in messages %}
+  {{ draw_message(message) }}
+{% endfor %}</div>{% endif %}
+{% endmacro %}
+
+{# Render single message #}
+{% macro draw_message(message, class='') %}
+  <div class="alert alert-{{ message.type }}{% if class %} {{ class }}{% endif %}">
+  	{{ draw_message_icon(message) }} <p><strong>{{ message.message }}</strong></p>
+  </div>
+{%- endmacro %}
+
+{# Render single message #}
+{% macro draw_message_icon(message) -%}
+  	<div class="alert-icon"><span><i class="icon-{% if message.type == 'error' -%}remove
+  		{%- elif message.type == 'success' -%}ok
+  		{%- elif message.type == 'info' -%}info-sign
+  		{%- else -%}warning-sign
+  		{%- endif %} icon-white"></i></span></div>
+{%- endmacro %}

+ 8 - 8
templates/admin/newsletters/list.html

@@ -1,8 +1,8 @@
-{% extends "admin/admin/list.html" %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block table_row scoped %}
-  <td class="lead-cell">
-  	<strong>{{ item.name }}</strong>{% if item.ignore_subscriptions %} <span class="label label-important">{% trans %}Ignoring Subscriptions{% endtrans %}</span>{% endif %}
-  </td>
-{% endblock%}
+{% extends "admin/admin/list.html" %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block table_row scoped %}
+  <td class="lead-cell">
+  	<strong>{{ item.name }}</strong>{% if item.ignore_subscriptions %} <span class="label label-important">{% trans %}Ignoring Subscriptions{% endtrans %}</span>{% endif %}
+  </td>
+{% endblock%}

+ 23 - 23
templates/admin/online/list.html

@@ -1,24 +1,24 @@
-{% extends "admin/admin/list.html" %}
-
-{% block table_row scoped %}
-  <td>
-  	<strong class="lead{% if not item.user %} muted{% endif %}">{% if item.user -%}
-  	<a href="{{ url('user', username=item.user.username_slug, user=item.user.id) }}">{{ item.user.username }}</a>
-  	{%- elif item.crawler -%}
-  	{{ item.crawler }}
-  	{%- else -%}
-  	{% trans %}Guest{% endtrans %}
-  	{%- endif %}</strong> <strong class="muted">{{ item.ip }}</strong> {% if item.user -%}
-  	<span class="label label-success">{% trans %}Registered Member{% endtrans %}</span>{% if item.hitten %} <span class="label label-info">{% trans %}Hidden{% endtrans %}</span>{% endif %}
-  	{%- elif item.crawler -%}
-  	<span class="label label-inverse">{% trans %}Crawler{% endtrans %}</span>
-  	{%- endif %}
-  	<div class="muted">{{ item.agent }}</div>
-  </td>
-  <td>
-  	{{ item.start|date("DATETIME_FORMAT") }}
-  </td>
-  <td>
-  	{{ item.last|date("TIME_FORMAT") }}
-  </td>
+{% extends "admin/admin/list.html" %}
+
+{% block table_row scoped %}
+  <td>
+  	<strong class="lead{% if not item.user %} muted{% endif %}">{% if item.user -%}
+  	<a href="{{ url('user', username=item.user.username_slug, user=item.user.id) }}">{{ item.user.username }}</a>
+  	{%- elif item.crawler -%}
+  	{{ item.crawler }}
+  	{%- else -%}
+  	{% trans %}Guest{% endtrans %}
+  	{%- endif %}</strong> <strong class="muted">{{ item.ip }}</strong> {% if item.user -%}
+  	<span class="label label-success">{% trans %}Registered Member{% endtrans %}</span>{% if item.hitten %} <span class="label label-info">{% trans %}Hidden{% endtrans %}</span>{% endif %}
+  	{%- elif item.crawler -%}
+  	<span class="label label-inverse">{% trans %}Crawler{% endtrans %}</span>
+  	{%- endif %}
+  	<div class="muted">{{ item.agent }}</div>
+  </td>
+  <td>
+  	{{ item.start|date("DATETIME_FORMAT") }}
+  </td>
+  <td>
+  	{{ item.last|date("TIME_FORMAT") }}
+  </td>
 {% endblock %}
 {% endblock %}

+ 21 - 21
templates/admin/processing.html

@@ -1,22 +1,22 @@
-{% extends "admin/layout_compact.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% from "admin/macros.html" import page_title, draw_message_icon %}
-
-{% block title %}{{ page_title(title=target_name,parent=task_name) }}{% endblock %}
-
-{% block header %}<strong>{{ target_name }}</strong>{% endblock %}
-      
-{% block content %}
-          <form class="form-vertical" action="{{ url(admin_index) }}" method="post">
-          	<div class="form-container">
-          	  <p class="lead">{{ task_name }}...</p>
-              <div class="progress progress-striped active">
-                <div class="bar" style="width: {{ progress }}%;"></div>
-              </div>
-          	  <p>{{ message }}</p>
-            </div>
-            <div class="form-actions">
-              <a href="{{ cancel_link }}" class="btn pull-right"><i class="icon-home"></i> {% trans %}Cancel Task{% endtrans %}</a>
-            </div>
-          </form>
+{% extends "admin/layout_compact.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% from "admin/macros.html" import page_title, draw_message_icon %}
+
+{% block title %}{{ page_title(title=target_name,parent=task_name) }}{% endblock %}
+
+{% block header %}<strong>{{ target_name }}</strong>{% endblock %}
+      
+{% block content %}
+          <form class="form-vertical" action="{{ url(admin_index) }}" method="post">
+          	<div class="form-container">
+          	  <p class="lead">{{ task_name }}...</p>
+              <div class="progress progress-striped active">
+                <div class="bar" style="width: {{ progress }}%;"></div>
+              </div>
+          	  <p>{{ message }}</p>
+            </div>
+            <div class="form-actions">
+              <a href="{{ cancel_link }}" class="btn pull-right"><i class="icon-home"></i> {% trans %}Cancel Task{% endtrans %}</a>
+            </div>
+          </form>
 {% endblock %}
 {% endblock %}

+ 14 - 14
templates/admin/prune/apply.html

@@ -1,14 +1,14 @@
-{% extends "admin/admin/layout.html" %}
-
-{% block action_body %}
-<form action="{{ link }}" method="post">
-  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-  <p class="lead">
-  	{% trans %}Users to be deleted{% endtrans %}: <strong>{{ total_users|intcomma }}</strong>
-  </p>
-  <div class="form-actions">
-  	<button name="save" type="submit" class="btn btn-danger">{% trans %}Apply Policy{% endtrans %}</button>
-  	<a href="{{ fallback }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
-  </div>
-</form>
-{% endblock %}
+{% extends "admin/admin/layout.html" %}
+
+{% block action_body %}
+<form action="{{ link }}" method="post">
+  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+  <p class="lead">
+  	{% trans %}Users to be deleted{% endtrans %}: <strong>{{ total_users|intcomma }}</strong>
+  </p>
+  <div class="form-actions">
+  	<button name="save" type="submit" class="btn btn-danger">{% trans %}Apply Policy{% endtrans %}</button>
+  	<a href="{{ fallback }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+  </div>
+</form>
+{% endblock %}

+ 7 - 7
templates/admin/prune/list.html

@@ -1,7 +1,7 @@
-{% extends "admin/admin/list.html" %}
-
-{% block table_row scoped %}
-  <td class="lead-cell">
-  	<strong>{{ item.name }}</strong>
-  </td>
-{% endblock%}
+{% extends "admin/admin/list.html" %}
+
+{% block table_row scoped %}
+  <td class="lead-cell">
+  	<strong>{{ item.name }}</strong>
+  </td>
+{% endblock%}

+ 16 - 16
templates/admin/ranks/list.html

@@ -1,16 +1,16 @@
-{% extends "admin/admin/list.html" %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block table_head scoped %}
-  {{ super() }}
-  <th>{% trans %}Order{% endtrans %}</th>
-{% endblock %}
-
-{% block table_row scoped %}
-  <td class="lead-cell">
-  	<strong>{{ _(item.name) }}</strong>{% if item.special %} <span class="label label-info">{% trans %}Special{% endtrans %}</span>{% endif %}{% if item.as_tab %} <span class="label label-inverse">{% trans %}Tab{% endtrans %}</span>{% endif %}{% if item.on_index %} <span class="label label-orange">{% trans %}On Index{% endtrans %}</span>{% endif %}
-  </td>
-  <td class="span2">
-  	{{ form_theme.field_widget(table_form['pos_' + item.pk|string], attrs={'form': 'table_form'}, width=2) }}
-  </td>
-{% endblock%}
+{% extends "admin/admin/list.html" %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block table_head scoped %}
+  {{ super() }}
+  <th>{% trans %}Order{% endtrans %}</th>
+{% endblock %}
+
+{% block table_row scoped %}
+  <td class="lead-cell">
+  	<strong>{{ _(item.name) }}</strong>{% if item.special %} <span class="label label-info">{% trans %}Special{% endtrans %}</span>{% endif %}{% if item.as_tab %} <span class="label label-inverse">{% trans %}Tab{% endtrans %}</span>{% endif %}{% if item.on_index %} <span class="label label-orange">{% trans %}On Index{% endtrans %}</span>{% endif %}
+  </td>
+  <td class="span2">
+  	{{ form_theme.field_widget(table_form['pos_' + item.pk|string], attrs={'form': 'table_form'}, width=2) }}
+  </td>
+{% endblock%}

+ 28 - 28
templates/admin/roles/forums.html

@@ -1,29 +1,29 @@
-{% extends "admin/admin/list.html" %}
-{% from "admin/macros.html" import page_title %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block title %}{{ page_title(parent=_(action.role.name), title=_("Role Forum Permissions")) }}{% endblock %}
-
-{% block table_head scoped %}
-  <th>{% trans %}Forum{% endtrans %}</th>
-  <th class="span3">{% trans %}Role{% endtrans %}</th>
-{% endblock %}
-
-{% block table_row scoped %}
-  <td class="lead-cell" style="padding-left: {{ 8 + ((item.level - 1) * 24) }}px;">
-  	<i class="icon-{{ forum_icon(item.type) }}"></i> <strong>{{ item.name }}</strong>
-  </td>
-  <td>
-  	{{ form_theme.field_widget(table_form['forum_' + item.pk|string], attrs={'form': 'table_form'}, width=3) }}
-  </td>
-{% endblock %}
-
-{% macro forum_icon(forum_type) -%}
-{%- if forum_type == 'category' -%}
-folder-open
-{%- elif forum_type == 'forum' -%}
-list
-{%- else -%}
-globe
-{%- endif -%}
+{% extends "admin/admin/list.html" %}
+{% from "admin/macros.html" import page_title %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{{ page_title(parent=_(action.role.name), title=_("Role Forum Permissions")) }}{% endblock %}
+
+{% block table_head scoped %}
+  <th>{% trans %}Forum{% endtrans %}</th>
+  <th class="span3">{% trans %}Role{% endtrans %}</th>
+{% endblock %}
+
+{% block table_row scoped %}
+  <td class="lead-cell" style="padding-left: {{ 8 + ((item.level - 1) * 24) }}px;">
+  	<i class="icon-{{ forum_icon(item.type) }}"></i> <strong>{{ item.name }}</strong>
+  </td>
+  <td>
+  	{{ form_theme.field_widget(table_form['forum_' + item.pk|string], attrs={'form': 'table_form'}, width=3) }}
+  </td>
+{% endblock %}
+
+{% macro forum_icon(forum_type) -%}
+{%- if forum_type == 'category' -%}
+folder-open
+{%- elif forum_type == 'forum' -%}
+list
+{%- else -%}
+globe
+{%- endif -%}
 {%- endmacro %}
 {%- endmacro %}

+ 7 - 7
templates/admin/roles/list.html

@@ -1,7 +1,7 @@
-{% extends "admin/admin/list.html" %}
-
-{% block table_row scoped %}
-  <td class="lead-cell">
-  	<strong>{{ _(item.name) }}</strong>{% if item.token %} <span class="label label-important">{% trans %}System Role{% endtrans %}</span>{% elif item.protected %} <span class="label label-info">{% trans %}Protected Role{% endtrans %}</span>{% endif %}
-  </td>
-{% endblock%}
+{% extends "admin/admin/list.html" %}
+
+{% block table_row scoped %}
+  <td class="lead-cell">
+  	<strong>{{ _(item.name) }}</strong>{% if item.token %} <span class="label label-important">{% trans %}System Role{% endtrans %}</span>{% elif item.protected %} <span class="label label-info">{% trans %}Protected Role{% endtrans %}</span>{% endif %}
+  </td>
+{% endblock%}

+ 7 - 7
templates/admin/roles_forums/list.html

@@ -1,7 +1,7 @@
-{% extends "admin/admin/list.html" %}
-
-{% block table_row scoped %}
-  <td class="lead-cell">
-  	<strong>{{ _(item.name) }}</strong>
-  </td>
-{% endblock%}
+{% extends "admin/admin/list.html" %}
+
+{% block table_row scoped %}
+  <td class="lead-cell">
+  	<strong>{{ _(item.name) }}</strong>
+  </td>
+{% endblock%}

+ 15 - 15
templates/admin/settings/search_results.html

@@ -1,15 +1,15 @@
-{% extends "admin/settings/settings.html" %}
-{% from "admin/macros.html" import page_title, draw_message %}
-
-{% block title %}{{ page_title(title=_('Search Results'), parent=_('Settings')) }}{% endblock %}
-
-{% block action %}
-<h2 class="sidepanel-header">{% trans %}Search Results{% endtrans %}</h2>{% if message %}
-{{ draw_message(message, 'alert-form') }}{% endif %}
-{% for setting in found_settings %}
-<h4>{{ _(setting.name) }} <small>{{ _(setting.group.name) }}</small></h4>
-{%- if setting.description %}<p>{{ _(setting.description) }}</p>{% endif -%}
-<a href="{{ url('admin_settings', group_id=setting.group.id, group_slug=setting.group.key) }}#id_{{ setting.pk }}">{% trans %}Go to this setting{% endtrans %}</a>
-<hr>
-{% endfor %}
-{% endblock %}
+{% extends "admin/settings/settings.html" %}
+{% from "admin/macros.html" import page_title, draw_message %}
+
+{% block title %}{{ page_title(title=_('Search Results'), parent=_('Settings')) }}{% endblock %}
+
+{% block action %}
+<h2 class="sidepanel-header">{% trans %}Search Results{% endtrans %}</h2>{% if message %}
+{{ draw_message(message, 'alert-form') }}{% endif %}
+{% for setting in found_settings %}
+<h4>{{ _(setting.name) }} <small>{{ _(setting.group.name) }}</small></h4>
+{%- if setting.description %}<p>{{ _(setting.description) }}</p>{% endif -%}
+<a href="{{ url('admin_settings', group_id=setting.group.id, group_slug=setting.group.key) }}#id_{{ setting.pk }}">{% trans %}Go to this setting{% endtrans %}</a>
+<hr>
+{% endfor %}
+{% endblock %}

+ 41 - 41
templates/admin/settings/settings.html

@@ -1,42 +1,42 @@
-{% extends "admin/layout.html" %}
-{% from "admin/macros.html" import page_title, draw_message %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block title %}{{ page_title(title=_(active_group.name), parent=_('Settings')) }}{% endblock %}
-
-{% block content %}
-<div class="page-header">
-  <h1>{% trans %}Settings{% endtrans %} <small>{% trans %}Change your forum configuration{% endtrans %}</small></h1>
-</div>
-<div class="row">
-  <div class="span3">
-    <ul class="nav nav-pills nav-stacked side-panel">
-      <h4>{% trans %}Search Settings{% endtrans %}</h4>
-      <form action="{{ url('admin_settings_search') }}" class="form-inline" method="post">
-        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-        {{ form_theme.input_text(search_form.fields.search_text, width=2, attrs={'placeholder': lang_search_settings()}) }}
-        <button type="submit" class="btn btn-primary"><i class="icon-search icon-white"></i></button>
-      </form>
-      <h4>{% trans%}Settings Groups{% endtrans %}</h4>{% for group in groups %}
-      <li{% if group.is_active(active_group) %} class="active"{% endif %}><a href="{{ url('admin_settings', group_id=group.id, group_slug=group.key) }}"{% if not group.is_active(active_group) and group.description %} class="tooltip-right" title="{{ _(group.description) }}"{% endif %}>{{ _(group.name) }}</a></li>{% endfor %}
-    </ul>
-  </div>
-  <div class="span9">{% block action %}
-  	<h2 class="sidepanel-header">{{ _(active_group.name) }}</h2>{% if message %}
-  	{{ draw_message(message, 'alert-form') }}
-  	{% endif %}{% if active_group.description %}
-  	<p>{{ _(active_group.description) }}</p>{% endif %}
-    <form class="form-vertical" action="{{ url('admin_settings', group_id=active_group.id, group_slug=active_group.key) }}" method="post">
-      <div class="form-container">
-       	{{ form_theme.form_widget(form, width=9) }}
-      </div>
-      <div class="form-actions">
-        <button type="submit" class="btn btn-primary">{% trans %}Change Settings{% endtrans %}</button>
-      </div>
-    </form>
-  {% endblock %}</div>
-</div>
-{% endblock %}
-
-{# Language strings macros #}
+{% extends "admin/layout.html" %}
+{% from "admin/macros.html" import page_title, draw_message %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block title %}{{ page_title(title=_(active_group.name), parent=_('Settings')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Settings{% endtrans %} <small>{% trans %}Change your forum configuration{% endtrans %}</small></h1>
+</div>
+<div class="row">
+  <div class="span3">
+    <ul class="nav nav-pills nav-stacked side-panel">
+      <h4>{% trans %}Search Settings{% endtrans %}</h4>
+      <form action="{{ url('admin_settings_search') }}" class="form-inline" method="post">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        {{ form_theme.input_text(search_form.fields.search_text, width=2, attrs={'placeholder': lang_search_settings()}) }}
+        <button type="submit" class="btn btn-primary"><i class="icon-search icon-white"></i></button>
+      </form>
+      <h4>{% trans%}Settings Groups{% endtrans %}</h4>{% for group in groups %}
+      <li{% if group.is_active(active_group) %} class="active"{% endif %}><a href="{{ url('admin_settings', group_id=group.id, group_slug=group.key) }}"{% if not group.is_active(active_group) and group.description %} class="tooltip-right" title="{{ _(group.description) }}"{% endif %}>{{ _(group.name) }}</a></li>{% endfor %}
+    </ul>
+  </div>
+  <div class="span9">{% block action %}
+  	<h2 class="sidepanel-header">{{ _(active_group.name) }}</h2>{% if message %}
+  	{{ draw_message(message, 'alert-form') }}
+  	{% endif %}{% if active_group.description %}
+  	<p>{{ _(active_group.description) }}</p>{% endif %}
+    <form class="form-vertical" action="{{ url('admin_settings', group_id=active_group.id, group_slug=active_group.key) }}" method="post">
+      <div class="form-container">
+       	{{ form_theme.form_widget(form, width=9) }}
+      </div>
+      <div class="form-actions">
+        <button type="submit" class="btn btn-primary">{% trans %}Change Settings{% endtrans %}</button>
+      </div>
+    </form>
+  {% endblock %}</div>
+</div>
+{% endblock %}
+
+{# Language strings macros #}
 {% macro lang_search_settings() -%}{% trans %}Search Settings...{% endtrans %}{%- endmacro %}
 {% macro lang_search_settings() -%}{% trans %}Search Settings...{% endtrans %}{%- endmacro %}

+ 26 - 26
templates/admin/signin.html

@@ -1,27 +1,27 @@
-{% extends "admin/layout_compact.html" %}
-{% import "forms.html" as form_theme with context %}
-{% from "admin/macros.html" import page_title, draw_message_icon %}
-
-{% block title %}{{ page_title(title=_('Sign In')) }}{% endblock %}
-
-{% block header %}<strong>Misago</strong> {% trans %}Board Administration{% endtrans %}{% endblock %}
-      
-{% block content %}
-          {% if message %}
-          <div class="alert alert-{{ message.type }} block-alert">
-            {{ draw_message_icon(message) }}
-	        <p><strong>{{ message.message }}</strong></p>
-          </div>
-          {% endif %}
-          <form class="form-vertical" action="{{ url(admin_index) }}" method="post">
-          	<div class="form-container">
-              {{ form_theme.hidden_fields(form) }}
-              {{ form_theme.row(form.user_email, width=4) }}
-              {{ form_theme.row(form.user_password, width=4) }}
-            </div>
-            <div class="form-actions">
-              <button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i> {% trans %}Sign In{% endtrans %}</button>
-              <a href="{{ url('index') }}" class="btn pull-right"><i class="icon-home"></i> {% trans %}Return to Forums{% endtrans %}</a>
-            </div>
-          </form>
+{% extends "admin/layout_compact.html" %}
+{% import "forms.html" as form_theme with context %}
+{% from "admin/macros.html" import page_title, draw_message_icon %}
+
+{% block title %}{{ page_title(title=_('Sign In')) }}{% endblock %}
+
+{% block header %}<strong>Misago</strong> {% trans %}Board Administration{% endtrans %}{% endblock %}
+      
+{% block content %}
+          {% if message %}
+          <div class="alert alert-{{ message.type }} block-alert">
+            {{ draw_message_icon(message) }}
+	        <p><strong>{{ message.message }}</strong></p>
+          </div>
+          {% endif %}
+          <form class="form-vertical" action="{{ url(admin_index) }}" method="post">
+          	<div class="form-container">
+              {{ form_theme.hidden_fields(form) }}
+              {{ form_theme.row(form.user_email, width=4) }}
+              {{ form_theme.row(form.user_password, width=4) }}
+            </div>
+            <div class="form-actions">
+              <button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i> {% trans %}Sign In{% endtrans %}</button>
+              <a href="{{ url('index') }}" class="btn pull-right"><i class="icon-home"></i> {% trans %}Return to Forums{% endtrans %}</a>
+            </div>
+          </form>
 {% endblock %}
 {% endblock %}

+ 16 - 16
templates/admin/stats/form.html

@@ -1,16 +1,16 @@
-{% extends "admin/stats/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-
-{% block action %}<div class="row">
-  <div class="span8 offset2">
-  	<h2>{% trans %}New Report{% endtrans %}</h2>
-    <form action="{{ url('admin_stats') }}" class="form-vertical" method="post">
-      <div class="form-container">
-        {{ form_theme.form_widget(form, width=8) }}
-      </div>
-      <div class="form-actions">
-        <button type="submit" class="btn btn-primary">{% trans %}Generate Report{% endtrans %}</button>
-      </div>
-    </form>
-  </div>
-</div>{% endblock %}
+{% extends "admin/stats/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+
+{% block action %}<div class="row">
+  <div class="span8 offset2">
+  	<h2>{% trans %}New Report{% endtrans %}</h2>
+    <form action="{{ url('admin_stats') }}" class="form-vertical" method="post">
+      <div class="form-container">
+        {{ form_theme.form_widget(form, width=8) }}
+      </div>
+      <div class="form-actions">
+        <button type="submit" class="btn btn-primary">{% trans %}Generate Report{% endtrans %}</button>
+      </div>
+    </form>
+  </div>
+</div>{% endblock %}

+ 31 - 31
templates/admin/stats/graph.html

@@ -1,31 +1,31 @@
-{% extends "admin/stats/form.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% from "admin/macros.html" import page_title %}
-{% from "admin/stats/plot.html" import draw_plot_tooltips, draw_plot with context %}
-
-{% block title %}{{ page_title(title=title, parent=_('Stats')) }}{% endblock %}
-
-{% block action %}
-<h2>{{ title }} <small>{% trans count=graph.total, total=graph.total|intcomma -%}One item found
-{%- pluralize -%}
-{{ total }} items found
-{%- endtrans %}</small></h2>
-<div class="graph">
-  <canvas id="graph-canvas" height="250px"></canvas>
-  {{ draw_plot_tooltips('graph-canvas', graph) }}
-  <div class="timeline">
-    <div class="pull-left">{{ graph.start|date }}</div>
-    <div class="pull-right">{{ graph.end|date }}</div>
-  </div>
-</div>
-
-<hr>
-
-{{ super() }}
-{% endblock %}
-
-{% block javascripts %}
-<script type="text/javascript">
-  {{ draw_plot('draw_graph', 'graph-canvas', graph.max, graph.stat) }}
-</script>
-{% endblock %}
+{% extends "admin/stats/form.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% from "admin/macros.html" import page_title %}
+{% from "admin/stats/plot.html" import draw_plot_tooltips, draw_plot with context %}
+
+{% block title %}{{ page_title(title=title, parent=_('Stats')) }}{% endblock %}
+
+{% block action %}
+<h2>{{ title }} <small>{% trans count=graph.total, total=graph.total|intcomma -%}One item found
+{%- pluralize -%}
+{{ total }} items found
+{%- endtrans %}</small></h2>
+<div class="graph">
+  <canvas id="graph-canvas" height="250px"></canvas>
+  {{ draw_plot_tooltips('graph-canvas', graph) }}
+  <div class="timeline">
+    <div class="pull-left">{{ graph.start|date }}</div>
+    <div class="pull-right">{{ graph.end|date }}</div>
+  </div>
+</div>
+
+<hr>
+
+{{ super() }}
+{% endblock %}
+
+{% block javascripts %}
+<script type="text/javascript">
+  {{ draw_plot('draw_graph', 'graph-canvas', graph.max, graph.stat) }}
+</script>
+{% endblock %}

+ 13 - 13
templates/admin/stats/layout.html

@@ -1,13 +1,13 @@
-{% extends "admin/layout.html" %}
-{% import "admin/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Stats')) }}{% endblock %}
-
-{% block content %}
-<div class="page-header">
-  <h1>{% trans %}Stats{% endtrans %} <small>{% trans %}Create Statistics Reports{% endtrans %}</small></h1>
-</div>{% if message %}
-{{ macros.draw_message(message, 'alert-form') }}
-{% endif %}
-{% block action %}{% endblock %}
-{% endblock %}
+{% extends "admin/layout.html" %}
+{% import "admin/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Stats')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Stats{% endtrans %} <small>{% trans %}Create Statistics Reports{% endtrans %}</small></h1>
+</div>{% if message %}
+{{ macros.draw_message(message, 'alert-form') }}
+{% endif %}
+{% block action %}{% endblock %}
+{% endblock %}

+ 8 - 8
templates/admin/stats/not_available.html

@@ -1,8 +1,8 @@
-{% extends "admin/stats/layout.html" %}
-
-{% block action %}<div class="alert">
-  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
-  <p><strong>{% trans %}No statistics providers could be found.{% endtrans %}</strong></p>
-  <p>{% trans %}This action is not avaiable because there are no models found that provide filter_overview method necessary to generate statistics reports.{% endtrans %}</p>
-  <p>{% trans %}Some of Misago models provide statistics so you should never see this page.{% endtrans %}</p>
-</div>{% endblock %}
+{% extends "admin/stats/layout.html" %}
+
+{% block action %}<div class="alert">
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><strong>{% trans %}No statistics providers could be found.{% endtrans %}</strong></p>
+  <p>{% trans %}This action is not avaiable because there are no models found that provide filter_overview method necessary to generate statistics reports.{% endtrans %}</p>
+  <p>{% trans %}Some of Misago models provide statistics so you should never see this page.{% endtrans %}</p>
+</div>{% endblock %}

+ 76 - 76
templates/admin/stats/plot.html

@@ -1,77 +1,77 @@
-{% macro draw_plot_tooltips(id, graph) -%}
-{%- for stop in graph.timeline -%}
-<div id="{{ id }}-tooltip-{{ loop.index0 }}" class="peak peak-{{ id }} tooltip-{% if loop.first %}right{% elif loop.last %}left{% else %}top{% endif %}" title="{{ stop|date(graph.format) }}<br><strong>{{ graph.stat[loop.index0]|intcomma }}</strong>"></div>
-{%- endfor -%}
-{%- endmacro %}
-
-{% macro draw_plot(name, id, max, variables=[], color='#729fcf') -%}
-      // Begin drawing graph for {{ id }}
-      function {{ name }}() {
-        var margin = 12;
-        var margin_end = margin * 2;
-        var canvas = document.getElementById('{{ id }}');
-        var canvas_parent = $(canvas).parent();
-        var offset = canvas_parent.offset();
-        canvas.width = canvas_parent.width();
-        var ctx = canvas.getContext('2d');
-
-        // Guidelines
-        ctx.beginPath();
-        ctx.lineWidth = 1;
-        ctx.strokeStyle = "#EEEEEE";
-        ctx.fillStyle = "#CCC";
-		ctx.font = "10pt Helvetica";
-        ctx.moveTo(margin, margin + 0.5);
-        ctx.lineTo(canvas.width - margin, margin + 0.5);
-{%- if max > 0 %}
-{%- for line in [25, 50, 75] %}
-{%- if max > 3 %}
-{% set y_scales = 4 %}
-{%- else -%}
-{% set y_scales = max %}
-{% endif -%}
-{% if loop.index < max and loop.index < 4 %}
-		{%- set y_label = max - (loop.index / y_scales * max)|int -%}
-        y_pos = margin + Math.round((canvas.height - margin_end) * ({{ loop.index }} / {{ max }})) + 0.5;
-        ctx.moveTo(margin, y_pos);
-        ctx.lineTo(canvas.width - margin, y_pos);
-        ctx.fillText("{{ y_label }}", margin + 4, y_pos - 4.5);
-        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.25), y_pos - 4.5);
-        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.5), y_pos - 4.5);
-        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.75), y_pos - 4.5);
-{%- endif %}
-{%- endfor %}
-{%- endif %}
-        ctx.stroke();
-
-        // Final guide
-        ctx.beginPath();
-        ctx.strokeStyle = "#999999";
-        ctx.moveTo(margin, canvas.height - margin + 0.5);
-        ctx.lineTo(canvas.width - margin, canvas.height - margin + 0.5);
-        ctx.fillText("0", margin + 4, canvas.height - margin - 4);
-        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.25), canvas.height - margin - 4);
-        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.5), canvas.height - margin - 4);
-        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.75), canvas.height - margin - 4);
-        ctx.stroke();
-
-        // Graph line
-        ctx.beginPath();
-        ctx.lineWidth = 4;
-        ctx.strokeStyle = "{{ color }}";
-        step_size = (canvas.width - margin_end) / {{ variables|length - 1 }};
-{%- for stop in variables %}
-        pos_x = margin + ({{ loop.index0 }} * step_size);
-        pos_y = {% if max > 0 %}margin + Math.round((canvas.height - margin_end) * {{ ((max - stop) / max)|round(5) }}) + 0.5{% else %}canvas.height - margin{% endif %};
-		$('#{{ id }}-tooltip-{{ loop.index0 }}').offset({ top: offset.top + pos_y - 2, left: offset.left + pos_x - 1 });
-        ctx.{% if loop.index0 == 0 %}moveTo{% else %}lineTo{% endif -%}
-        (pos_x, pos_y);
-{%- endfor %}
-        ctx.stroke();
-      }
-
-      // Update graphs
-      $(function() { {{ name }}(); $(".peak-{{ id }}").css('border-color', '{{ color }}') });
-      $(window).resize(function() { {{ name }}(); });
-      // End drawing graph for {{ id }}
+{% macro draw_plot_tooltips(id, graph) -%}
+{%- for stop in graph.timeline -%}
+<div id="{{ id }}-tooltip-{{ loop.index0 }}" class="peak peak-{{ id }} tooltip-{% if loop.first %}right{% elif loop.last %}left{% else %}top{% endif %}" title="{{ stop|date(graph.format) }}<br><strong>{{ graph.stat[loop.index0]|intcomma }}</strong>"></div>
+{%- endfor -%}
+{%- endmacro %}
+
+{% macro draw_plot(name, id, max, variables=[], color='#729fcf') -%}
+      // Begin drawing graph for {{ id }}
+      function {{ name }}() {
+        var margin = 12;
+        var margin_end = margin * 2;
+        var canvas = document.getElementById('{{ id }}');
+        var canvas_parent = $(canvas).parent();
+        var offset = canvas_parent.offset();
+        canvas.width = canvas_parent.width();
+        var ctx = canvas.getContext('2d');
+
+        // Guidelines
+        ctx.beginPath();
+        ctx.lineWidth = 1;
+        ctx.strokeStyle = "#EEEEEE";
+        ctx.fillStyle = "#CCC";
+		ctx.font = "10pt Helvetica";
+        ctx.moveTo(margin, margin + 0.5);
+        ctx.lineTo(canvas.width - margin, margin + 0.5);
+{%- if max > 0 %}
+{%- for line in [25, 50, 75] %}
+{%- if max > 3 %}
+{% set y_scales = 4 %}
+{%- else -%}
+{% set y_scales = max %}
+{% endif -%}
+{% if loop.index < max and loop.index < 4 %}
+		{%- set y_label = max - (loop.index / y_scales * max)|int -%}
+        y_pos = margin + Math.round((canvas.height - margin_end) * ({{ loop.index }} / {{ max }})) + 0.5;
+        ctx.moveTo(margin, y_pos);
+        ctx.lineTo(canvas.width - margin, y_pos);
+        ctx.fillText("{{ y_label }}", margin + 4, y_pos - 4.5);
+        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.25), y_pos - 4.5);
+        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.5), y_pos - 4.5);
+        ctx.fillText("{{ y_label }}", margin + Math.round((canvas.width - margin_end) * 0.75), y_pos - 4.5);
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+        ctx.stroke();
+
+        // Final guide
+        ctx.beginPath();
+        ctx.strokeStyle = "#999999";
+        ctx.moveTo(margin, canvas.height - margin + 0.5);
+        ctx.lineTo(canvas.width - margin, canvas.height - margin + 0.5);
+        ctx.fillText("0", margin + 4, canvas.height - margin - 4);
+        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.25), canvas.height - margin - 4);
+        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.5), canvas.height - margin - 4);
+        ctx.fillText("0", margin + Math.round((canvas.width - margin_end) * 0.75), canvas.height - margin - 4);
+        ctx.stroke();
+
+        // Graph line
+        ctx.beginPath();
+        ctx.lineWidth = 4;
+        ctx.strokeStyle = "{{ color }}";
+        step_size = (canvas.width - margin_end) / {{ variables|length - 1 }};
+{%- for stop in variables %}
+        pos_x = margin + ({{ loop.index0 }} * step_size);
+        pos_y = {% if max > 0 %}margin + Math.round((canvas.height - margin_end) * {{ ((max - stop) / max)|round(5) }}) + 0.5{% else %}canvas.height - margin{% endif %};
+		$('#{{ id }}-tooltip-{{ loop.index0 }}').offset({ top: offset.top + pos_y - 2, left: offset.left + pos_x - 1 });
+        ctx.{% if loop.index0 == 0 %}moveTo{% else %}lineTo{% endif -%}
+        (pos_x, pos_y);
+{%- endfor %}
+        ctx.stroke();
+      }
+
+      // Update graphs
+      $(function() { {{ name }}(); $(".peak-{{ id }}").css('border-color', '{{ color }}') });
+      $(window).resize(function() { {{ name }}(); });
+      // End drawing graph for {{ id }}
 {%- endmacro %}
 {%- endmacro %}

+ 25 - 25
templates/admin/team/list.html

@@ -1,26 +1,26 @@
-{% extends "admin/admin/list.html" %}
-
-{% block action_body scoped %}
-<table class="table table-striped table-users">
-  <thead>
-    <tr>
-      <th colspan="2">{% trans %}Forum Team Members{% endtrans %}</th>
-    </tr>
-  </thead>
-  <tbody>   	
-    <tr>
-      {% for user in items %} 
-      <td class="colspan6">
-          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}"><img src="{{ user.get_avatar(42) }}" class="avatar" alt=""> <strong>{{ user.username }}</strong></a>
-      </td>{% if loop.last and loop.index is odd %}
-      <td class="span6">
-      	&nbsp;
-      </td>
-      {% endif %}{% if not loop.last and loop.index is even %}
-    </tr>
-    <tr>{% endif %}
-      {% endfor %}
-    </tr>
-  </tbody>
-</table>
+{% extends "admin/admin/list.html" %}
+
+{% block action_body scoped %}
+<table class="table table-striped table-users">
+  <thead>
+    <tr>
+      <th colspan="2">{% trans %}Forum Team Members{% endtrans %}</th>
+    </tr>
+  </thead>
+  <tbody>   	
+    <tr>
+      {% for user in items %} 
+      <td class="colspan6">
+          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}"><img src="{{ user.get_avatar(42) }}" class="avatar" alt=""> <strong>{{ user.username }}</strong></a>
+      </td>{% if loop.last and loop.index is odd %}
+      <td class="span6">
+      	&nbsp;
+      </td>
+      {% endif %}{% if not loop.last and loop.index is even %}
+    </tr>
+    <tr>{% endif %}
+      {% endfor %}
+    </tr>
+  </tbody>
+</table>
 {% endblock %}
 {% endblock %}

+ 11 - 11
templates/admin/todo.html

@@ -1,11 +1,11 @@
-{% extends "admin/layout.html" %}
-{% from "admin/macros.html" import page_title %}
-
-{% block title %}{{ page_title(title=_('Unimplemented Admin Action')) }}{% endblock %}
-
-{% block content %}
-<div class="page-header">
-  <h1>{% trans %}Unimplemented Admin Action{% endtrans %} <small>{% trans %}Placeholder Page{% endtrans %}</small></h1>
-</div>
-<p>{% trans %}This is placeholder page unimplemented admin actions can refer to.{% endtrans %}</p>
-{% endblock %}
+{% extends "admin/layout.html" %}
+{% from "admin/macros.html" import page_title %}
+
+{% block title %}{{ page_title(title=_('Unimplemented Admin Action')) }}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% trans %}Unimplemented Admin Action{% endtrans %} <small>{% trans %}Placeholder Page{% endtrans %}</small></h1>
+</div>
+<p>{% trans %}This is placeholder page unimplemented admin actions can refer to.{% endtrans %}</p>
+{% endblock %}

+ 35 - 35
templates/admin/users/list.html

@@ -1,35 +1,35 @@
-{% extends "admin/admin/list.html" %}
-
-{% block action_body %}
-{% if monitor.users_inactive|int > 0 %}
-<div class="alert alert-info alert-form">
-  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
-  <p><a href="{{ url('admin_users_inactive') }}">{%- trans count=monitor.users_inactive|int, total=monitor.users_inactive|int|intcomma -%}
-  There is one inactive user.
-  {%- pluralize -%}
-  There are {{ total }} inactive users.
-  {%- endtrans -%}</a></p>
-</div>
-{% endif %}
-
-{{ super() }}
-{% endblock %}
-
-{% block table_head scoped %}
-  <th>&nbsp;</th>
-  {{ super() }}
-{% endblock %}
-
-{% block table_row scoped %}
-  <td class="avatar-small"><img src="{{ item.get_avatar(28) }}" class="avatar-small" alt=""></td>
-  <td class="lead-cell">
-  	<strong><a href="{{ url('user', username=item.username_slug, user=item.pk) }}">{{ item.username }}</a></strong> <span class="muted">{{ item.email }}</span>{% if item.activation > 0 %} <span class="label tooltip-top" title="{% if item.activation == 1 -%}
-  	{% trans %}This user has not yet validated his e-mail address.{% endtrans %}
-  	{%- else -%}
-  	{% trans %}This user is awaiting admin approval.{% endtrans %}
-  	{%- endif %}">{% trans %}Inactive{% endtrans %}</span>{% endif %}
-  </td>
-  <td>
-	{{ item.join_date|date("DATETIME_FORMAT") }}
-  </td>
-{% endblock%}
+{% extends "admin/admin/list.html" %}
+
+{% block action_body %}
+{% if monitor.users_inactive|int > 0 %}
+<div class="alert alert-info alert-form">
+  <div class="alert-icon"><span><i class="icon-info-sign icon-white"></i></span></div>
+  <p><a href="{{ url('admin_users_inactive') }}">{%- trans count=monitor.users_inactive|int, total=monitor.users_inactive|int|intcomma -%}
+  There is one inactive user.
+  {%- pluralize -%}
+  There are {{ total }} inactive users.
+  {%- endtrans -%}</a></p>
+</div>
+{% endif %}
+
+{{ super() }}
+{% endblock %}
+
+{% block table_head scoped %}
+  <th>&nbsp;</th>
+  {{ super() }}
+{% endblock %}
+
+{% block table_row scoped %}
+  <td class="avatar-small"><img src="{{ item.get_avatar(28) }}" class="avatar-small" alt=""></td>
+  <td class="lead-cell">
+  	<strong><a href="{{ url('user', username=item.username_slug, user=item.pk) }}">{{ item.username }}</a></strong> <span class="muted">{{ item.email }}</span>{% if item.activation > 0 %} <span class="label tooltip-top" title="{% if item.activation == 1 -%}
+  	{% trans %}This user has not yet validated his e-mail address.{% endtrans %}
+  	{%- else -%}
+  	{% trans %}This user is awaiting admin approval.{% endtrans %}
+  	{%- endif %}">{% trans %}Inactive{% endtrans %}</span>{% endif %}
+  </td>
+  <td>
+	{{ item.join_date|date("DATETIME_FORMAT") }}
+  </td>
+{% endblock%}

+ 71 - 71
templates/cranefly/alerts.html

@@ -1,72 +1,72 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{% if user.alerts -%}
-{{ macros.page_title(title=get_title(),parent=_('Your Notifications')) }}
-{%- else -%}
-{{ macros.page_title(title=get_title()) }}
-{%- endif %}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <h1>{% if user.alerts %}{{ get_title() }} <small>{% trans %}Your Notifications{% endtrans %}</small>
-      {%- else -%}
-      {% trans %}Your Notifications{% endtrans %}{% endif %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if alerts %}
-  <div class="user-alerts">
-    {% if alerts.today %}{{ alerts_list(lang_today(), alerts.today) }}{% endif %}
-    {% if alerts.yesterday %}{{ alerts_list(lang_yesterday(), alerts.yesterday) }}{% endif %}
-    {% if alerts.week %}{{ alerts_list(lang_week(), alerts.week) }}{% endif %}
-    {% if alerts.month %}{{ alerts_list(lang_month(), alerts.month) }}{% endif %}
-    {% if alerts.older %}{{ alerts_list(lang_older(), alerts.older) }}{% endif %}
-  </div>
-  {% else %}
-  <p class="lead">{% trans %}Looks like you don't have any notifications... yet.{% endtrans %}</p>
-  {% endif %}
-</div>
-{% endblock %}
-
-{% macro get_title() -%}
-{% if new_alerts -%}
-{% trans alerts=new_alerts -%}
-You have one new alert
-{%- pluralize -%}
-You have {{ alerts }} new alerts
-{%- endtrans %}
-{%- else -%}
-{% trans %}Your Notifications{% endtrans %}
-{%- endif %}
-{%- endmacro %}
-
-{% macro alerts_list(title, alerts) %}
-  <table class="table table-striped">
-    <thead>
-      <tr>
-        <th style="width: 1%;">&nbsp;</th>
-        <th colspan="2">{{ title }}</th>
-      </tr>
-    </thead>
-    <tbody>
-      {% for alert in alerts %}
-      <tr>
-        <td class="alert-icon"><span class="label {% if alert.new %} label-warning{% endif %} tooltip-top" title="{% if alert.new %}{% trans %}New notification{% endtrans %}{% else %}{% trans %}Old notification{% endtrans %}{% endif %}"><i class="icon-fire"></i></label></td>
-        <td class="alert-message">{{ (_(alert.message) % alert.vars())|safe }}</td>
-        <td class="alert-date">{{ alert.date|reltimesince }}</td>
-      </tr>
-      {% endfor %}
-    </tbody>
-  </table>
-{% endmacro %}
-
-{# Language strings macros #}
-{% macro lang_today() -%}{% trans %}Today Notifications{% endtrans %}{%- endmacro %}
-{% macro lang_yesterday() -%}{% trans %}Yesterday Notifications{% endtrans %}{%- endmacro %}
-{% macro lang_week() -%}{% trans %}This Week{% endtrans %}{%- endmacro %}
-{% macro lang_month() -%}{% trans %}This Month{% endtrans %}{%- endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{% if user.alerts -%}
+{{ macros.page_title(title=get_title(),parent=_('Your Notifications')) }}
+{%- else -%}
+{{ macros.page_title(title=get_title()) }}
+{%- endif %}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% if user.alerts %}{{ get_title() }} <small>{% trans %}Your Notifications{% endtrans %}</small>
+      {%- else -%}
+      {% trans %}Your Notifications{% endtrans %}{% endif %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if alerts %}
+  <div class="user-alerts">
+    {% if alerts.today %}{{ alerts_list(lang_today(), alerts.today) }}{% endif %}
+    {% if alerts.yesterday %}{{ alerts_list(lang_yesterday(), alerts.yesterday) }}{% endif %}
+    {% if alerts.week %}{{ alerts_list(lang_week(), alerts.week) }}{% endif %}
+    {% if alerts.month %}{{ alerts_list(lang_month(), alerts.month) }}{% endif %}
+    {% if alerts.older %}{{ alerts_list(lang_older(), alerts.older) }}{% endif %}
+  </div>
+  {% else %}
+  <p class="lead">{% trans %}Looks like you don't have any notifications... yet.{% endtrans %}</p>
+  {% endif %}
+</div>
+{% endblock %}
+
+{% macro get_title() -%}
+{% if new_alerts -%}
+{% trans alerts=new_alerts -%}
+You have one new alert
+{%- pluralize -%}
+You have {{ alerts }} new alerts
+{%- endtrans %}
+{%- else -%}
+{% trans %}Your Notifications{% endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+{% macro alerts_list(title, alerts) %}
+  <table class="table table-striped">
+    <thead>
+      <tr>
+        <th style="width: 1%;">&nbsp;</th>
+        <th colspan="2">{{ title }}</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for alert in alerts %}
+      <tr>
+        <td class="alert-icon"><span class="label {% if alert.new %} label-warning{% endif %} tooltip-top" title="{% if alert.new %}{% trans %}New notification{% endtrans %}{% else %}{% trans %}Old notification{% endtrans %}{% endif %}"><i class="icon-fire"></i></label></td>
+        <td class="alert-message">{{ (_(alert.message) % alert.vars())|safe }}</td>
+        <td class="alert-date">{{ alert.date|reltimesince }}</td>
+      </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+{% endmacro %}
+
+{# Language strings macros #}
+{% macro lang_today() -%}{% trans %}Today Notifications{% endtrans %}{%- endmacro %}
+{% macro lang_yesterday() -%}{% trans %}Yesterday Notifications{% endtrans %}{%- endmacro %}
+{% macro lang_week() -%}{% trans %}This Week{% endtrans %}{%- endmacro %}
+{% macro lang_month() -%}{% trans %}This Month{% endtrans %}{%- endmacro %}
 {% macro lang_older() -%}{% trans %}Older Notifications{% endtrans %}{%- endmacro %}
 {% macro lang_older() -%}{% trans %}Older Notifications{% endtrans %}{%- endmacro %}

+ 21 - 21
templates/cranefly/base.html

@@ -1,22 +1,22 @@
-{% from "cranefly/macros.html" import page_title -%}
-<!DOCTYPE html>
-<html lang="{{ LANGUAGE_CODE }}">
-  <head>
-    <meta charset="utf-8">
-    <title>{% block title %}{{ page_title() }}{% endblock %}</title>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">{% block meta %}{% endblock %}
-    <link href="{{ STATIC_URL }}cranefly/css/cranefly.css" rel="stylesheet">{% block stylesheets %}{% endblock %}
-    <link rel="shortcut icon" href="{{ STATIC_URL }}favicon.ico">
-  </head>
-  <body itemscope itemtype="http://schema.org/WebPage"{% block body_class %}{% endblock %}>
-  	{% block body %}{% endblock %}
-
-  	<script src="{{ STATIC_URL }}cranefly/js/jquery-1.7.2.min.js"></script>
-  	<script src="{{ STATIC_URL }}cranefly/js/bootstrap.min.js"></script>
-    <script type="text/javascript">
-      var l_img_broken_msg = "{{ _('Image could not be loaded.') }}";
-    </script>
-  	<script src="{{ STATIC_URL }}cranefly/js/cranefly.js"></script>{% block javascripts %}{% endblock %}
-    {{ hook_append_extra|safe }}
-  </body>
+{% from "cranefly/macros.html" import page_title -%}
+<!DOCTYPE html>
+<html lang="{{ LANGUAGE_CODE }}">
+  <head>
+    <meta charset="utf-8">
+    <title>{% block title %}{{ page_title() }}{% endblock %}</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">{% block meta %}{% endblock %}
+    <link href="{{ STATIC_URL }}cranefly/css/cranefly.css" rel="stylesheet">{% block stylesheets %}{% endblock %}
+    <link rel="shortcut icon" href="{{ STATIC_URL }}favicon.ico">
+  </head>
+  <body itemscope itemtype="http://schema.org/WebPage"{% block body_class %}{% endblock %}>
+  	{% block body %}{% endblock %}
+
+  	<script src="{{ STATIC_URL }}cranefly/js/jquery-1.7.2.min.js"></script>
+  	<script src="{{ STATIC_URL }}cranefly/js/bootstrap.min.js"></script>
+    <script type="text/javascript">
+      var l_img_broken_msg = "{{ _('Image could not be loaded.') }}";
+    </script>
+  	<script src="{{ STATIC_URL }}cranefly/js/cranefly.js"></script>{% block javascripts %}{% endblock %}
+    {{ hook_append_extra|safe }}
+  </body>
 </html>
 </html>

+ 114 - 114
templates/cranefly/category.html

@@ -1,115 +1,115 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=category.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li class="active">{{ category.name }}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{{ category.name }}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if category.description %}
-  <div class="markdown lead page-description">
-    {{ category.description_preparsed|markdown_final|safe }}
-  </div>
-  {% endif %}
-  {% if category.subforums %}
-  <div id="subforums" class="category-forums-list{% if category.style %} category-forums-{{ category.style }}{% endif %}">
-    {% for forum in category.subforums %}
-    <div class="forum{% if loop.last %} last{% endif %}">
-      <div class="forum-icon">
-        <div class="forum-icon-wrap{% if forum.type == 'redirect' %} forum-icon-redirect{% elif not forum.is_read %} forum-icon-new{% endif %}"><i class="icon-{% if forum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %} icon-white"></i></div>
-      </div>
-      <div id="forum-{{ forum.id }}" class="forum-main">
-        <h3 class="forum-title{% if not forum.is_read %} forum-title-new{% endif %}"><a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}">{{ forum.name }}</a></h3>
-        {% if forum.show_details %}
-        <div class="forum-details">
-          {% if forum.type != 'redirect' %}
-          {% if acl.forums.can_browse(forum) and (acl.threads.can_read_threads(forum) == 2 or (acl.threads.can_read_threads(forum) == 1 and forum.last_poster_id == user.pk)) %}
-          {% if forum.last_thread_id -%}
-          <div class="thread-name">
-            <a href="{{ url('thread_new', thread=forum.last_thread_id, slug=forum.last_thread_slug) }}"{% if forum.last_thread_name|length > 34 %} class="tooltip-top" title="{{ forum.last_thread_name }}"{% endif %}>{{ forum.last_thread_name|short_string(34) }}</a>
-          </div>
-          <div class="muted">{% if forum.last_poster_id %}<a href="{{ url('user', user=forum.last_poster_id, username=forum.last_poster_slug) }}" class="last-poster">{{ forum.last_poster_name }}</a>{% else %}<span class="last-poster">{{ forum.last_poster_name }}</span>{% endif %} - {{ forum.last_thread_date|reltimesince }}</div>
-          {%- else -%}
-          <em>{% trans %}This forum is empty{% endtrans %}</em>
-          {%- endif %}
-          {%- else -%}
-          <em>{% trans %}This forum is protected{% endtrans %}</em>
-          {%- endif %}
-          {%- else -%}
-          <div class="thread-name">
-            <a href="{{ url('redirect', slug=forum.slug, forum=forum.id) }}">{{ forum.redirect_domain() }}</a>
-          </div>
-          <div class="muted">{% trans count=forum.redirects, clicks=macros.wrap(forum.redirects|intcomma, 'span', 'class="last-poster"') %}{{ clicks }} click{% pluralize %}{{ clicks }} clicks{% endtrans %}</div>
-          {%- endif %}
-        </div>
-        {% endif %}
-        {% if forum.subforums %}
-        <div class="dropdown">
-          {% if forum.subforums|length > 1 %}
-          <a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}#subforums" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-chevron-down"></i> {% trans %}Subforums{% endtrans %}</a>
-          <div class="dropdown-menu" role="menu" aria-labelledby="dLabel">
-            <div class="dropdown-shadow">
-              <ul>
-                {% for subforum in forum.subforums %}
-                <li><a href="{{ url(subforum.type, slug=subforum.slug, forum=subforum.id) }}"><i class="icon-{% if subforum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %}"></i> {{ subforum.name }}</a></li>
-                {% endfor %}
-              </ul>
-            </div>
-          </div>
-          {% else %}
-          <a href="{{ url(forum.subforums[0].type, slug=forum.subforums[0].slug, forum=forum.subforums[0].id) }}" class="subforum tooltip-top" title="{% trans forum=forum.subforums[0].name %}Go to the {{ forum }} subforum{% endtrans %}">{{ forum.subforums[0].name|short_string(16) }}</a>
-          {% endif %}
-        </div>
-        {% endif%}
-        <div class="hide forum-meta">
-          {% if forum.description %}<p class="forum-description">{{ forum.description }}</p>{% endif %}
-          <div class="forum-stats">
-            {% if forum.type != 'redirect' %}
-            <span>{% trans %}Posts{% endtrans %}: <strong>{{ forum.posts|intcomma }}</strong></span>
-            {% trans %}Threads{% endtrans %}: <strong>{{ forum.threads|intcomma }}</strong>
-            {% else %}
-            {% trans %}Clicks{% endtrans %}: <strong>{{ forum.redirects|intcomma }}</strong>
-            {% endif %}
-          </div>
-        </div>
-      </div>
-    </div>
-    {% endfor %}
-  </div>
-  {% else %}
-  <p class="lead">{% trans %}Looks like there are no forums to display in this category.{% endtrans %}</p>
-  {% endif %}
-</div>
-{% endblock %}
-
-{% block javascripts -%}{{ super() }}
-  <script type="text/javascript">
-    $(function () {
-      function populateForumTooltip(target) {
-        return $('#forum-' + target + ' .forum-meta').html();
-      };
-      {% for forum in category.subforums %}
-        $('#forum-{{ forum.id }} .forum-title').tooltip({
-          template: '<div class="tooltip forum-meta-tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
-          placement: 'right',
-          html: true,
-          title: populateForumTooltip({{ forum.id }})
-        });
-      {% endfor %}
-    });
-  </script>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=category.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li class="active">{{ category.name }}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{{ category.name }}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if category.description %}
+  <div class="markdown lead page-description">
+    {{ category.description_preparsed|markdown_final|safe }}
+  </div>
+  {% endif %}
+  {% if category.subforums %}
+  <div id="subforums" class="category-forums-list{% if category.style %} category-forums-{{ category.style }}{% endif %}">
+    {% for forum in category.subforums %}
+    <div class="forum{% if loop.last %} last{% endif %}">
+      <div class="forum-icon">
+        <div class="forum-icon-wrap{% if forum.type == 'redirect' %} forum-icon-redirect{% elif not forum.is_read %} forum-icon-new{% endif %}"><i class="icon-{% if forum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %} icon-white"></i></div>
+      </div>
+      <div id="forum-{{ forum.id }}" class="forum-main">
+        <h3 class="forum-title{% if not forum.is_read %} forum-title-new{% endif %}"><a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}">{{ forum.name }}</a></h3>
+        {% if forum.show_details %}
+        <div class="forum-details">
+          {% if forum.type != 'redirect' %}
+          {% if acl.forums.can_browse(forum) and (acl.threads.can_read_threads(forum) == 2 or (acl.threads.can_read_threads(forum) == 1 and forum.last_poster_id == user.pk)) %}
+          {% if forum.last_thread_id -%}
+          <div class="thread-name">
+            <a href="{{ url('thread_new', thread=forum.last_thread_id, slug=forum.last_thread_slug) }}"{% if forum.last_thread_name|length > 34 %} class="tooltip-top" title="{{ forum.last_thread_name }}"{% endif %}>{{ forum.last_thread_name|short_string(34) }}</a>
+          </div>
+          <div class="muted">{% if forum.last_poster_id %}<a href="{{ url('user', user=forum.last_poster_id, username=forum.last_poster_slug) }}" class="last-poster">{{ forum.last_poster_name }}</a>{% else %}<span class="last-poster">{{ forum.last_poster_name }}</span>{% endif %} - {{ forum.last_thread_date|reltimesince }}</div>
+          {%- else -%}
+          <em>{% trans %}This forum is empty{% endtrans %}</em>
+          {%- endif %}
+          {%- else -%}
+          <em>{% trans %}This forum is protected{% endtrans %}</em>
+          {%- endif %}
+          {%- else -%}
+          <div class="thread-name">
+            <a href="{{ url('redirect', slug=forum.slug, forum=forum.id) }}">{{ forum.redirect_domain() }}</a>
+          </div>
+          <div class="muted">{% trans count=forum.redirects, clicks=macros.wrap(forum.redirects|intcomma, 'span', 'class="last-poster"') %}{{ clicks }} click{% pluralize %}{{ clicks }} clicks{% endtrans %}</div>
+          {%- endif %}
+        </div>
+        {% endif %}
+        {% if forum.subforums %}
+        <div class="dropdown">
+          {% if forum.subforums|length > 1 %}
+          <a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}#subforums" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-chevron-down"></i> {% trans %}Subforums{% endtrans %}</a>
+          <div class="dropdown-menu" role="menu" aria-labelledby="dLabel">
+            <div class="dropdown-shadow">
+              <ul>
+                {% for subforum in forum.subforums %}
+                <li><a href="{{ url(subforum.type, slug=subforum.slug, forum=subforum.id) }}"><i class="icon-{% if subforum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %}"></i> {{ subforum.name }}</a></li>
+                {% endfor %}
+              </ul>
+            </div>
+          </div>
+          {% else %}
+          <a href="{{ url(forum.subforums[0].type, slug=forum.subforums[0].slug, forum=forum.subforums[0].id) }}" class="subforum tooltip-top" title="{% trans forum=forum.subforums[0].name %}Go to the {{ forum }} subforum{% endtrans %}">{{ forum.subforums[0].name|short_string(16) }}</a>
+          {% endif %}
+        </div>
+        {% endif%}
+        <div class="hide forum-meta">
+          {% if forum.description %}<p class="forum-description">{{ forum.description }}</p>{% endif %}
+          <div class="forum-stats">
+            {% if forum.type != 'redirect' %}
+            <span>{% trans %}Posts{% endtrans %}: <strong>{{ forum.posts|intcomma }}</strong></span>
+            {% trans %}Threads{% endtrans %}: <strong>{{ forum.threads|intcomma }}</strong>
+            {% else %}
+            {% trans %}Clicks{% endtrans %}: <strong>{{ forum.redirects|intcomma }}</strong>
+            {% endif %}
+          </div>
+        </div>
+      </div>
+    </div>
+    {% endfor %}
+  </div>
+  {% else %}
+  <p class="lead">{% trans %}Looks like there are no forums to display in this category.{% endtrans %}</p>
+  {% endif %}
+</div>
+{% endblock %}
+
+{% block javascripts -%}{{ super() }}
+  <script type="text/javascript">
+    $(function () {
+      function populateForumTooltip(target) {
+        return $('#forum-' + target + ' .forum-meta').html();
+      };
+      {% for forum in category.subforums %}
+        $('#forum-{{ forum.id }} .forum-title').tooltip({
+          template: '<div class="tooltip forum-meta-tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+          placement: 'right',
+          html: true,
+          title: populateForumTooltip({{ forum.id }})
+        });
+      {% endfor %}
+    });
+  </script>
 {%- endblock %}
 {%- endblock %}

+ 45 - 45
templates/cranefly/editor.html

@@ -1,46 +1,46 @@
-{% macro editor(field, submit_button, placeholder=None, rows=4, hide_links=False, hide_images=False, hide_hr=False, extra=None) %}
-<div class="editor">
-  {% if field.errors %}
-  <div class="editor-error">
-    {% for error in field.errors %}
-    <p class="help-block">{{ error }}</p>
-    {% endfor %}
-  </div>
-  {% endif %}
-  <div class="editor-input">
-    <div>
-      <textarea name="{{ field.html_name }}" id="{{ field.html_id }}" rows="{{ rows }}"{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}>{% if field.has_value %}{{ field.value }}{% endif %}</textarea>
-    </div>
-  </div>
-  <div class="editor-actions">
-    <ul class="editor-tools unstyled pull-left">
-      <li><a href="#" class="tooltip-top btn editor-bold" title="{% trans %}Bold{% endtrans %}"><i class="icon-bold"></i></a></li>
-      <li><a href="#" class="tooltip-top btn editor-emphasis" title="{% trans %}Emphasis{% endtrans %}"><i class="icon-italic"></i></a></li>
-      {% if not hide_links %}<li class="divider"><a href="#" class="tooltip-top btn editor-link" title="{% trans %}Insert Link{% endtrans %}"><i class="icon-share-alt"></i></a></li>{% endif %}
-      {% if not hide_images %}<li{% if hide_links %} class="divider"{% endif %}><a href="#" class="tooltip-top btn editor-image" title="{% trans %}Insert Image{% endtrans %}"><i class="icon-picture"></i></a></li>{% endif %}
-      {% if not hide_hr %}<li class="divider"><a href="#" class="tooltip-top btn editor-hr" title="{% trans %}Insert Horizontal Line{% endtrans %}"><i class="icon-minus"></i></a></li>{% endif %}
-    </ul>
-    <button name="save" type="submit" class="btn btn-primary pull-right">{{ submit_button }}</button>
-    {% if extra %}{{ extra }}{% endif %}
-  </div>
-</div>
-{% endmacro %}
-
-{% macro js() %}
-  <script type="text/javascript">
-    $(function () {
-      ed_lang_enter_link_url = "{% trans %}Enter link address{% endtrans %}";
-      ed_lang_enter_link_label = "{% trans %}Enter link label (optional){% endtrans %}";
-      ed_lang_enter_image_url = "{% trans %}Enter image address{% endtrans %}";
-      ed_lang_enter_image_label = "{% trans %}Enter image label{% endtrans %}";
-      ed_emojis = ['{{ ("', '".join(emojis))|safe }}'];
-      ed_emoji_tpl = "<li data-value='${key}'><img src='{{ STATIC_URL }}emojis/${name}.png' height='20' width='20'/> ${name} </li>";
-      ed_emojis_list = $.map(ed_emojis, function(value, i) {
-        return {'id':i, 'key':value+":", 'name':value};
-      });
-    });
-  </script>
-  <script src="{{ STATIC_URL }}cranefly/js/jquery.caret.js"></script>
-  <script src="{{ STATIC_URL }}cranefly/js/jquery.atwho.js"></script>
-  <script src="{{ STATIC_URL }}cranefly/js/editor.js"></script>
+{% macro editor(field, submit_button, placeholder=None, rows=4, hide_links=False, hide_images=False, hide_hr=False, extra=None) %}
+<div class="editor">
+  {% if field.errors %}
+  <div class="editor-error">
+    {% for error in field.errors %}
+    <p class="help-block">{{ error }}</p>
+    {% endfor %}
+  </div>
+  {% endif %}
+  <div class="editor-input">
+    <div>
+      <textarea name="{{ field.html_name }}" id="{{ field.html_id }}" rows="{{ rows }}"{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}>{% if field.has_value %}{{ field.value }}{% endif %}</textarea>
+    </div>
+  </div>
+  <div class="editor-actions">
+    <ul class="editor-tools unstyled pull-left">
+      <li><a href="#" class="tooltip-top btn editor-bold" title="{% trans %}Bold{% endtrans %}"><i class="icon-bold"></i></a></li>
+      <li><a href="#" class="tooltip-top btn editor-emphasis" title="{% trans %}Emphasis{% endtrans %}"><i class="icon-italic"></i></a></li>
+      {% if not hide_links %}<li class="divider"><a href="#" class="tooltip-top btn editor-link" title="{% trans %}Insert Link{% endtrans %}"><i class="icon-share-alt"></i></a></li>{% endif %}
+      {% if not hide_images %}<li{% if hide_links %} class="divider"{% endif %}><a href="#" class="tooltip-top btn editor-image" title="{% trans %}Insert Image{% endtrans %}"><i class="icon-picture"></i></a></li>{% endif %}
+      {% if not hide_hr %}<li class="divider"><a href="#" class="tooltip-top btn editor-hr" title="{% trans %}Insert Horizontal Line{% endtrans %}"><i class="icon-minus"></i></a></li>{% endif %}
+    </ul>
+    <button name="save" type="submit" class="btn btn-primary pull-right">{{ submit_button }}</button>
+    {% if extra %}{{ extra }}{% endif %}
+  </div>
+</div>
+{% endmacro %}
+
+{% macro js() %}
+  <script type="text/javascript">
+    $(function () {
+      ed_lang_enter_link_url = "{% trans %}Enter link address{% endtrans %}";
+      ed_lang_enter_link_label = "{% trans %}Enter link label (optional){% endtrans %}";
+      ed_lang_enter_image_url = "{% trans %}Enter image address{% endtrans %}";
+      ed_lang_enter_image_label = "{% trans %}Enter image label{% endtrans %}";
+      ed_emojis = ['{{ ("', '".join(emojis))|safe }}'];
+      ed_emoji_tpl = "<li data-value='${key}'><img src='{{ STATIC_URL }}emojis/${name}.png' height='20' width='20'/> ${name} </li>";
+      ed_emojis_list = $.map(ed_emojis, function(value, i) {
+        return {'id':i, 'key':value+":", 'name':value};
+      });
+    });
+  </script>
+  <script src="{{ STATIC_URL }}cranefly/js/jquery.caret.js"></script>
+  <script src="{{ STATIC_URL }}cranefly/js/jquery.atwho.js"></script>
+  <script src="{{ STATIC_URL }}cranefly/js/editor.js"></script>
 {% endmacro %}
 {% endmacro %}

+ 22 - 22
templates/cranefly/error403.html

@@ -1,23 +1,23 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Error')) }}{% endblock %}
-      
-{% block content %}
-<div class="row">
-  <div class="span8 offset2 error-page">
-    <div class="page-header">
-      <h1><span class="error-color">403</span> {% trans %}Permission Denied{% endtrans %}</h1>
-    </div>
-    {% if message %}
-    <p class="lead">{{ message }}</p>
-    {% else %}
-    <p class="lead">{% trans %}You dont have permission to see this page.{% endtrans %}</p>
-    {% endif %}
-    <div class="error-protips">
-      <a href="#" class="go-back"><i class="icon-arrow-left icon-white"></i> {% trans %}Return to previous page{% endtrans %}</a>
-      <a href="{{ url('index') }}"><i class="icon-home icon-white"></i> {% trans %}Return to board index{% endtrans %}</a>
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Error')) }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span8 offset2 error-page">
+    <div class="page-header">
+      <h1><span class="error-color">403</span> {% trans %}Permission Denied{% endtrans %}</h1>
+    </div>
+    {% if message %}
+    <p class="lead">{{ message }}</p>
+    {% else %}
+    <p class="lead">{% trans %}You dont have permission to see this page.{% endtrans %}</p>
+    {% endif %}
+    <div class="error-protips">
+      <a href="#" class="go-back"><i class="icon-arrow-left icon-white"></i> {% trans %}Return to previous page{% endtrans %}</a>
+      <a href="{{ url('index') }}"><i class="icon-home icon-white"></i> {% trans %}Return to board index{% endtrans %}</a>
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 39 - 39
templates/cranefly/error403_banned.html

@@ -1,40 +1,40 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Error')) }}{% endblock %}
-      
-{% block content %}
-<div class="row">
-  <div class="span8 offset2 error-page">
-    <div class="page-header">
-      <h1><span class="error-color">403</span> {% trans %}You Are Banned{% endtrans %}</h1>
-    </div>
-    {% if banned_user %}
-      {% if ban.reason_user %}
-      <p class="lead">{% trans username=banned_user.username %}{{ username }}, your account has been banned for following reason:{% endtrans %}</p>
-      {% else %}
-      <p class="lead">{% trans username=banned_user.username %}{{ username }}, your account has been banned.{% endtrans %}</p>
-      {% endif %}
-    {% else %}
-      {% if ban.reason_user %}
-      <p class="lead">{% trans %}Guest, your IP Address has been banned from accessing this page for following reason:{% endtrans %}</p>
-      {% else %}
-      <p class="lead">{% trans %}Guest, your IP Address has been banned from accessing this page.{% endtrans %}</p>
-      {% endif %}
-    {% endif %}
-    {% if ban.reason_user %}
-    <div class="error-ban-reason">
-      {{ ban.reason_user|markdown|safe }}
-      {% if ban.expires %}
-      <p class="error-ban-expires">{% trans ban_expires=ban.expires|date %}Your ban will expire on {{ ban_expires }}{% endtrans %}</p>
-      {% endif %}
-    </div>
-    {% elif ban.expires %}
-    <p class="error-ban-expires">{% trans ban_expires=ban.expires|date %}Your ban will expire on {{ ban_expires }}{% endtrans %}</p>
-    {% endif %}
-    <div class="error-protips">
-      <a href="{{ url('index') }}"><i class="icon-home icon-white"></i> {% trans %}Return to board index{% endtrans %}</a>
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Error')) }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span8 offset2 error-page">
+    <div class="page-header">
+      <h1><span class="error-color">403</span> {% trans %}You Are Banned{% endtrans %}</h1>
+    </div>
+    {% if banned_user %}
+      {% if ban.reason_user %}
+      <p class="lead">{% trans username=banned_user.username %}{{ username }}, your account has been banned for following reason:{% endtrans %}</p>
+      {% else %}
+      <p class="lead">{% trans username=banned_user.username %}{{ username }}, your account has been banned.{% endtrans %}</p>
+      {% endif %}
+    {% else %}
+      {% if ban.reason_user %}
+      <p class="lead">{% trans %}Guest, your IP Address has been banned from accessing this page for following reason:{% endtrans %}</p>
+      {% else %}
+      <p class="lead">{% trans %}Guest, your IP Address has been banned from accessing this page.{% endtrans %}</p>
+      {% endif %}
+    {% endif %}
+    {% if ban.reason_user %}
+    <div class="error-ban-reason">
+      {{ ban.reason_user|markdown|safe }}
+      {% if ban.expires %}
+      <p class="error-ban-expires">{% trans ban_expires=ban.expires|date %}Your ban will expire on {{ ban_expires }}{% endtrans %}</p>
+      {% endif %}
+    </div>
+    {% elif ban.expires %}
+    <p class="error-ban-expires">{% trans ban_expires=ban.expires|date %}Your ban will expire on {{ ban_expires }}{% endtrans %}</p>
+    {% endif %}
+    <div class="error-protips">
+      <a href="{{ url('index') }}"><i class="icon-home icon-white"></i> {% trans %}Return to board index{% endtrans %}</a>
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 22 - 22
templates/cranefly/error404.html

@@ -1,23 +1,23 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Error')) }}{% endblock %}
-      
-{% block content %}
-<div class="row">
-  <div class="span8 offset2 error-page">
-    <div class="page-header">
-      <h1><span class="error-color">404</span> {% trans %}Page Not Found{% endtrans %}</h1>
-    </div>
-    {% if message %}
-    <p class="lead">{{ message }}</p>
-    {% else %}
-    <p class="lead">{% trans %}The page you are looking for could not be found.{% endtrans %}</p>
-    {% endif %}
-    <div class="error-protips">
-      <a href="#" class="go-back"><i class="icon-arrow-left icon-white"></i> {% trans %}Return to previous page{% endtrans %}</a>
-      <a href="{{ url('index') }}"><i class="icon-home icon-white"></i> {% trans %}Return to board index{% endtrans %}</a>
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Error')) }}{% endblock %}
+      
+{% block content %}
+<div class="row">
+  <div class="span8 offset2 error-page">
+    <div class="page-header">
+      <h1><span class="error-color">404</span> {% trans %}Page Not Found{% endtrans %}</h1>
+    </div>
+    {% if message %}
+    <p class="lead">{{ message }}</p>
+    {% else %}
+    <p class="lead">{% trans %}The page you are looking for could not be found.{% endtrans %}</p>
+    {% endif %}
+    <div class="error-protips">
+      <a href="#" class="go-back"><i class="icon-arrow-left icon-white"></i> {% trans %}Return to previous page{% endtrans %}</a>
+      <a href="{{ url('index') }}"><i class="icon-home icon-white"></i> {% trans %}Return to board index{% endtrans %}</a>
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 74 - 74
templates/cranefly/forum_map.html

@@ -1,75 +1,75 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_("Forum Map")) }}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <h1>{% trans %}Forum Map{% endtrans %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if forums %}
-  <div class="row">
-    <div class="span6">
-      {% for category in forums %}{% if loop.index0 is odd and category.subforums %}
-      {{ draw_category(category) }}
-      {% endif %}{% endfor %}
-    </div>
-    <div class="span6">
-      {% for category in forums %}{% if loop.index0 is even and category.subforums %}
-      {{ draw_category(category) }}
-      {% endif %}{% endfor %}
-    </div>
-  </div>
-  {% else %}
-  <p class="lead">{% trans %}Looks like no forums exist that you have permission to see.{% endtrans %}</p>
-  {% endif %}
-</div>
-{% endblock %}
-
-
-{% macro draw_category(category) %}
-<div class="forum-map-category{% if category.style %} forum-map-category-{{ category.style }}{% endif %}">
-  <div class="header">
-    <h2>{{ category.name }}</h2>
-  </div>
-  {% for forum in category.subforums%}
-  {{ draw_forum(forum) }}
-  {% endfor %}
-</div>
-{% endmacro %}
-
-{% macro draw_forum(forum, depth=0, branch='', last=false) %}
-  <div class="{% if depth -%}
-    forum-map-subforum
-    {%- else -%}
-    forum-map-forum
-    {%- endif %}">
-    <h3>{% if depth %}{% if last -%}
-    {{ draw_tree(branch ~ 'l') }}
-    {%- else -%}
-    {{ draw_tree(branch ~ 't') }}
-    {%- endif %}{% endif %} <a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}">{{ forum.name }}</a></h3>
-  </div>
-  {% for subforum in forum.subforums %}
-    {% if depth %}
-      {% if last %}
-      {{ draw_forum(subforum, (depth + 1), (branch ~ 's'), loop.last) }}
-      {% else %}
-      {{ draw_forum(subforum, (depth + 1), (branch ~ 'i'), loop.last) }}
-      {% endif %}
-    {% else %}
-    {{ draw_forum(subforum, (depth + 1), '', loop.last) }}
-    {% endif %}
-  {% endfor %}
-{% endmacro %}
-
-{% macro draw_tree(branch) %}
-{% for item in branch %}
-<span class="tree-{{ item }}"><span></span></span>
-{% endfor %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_("Forum Map")) }}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% trans %}Forum Map{% endtrans %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if forums %}
+  <div class="row">
+    <div class="span6">
+      {% for category in forums %}{% if loop.index0 is odd and category.subforums %}
+      {{ draw_category(category) }}
+      {% endif %}{% endfor %}
+    </div>
+    <div class="span6">
+      {% for category in forums %}{% if loop.index0 is even and category.subforums %}
+      {{ draw_category(category) }}
+      {% endif %}{% endfor %}
+    </div>
+  </div>
+  {% else %}
+  <p class="lead">{% trans %}Looks like no forums exist that you have permission to see.{% endtrans %}</p>
+  {% endif %}
+</div>
+{% endblock %}
+
+
+{% macro draw_category(category) %}
+<div class="forum-map-category{% if category.style %} forum-map-category-{{ category.style }}{% endif %}">
+  <div class="header">
+    <h2>{{ category.name }}</h2>
+  </div>
+  {% for forum in category.subforums%}
+  {{ draw_forum(forum) }}
+  {% endfor %}
+</div>
+{% endmacro %}
+
+{% macro draw_forum(forum, depth=0, branch='', last=false) %}
+  <div class="{% if depth -%}
+    forum-map-subforum
+    {%- else -%}
+    forum-map-forum
+    {%- endif %}">
+    <h3>{% if depth %}{% if last -%}
+    {{ draw_tree(branch ~ 'l') }}
+    {%- else -%}
+    {{ draw_tree(branch ~ 't') }}
+    {%- endif %}{% endif %} <a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}">{{ forum.name }}</a></h3>
+  </div>
+  {% for subforum in forum.subforums %}
+    {% if depth %}
+      {% if last %}
+      {{ draw_forum(subforum, (depth + 1), (branch ~ 's'), loop.last) }}
+      {% else %}
+      {{ draw_forum(subforum, (depth + 1), (branch ~ 'i'), loop.last) }}
+      {% endif %}
+    {% else %}
+    {{ draw_forum(subforum, (depth + 1), '', loop.last) }}
+    {% endif %}
+  {% endfor %}
+{% endmacro %}
+
+{% macro draw_tree(branch) %}
+{% for item in branch %}
+<span class="tree-{{ item }}"><span></span></span>
+{% endfor %}
 {% endmacro %}
 {% endmacro %}

+ 26 - 26
templates/cranefly/forum_tos.html

@@ -1,27 +1,27 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{% if settings.tos_title -%}
-{{ macros.page_title(title=settings.tos_title) }}
-{%- else -%}
-{{ macros.page_title(title=_('Terms of Service')) }}
-{%- endif %}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <h1>{% if settings.tos_title -%}
-{{ settings.tos_title }}
-{%- else -%}
-{% trans %}Terms of Service{% endtrans %}
-{%- endif %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="markdown">
-    {{ settings.tos_content|markdown|safe }}
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{% if settings.tos_title -%}
+{{ macros.page_title(title=settings.tos_title) }}
+{%- else -%}
+{{ macros.page_title(title=_('Terms of Service')) }}
+{%- endif %}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% if settings.tos_title -%}
+{{ settings.tos_title }}
+{%- else -%}
+{% trans %}Terms of Service{% endtrans %}
+{%- endif %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="markdown">
+    {{ settings.tos_content|markdown|safe }}
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 188 - 188
templates/cranefly/index.html

@@ -1,188 +1,188 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{% if settings.board_index_title %}{{ settings.board_index_title }}{% else %}{{ settings.board_name }}{% endif %}{% endblock %}
-
-{% block meta %}{% if settings.board_index_meta %}
-    <meta name="description" content="{{ settings.board_index_meta }}">
-{%- endif %}{%- endblock %}
-      
-{% block content %}
-{{ hook_above_forum_home|safe }}
-<div class="row">
-  <div class="span8">
-    <div class="index-forums-list">
-      {{ hook_above_home_forums_list|safe }}
-
-      {% for category in forums_list %}{% if category.subforums %}
-      <div id="{{ category.slug }}" class="index-category{% if category.style %} index-category-{{ category.style }}{% endif %}">
-        <div class="header">
-          <h2>{{ category.name }}{% if category.description %} <small>{{ category.description }}</small>{% endif %}</h2>
-        </div>
-        {% for forum in category.subforums %}
-        <div class="forum{% if loop.last %} last{% endif %}">
-          <div class="forum-icon">
-            <div class="forum-icon-wrap{% if forum.type == 'redirect' %} forum-icon-redirect{% elif not forum.is_read %} forum-icon-new{% endif %}"><i class="icon-{% if forum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %} icon-white"></i></div>
-          </div>
-          <div id="forum-{{ forum.id }}" class="forum-main">
-            <h3 class="forum-title{% if not forum.is_read %} forum-title-new{% endif %}"><a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}">{{ forum.name }}</a></h3>
-            {% if forum.show_details %}
-            <div class="forum-details">
-              {% if forum.type != 'redirect' %}
-              {% if acl.forums.can_browse(forum) and (acl.threads.can_read_threads(forum) == 2 or (acl.threads.can_read_threads(forum) == 1 and forum.last_poster_id == user.pk)) %}
-              {% if forum.last_thread_id -%}
-              <div class="thread-name">
-                <a href="{{ url('thread_new', thread=forum.last_thread_id, slug=forum.last_thread_slug) }}"{% if forum.last_thread_name|length > 34 %} class="tooltip-top" title="{{ forum.last_thread_name }}"{% endif %}>{{ forum.last_thread_name|short_string(34) }}</a>
-              </div>
-              <div class="muted">{% if forum.last_poster_id %}<a href="{{ url('user', user=forum.last_poster_id, username=forum.last_poster_slug) }}" class="last-poster">{{ forum.last_poster_name }}</a>{% else %}<span class="last-poster">{{ forum.last_poster_name }}</span>{% endif %} - {{ forum.last_thread_date|reltimesince }}</div>
-              {%- else -%}
-              <em>{% trans %}This forum is empty{% endtrans %}</em>
-              {%- endif %}
-              {%- else -%}
-              <em>{% trans %}This forum is protected{% endtrans %}</em>
-              {%- endif %}
-              {%- else -%}
-              <div class="thread-name">
-                <a href="{{ url('redirect', slug=forum.slug, forum=forum.id) }}">{{ forum.redirect_domain() }}</a>
-              </div>
-              <div class="muted">{% trans count=forum.redirects, clicks=macros.wrap(forum.redirects|intcomma, 'span', 'class="last-poster"') %}{{ clicks }} click{% pluralize %}{{ clicks }} clicks{% endtrans %}</div>
-              {%- endif %}
-            </div>
-            {% endif %}
-            {% if forum.subforums %}
-            <div class="dropdown">
-              {% if forum.subforums|length > 1 %}
-              <a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}#subforums" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-chevron-down"></i> {% trans %}Subforums{% endtrans %}</a>
-              <div class="dropdown-menu" role="menu" aria-labelledby="dLabel">
-                <div class="dropdown-shadow">
-                  <ul>
-                    {% for subforum in forum.subforums %}
-                    <li><a href="{{ url(subforum.type, slug=subforum.slug, forum=subforum.id) }}"><i class="icon-{% if subforum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %}"></i> {{ subforum.name }}</a></li>
-                    {% endfor %}
-                  </ul>
-                </div>
-              </div>
-              {% else %}
-              <a href="{{ url(forum.subforums[0].type, slug=forum.subforums[0].slug, forum=forum.subforums[0].id) }}" class="subforum tooltip-top" title="{% trans forum=forum.subforums[0].name %}Go to the {{ forum }} subforum{% endtrans %}">{{ forum.subforums[0].name|short_string(16) }}</a>
-              {% endif %}
-            </div>
-            {% endif%}
-            <div class="hide forum-meta">
-              {% if forum.description %}<p class="forum-description">{{ forum.description }}</p>{% endif %}
-              <div class="forum-stats">
-                {% if forum.type != 'redirect' %}
-                <span>{% trans %}Posts{% endtrans %}: <strong>{{ forum.posts|intcomma }}</strong></span>
-                {% trans %}Threads{% endtrans %}: <strong>{{ forum.threads|intcomma }}</strong>
-                {% else %}
-                {% trans %}Clicks{% endtrans %}: <strong>{{ forum.redirects|intcomma }}</strong>
-                {% endif %}
-              </div>
-            </div>
-          </div>
-        </div>
-        {% endfor %}
-      </div>
-      {% endif %}{% endfor %}
-
-      {{ hook_below_home_forums_list|safe }}
-    </div>
-  </div>
-  <div class="span4 index-sidebar">
-
-    {{ hook_above_home_sidepanel|safe }}
-
-    {% if ranks_online %}
-    <div class="index-ranks-list">
-      {% for rank in ranks_online %}{% if rank.online %}
-      <div class="inder-rank{% if rank.style %} index-rank-{{ rank.style }}{% endif %}">
-        <h3>{% if rank.slug %}<a href="{{ url('users', slug=rank.slug) }}">{% endif %}{% trans rank_name=_(rank.name) %}{{ rank_name }} Online{% endtrans %}{% if rank.slug %}</a>{% endif %}</h3>
-        <ul class="unstyled">
-          {% for online in rank.online %}
-          <li>
-            <img src="{{ online.get_avatar(24) }}" alt="" class="avatar-small">
-            <a href="{{ url('user', username=online.username_slug, user=online.pk) }}" class="user-name">{{ online.username }}</a>
-            {% if rank.title or online.title %}
-            {% if rank.slug -%}
-            <a href="{{ url('users', slug=rank.slug) }}" class="label">{% if online.title %}{{ online.title }}{% else %}{{ _(rank.title) }}{% endif %}</a>
-            {%- else -%}
-            <span class="label">{% if online.title %}{{ online.title }}{% else %}{{ _(rank.title) }}{% endif %}</span>
-            {% endif %}
-            {% endif %}
-          </li>
-          {% endfor %}
-        </ul>
-      </div>
-      {% endif %}{% endfor %}
-    </div>
-    {% endif %}
-
-    {{ hook_after_home_sidepanel_ranks_online|safe }}
-
-    {% if popular_threads %}
-    <div class="index-popular-threads">
-      <h4>{% trans %}Popular Threads{% endtrans %}</h4>
-      <ul class="unstyled">
-        {% for thread in popular_threads %}
-        <li>
-          <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="index-popular-thread{% if thread.name|length > 42 %} tooltip-top{% endif %}"{% if thread.name|length > 42 %} title="{{ thread.name }}"{% endif %}>{{ thread.name|short_string(42) }}</a>
-          <div class="muted"><a href="{{ url('forum', forum=thread.forum_id, slug=thread.forum_slug) }}">{{ thread.forum_name }}</a> - {{ thread.last|reltimesince }}</div>
-        </li>
-        {% endfor %}
-      </ul>
-    </div>
-    {% endif %}
-
-    {{ hook_after_home_sidepanel_popular_threads|safe }}
-
-    <div class="index-stats">
-      <ul class="unstyled">
-        <li>
-          <span class="tooltip-top" title="{% trans %}Posts{% endtrans %}"><i class="icon-comment"></i> {{ monitor.posts|int|intcomma }}</span>
-        </li>
-        <li>
-          <span class="tooltip-top" title="{% trans %}Threads{% endtrans %}"><i class="icon-th-list"></i> {{ monitor.threads|int|intcomma }}</span>
-        </li>
-        <li>
-          <span class="tooltip-top" title="{% trans %}Members{% endtrans %}"><i class="icon-user"></i> {{ monitor.users|int|intcomma }}</span>
-        </li>
-        {% if settings.online_counting != 'no' %}
-        <li>
-          <span class="tooltip-top" title="{% trans %}Online{% endtrans %}"><i class="icon-map-marker"></i> {{ users_online.members|int|intcomma }} <span class="muted">{{ users_online.all|int|intcomma }}</span></span>
-        </li>
-        {% endif %}
-      </ul>
-    </div>
-
-    {{ hook_after_home_sidepanel_forum_stats|safe }}
-
-    {% if user.is_authenticated() %}
-    <form action="{{ url('read_all') }}" method="post" class="index-forums-read-all">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <button type="submit" class="btn btn-link"><i class="icon-ok"></i> {% trans %}Mark forums read{% endtrans %}</button>
-    </form>
-    {% endif %}
-
-    {{ hook_below_home_sidepanel|safe }}
-
-  </div>
-</div>
-{{ hook_below_forum_home|safe }}
-{% endblock %}
-
-{% block javascripts -%}{{ super() }}
-  <script type="text/javascript">
-    $(function () {
-      function populateForumTooltip(target) {
-        return $('#forum-' + target + ' .forum-meta').html();
-      };
-      {% for category in forums_list %}{% for forum in category.subforums %}
-        $('#forum-{{ forum.id }} .forum-title').tooltip({
-          template: '<div class="tooltip forum-meta-tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
-          placement: 'right',
-          html: true,
-          title: populateForumTooltip({{ forum.id }})
-        });
-      {% endfor %}{% endfor %}
-    });
-  </script>
-{%- endblock %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{% if settings.board_index_title %}{{ settings.board_index_title }}{% else %}{{ settings.board_name }}{% endif %}{% endblock %}
+
+{% block meta %}{% if settings.board_index_meta %}
+    <meta name="description" content="{{ settings.board_index_meta }}">
+{%- endif %}{%- endblock %}
+      
+{% block content %}
+{{ hook_above_forum_home|safe }}
+<div class="row">
+  <div class="span8">
+    <div class="index-forums-list">
+      {{ hook_above_home_forums_list|safe }}
+
+      {% for category in forums_list %}{% if category.subforums %}
+      <div id="{{ category.slug }}" class="index-category{% if category.style %} index-category-{{ category.style }}{% endif %}">
+        <div class="header">
+          <h2>{{ category.name }}{% if category.description %} <small>{{ category.description }}</small>{% endif %}</h2>
+        </div>
+        {% for forum in category.subforums %}
+        <div class="forum{% if loop.last %} last{% endif %}">
+          <div class="forum-icon">
+            <div class="forum-icon-wrap{% if forum.type == 'redirect' %} forum-icon-redirect{% elif not forum.is_read %} forum-icon-new{% endif %}"><i class="icon-{% if forum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %} icon-white"></i></div>
+          </div>
+          <div id="forum-{{ forum.id }}" class="forum-main">
+            <h3 class="forum-title{% if not forum.is_read %} forum-title-new{% endif %}"><a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}">{{ forum.name }}</a></h3>
+            {% if forum.show_details %}
+            <div class="forum-details">
+              {% if forum.type != 'redirect' %}
+              {% if acl.forums.can_browse(forum) and (acl.threads.can_read_threads(forum) == 2 or (acl.threads.can_read_threads(forum) == 1 and forum.last_poster_id == user.pk)) %}
+              {% if forum.last_thread_id -%}
+              <div class="thread-name">
+                <a href="{{ url('thread_new', thread=forum.last_thread_id, slug=forum.last_thread_slug) }}"{% if forum.last_thread_name|length > 34 %} class="tooltip-top" title="{{ forum.last_thread_name }}"{% endif %}>{{ forum.last_thread_name|short_string(34) }}</a>
+              </div>
+              <div class="muted">{% if forum.last_poster_id %}<a href="{{ url('user', user=forum.last_poster_id, username=forum.last_poster_slug) }}" class="last-poster">{{ forum.last_poster_name }}</a>{% else %}<span class="last-poster">{{ forum.last_poster_name }}</span>{% endif %} - {{ forum.last_thread_date|reltimesince }}</div>
+              {%- else -%}
+              <em>{% trans %}This forum is empty{% endtrans %}</em>
+              {%- endif %}
+              {%- else -%}
+              <em>{% trans %}This forum is protected{% endtrans %}</em>
+              {%- endif %}
+              {%- else -%}
+              <div class="thread-name">
+                <a href="{{ url('redirect', slug=forum.slug, forum=forum.id) }}">{{ forum.redirect_domain() }}</a>
+              </div>
+              <div class="muted">{% trans count=forum.redirects, clicks=macros.wrap(forum.redirects|intcomma, 'span', 'class="last-poster"') %}{{ clicks }} click{% pluralize %}{{ clicks }} clicks{% endtrans %}</div>
+              {%- endif %}
+            </div>
+            {% endif %}
+            {% if forum.subforums %}
+            <div class="dropdown">
+              {% if forum.subforums|length > 1 %}
+              <a href="{{ url(forum.type, slug=forum.slug, forum=forum.id) }}#subforums" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-chevron-down"></i> {% trans %}Subforums{% endtrans %}</a>
+              <div class="dropdown-menu" role="menu" aria-labelledby="dLabel">
+                <div class="dropdown-shadow">
+                  <ul>
+                    {% for subforum in forum.subforums %}
+                    <li><a href="{{ url(subforum.type, slug=subforum.slug, forum=subforum.id) }}"><i class="icon-{% if subforum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %}"></i> {{ subforum.name }}</a></li>
+                    {% endfor %}
+                  </ul>
+                </div>
+              </div>
+              {% else %}
+              <a href="{{ url(forum.subforums[0].type, slug=forum.subforums[0].slug, forum=forum.subforums[0].id) }}" class="subforum tooltip-top" title="{% trans forum=forum.subforums[0].name %}Go to the {{ forum }} subforum{% endtrans %}">{{ forum.subforums[0].name|short_string(16) }}</a>
+              {% endif %}
+            </div>
+            {% endif%}
+            <div class="hide forum-meta">
+              {% if forum.description %}<p class="forum-description">{{ forum.description }}</p>{% endif %}
+              <div class="forum-stats">
+                {% if forum.type != 'redirect' %}
+                <span>{% trans %}Posts{% endtrans %}: <strong>{{ forum.posts|intcomma }}</strong></span>
+                {% trans %}Threads{% endtrans %}: <strong>{{ forum.threads|intcomma }}</strong>
+                {% else %}
+                {% trans %}Clicks{% endtrans %}: <strong>{{ forum.redirects|intcomma }}</strong>
+                {% endif %}
+              </div>
+            </div>
+          </div>
+        </div>
+        {% endfor %}
+      </div>
+      {% endif %}{% endfor %}
+
+      {{ hook_below_home_forums_list|safe }}
+    </div>
+  </div>
+  <div class="span4 index-sidebar">
+
+    {{ hook_above_home_sidepanel|safe }}
+
+    {% if ranks_online %}
+    <div class="index-ranks-list">
+      {% for rank in ranks_online %}{% if rank.online %}
+      <div class="inder-rank{% if rank.style %} index-rank-{{ rank.style }}{% endif %}">
+        <h3>{% if rank.slug %}<a href="{{ url('users', slug=rank.slug) }}">{% endif %}{% trans rank_name=_(rank.name) %}{{ rank_name }} Online{% endtrans %}{% if rank.slug %}</a>{% endif %}</h3>
+        <ul class="unstyled">
+          {% for online in rank.online %}
+          <li>
+            <img src="{{ online.get_avatar(24) }}" alt="" class="avatar-small">
+            <a href="{{ url('user', username=online.username_slug, user=online.pk) }}" class="user-name">{{ online.username }}</a>
+            {% if rank.title or online.title %}
+            {% if rank.slug -%}
+            <a href="{{ url('users', slug=rank.slug) }}" class="label">{% if online.title %}{{ online.title }}{% else %}{{ _(rank.title) }}{% endif %}</a>
+            {%- else -%}
+            <span class="label">{% if online.title %}{{ online.title }}{% else %}{{ _(rank.title) }}{% endif %}</span>
+            {% endif %}
+            {% endif %}
+          </li>
+          {% endfor %}
+        </ul>
+      </div>
+      {% endif %}{% endfor %}
+    </div>
+    {% endif %}
+
+    {{ hook_after_home_sidepanel_ranks_online|safe }}
+
+    {% if popular_threads %}
+    <div class="index-popular-threads">
+      <h4>{% trans %}Popular Threads{% endtrans %}</h4>
+      <ul class="unstyled">
+        {% for thread in popular_threads %}
+        <li>
+          <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="index-popular-thread{% if thread.name|length > 42 %} tooltip-top{% endif %}"{% if thread.name|length > 42 %} title="{{ thread.name }}"{% endif %}>{{ thread.name|short_string(42) }}</a>
+          <div class="muted"><a href="{{ url('forum', forum=thread.forum_id, slug=thread.forum_slug) }}">{{ thread.forum_name }}</a> - {{ thread.last|reltimesince }}</div>
+        </li>
+        {% endfor %}
+      </ul>
+    </div>
+    {% endif %}
+
+    {{ hook_after_home_sidepanel_popular_threads|safe }}
+
+    <div class="index-stats">
+      <ul class="unstyled">
+        <li>
+          <span class="tooltip-top" title="{% trans %}Posts{% endtrans %}"><i class="icon-comment"></i> {{ monitor.posts|int|intcomma }}</span>
+        </li>
+        <li>
+          <span class="tooltip-top" title="{% trans %}Threads{% endtrans %}"><i class="icon-th-list"></i> {{ monitor.threads|int|intcomma }}</span>
+        </li>
+        <li>
+          <span class="tooltip-top" title="{% trans %}Members{% endtrans %}"><i class="icon-user"></i> {{ monitor.users|int|intcomma }}</span>
+        </li>
+        {% if settings.online_counting != 'no' %}
+        <li>
+          <span class="tooltip-top" title="{% trans %}Online{% endtrans %}"><i class="icon-map-marker"></i> {{ users_online.members|int|intcomma }} <span class="muted">{{ users_online.all|int|intcomma }}</span></span>
+        </li>
+        {% endif %}
+      </ul>
+    </div>
+
+    {{ hook_after_home_sidepanel_forum_stats|safe }}
+
+    {% if user.is_authenticated() %}
+    <form action="{{ url('read_all') }}" method="post" class="index-forums-read-all">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <button type="submit" class="btn btn-link"><i class="icon-ok"></i> {% trans %}Mark forums read{% endtrans %}</button>
+    </form>
+    {% endif %}
+
+    {{ hook_below_home_sidepanel|safe }}
+
+  </div>
+</div>
+{{ hook_below_forum_home|safe }}
+{% endblock %}
+
+{% block javascripts -%}{{ super() }}
+  <script type="text/javascript">
+    $(function () {
+      function populateForumTooltip(target) {
+        return $('#forum-' + target + ' .forum-meta').html();
+      };
+      {% for category in forums_list %}{% for forum in category.subforums %}
+        $('#forum-{{ forum.id }} .forum-title').tooltip({
+          template: '<div class="tooltip forum-meta-tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+          placement: 'right',
+          html: true,
+          title: populateForumTooltip({{ forum.id }})
+        });
+      {% endfor %}{% endfor %}
+    });
+  </script>
+{%- endblock %}

+ 170 - 170
templates/cranefly/layout.html

@@ -1,171 +1,171 @@
-{% extends "cranefly/base.html" %}
-{% from "cranefly/macros.html" import messages_list %}
-
-{% block body %}
-<div id="wrap">
-  <div class="navbar navbar-header navbar-static-top">
-    <div class="navbar-inner">
-      <div class="container">
-        <a href="{{ url('index') }}" class="brand">{% if settings.board_header %}{{ settings.board_header }}{% else %}{{ settings.board_name }}{% endif %}</a>
-        {% if acl.search.can_search() and not user.is_crawler() %}
-        <form action="{{ url('search') }}" method="post" class="navbar-form pull-left">
-          <div class="navbar-search-form">
-            <div id="navbar-search" class="navbar-search-border">
-              <div class="navbar-search-text">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                {% if thread is defined %}
-                <input type="hidden" name="search_thread" value="{{ thread.pk }}">
-                {% elif search_thread is defined %}
-                <input type="hidden" name="search_thread" value="{{ search_thread.pk }}">
-                {% endif %}
-                <i class="icon-search"></i>
-                <input type="text" id="search-field" name="search_query" placeholder="{% trans %}Search...{% endtrans %}"{% if search_query is defined and search_query %} value="{{ search_query }}"{% endif %}>
-              </div>
-              <div class="extra">
-                <div class="extra-form">
-                  <div class="control">
-                    <label>{% trans %}Search in{% endtrans %}:</label>
-                    <select name="search_in">
-                      <option value="forums"{% if not search_in is defined or search_in == 'forums' %} selected="selected"{% endif %}>{% trans %}Forums{% endtrans%}</option>
-                      {% if settings.enable_private_threads and acl.private_threads.can_participate()%}
-                      <option value="private"{% if search_in == 'private' %} selected="selected"{% endif %}>{% trans %}Private Threads{% endtrans %}</option>
-                      {% endif %}
-                      {% if acl.reports.can_handle() %}
-                      <option value="reports"{% if search_in == 'reports' %} selected="selected"{% endif %}>{% trans %}Reports{% endtrans %}</option>
-                      {% endif %}
-                      {% if thread is defined %}
-                      <option value="thread"{% if search_in == 'thread' %} selected="selected"{% endif %}>{% trans %}This thread{% endtrans %}</option>
-                      {% elif search_thread is defined %}
-                      <option value="thread"{% if search_in == 'thread' %} selected="selected"{% endif %}>{% trans thread=search_thread.name %}In thread "{{ thread }}"{% endtrans %}</option>
-                      {% endif %}
-                    </select>
-                  </div>
-                  <div class="control">
-                    <label>{% trans %}Author name{% endtrans %}:</label>
-                    <input type="text" name="search_author" placeholder="{% trans %}User name...{% endtrans %}"{% if search_author is defined and search_author %} value="{{ search_author }}"{% endif %}>
-                  </div>
-                  <div class="control">
-                    <label class="checkbox">
-                      <input name="search_thread_titles" type="checkbox"{% if search_thread_titles is defined and search_thread_titles %} checked="checked"{% endif %}> {% trans %}Search only in threads titles{% endtrans %}
-                    </label>
-                  </div>
-                </div>
-                <div class="form-actions">
-                  <button type="submit" class="btn btn-primary"><i class="icon-search"></i> {% trans %}Search{% endtrans%}</button>
-                  {#<a href="#">{% trans %}Advanced Search{% endtrans %}</a>#}
-                </div>
-              </div>
-            </div>
-          </div>
-        </form>
-        {% endif %}
-        <ul class="nav navbar-blocks pull-left">
-          <li><a href="{{ url('index') }}" title="{% trans %}Forum Home{% endtrans %}" class="tooltip-bottom"><i class="icon-th-list"></i></a></li>
-          {{ hook_primary_menu_prepend|safe }}
-          <li><a href="{{ url('popular_threads') }}" title="{% trans %}Popular Threads{% endtrans %}" class="hot tooltip-bottom"><i class="icon-fire"></i></a></li>
-          <li><a href="{{ url('new_threads') }}" title="{% trans %}New Threads{% endtrans %}" class="fresh tooltip-bottom"><i class="icon-leaf"></i></a></li>{% if not user.crawler %}
-          {% if 1==2 and acl.search.can_search() and not user.is_crawler() %}
-          <li><a href="{{ url('search') }}" title="{% trans %}Search Forums{% endtrans %}" class="tooltip-bottom"><i class="icon-search"></i></a></li>{% endif %}
-          {% endif %}
-          <li><a href="{{ url('users') }}" title="{% trans %}Browse Users{% endtrans %}" class="tooltip-bottom"><i class="icon-user"></i></a></li>
-          {% if settings.tos_url or settings.tos_content %}<li><a href="{% if settings.tos_url %}{{ settings.tos_url }}{% else %}{{ url('tos') }}{% endif %}" title="{% if settings.tos_title %}{{ settings.tos_title }}{% else %}{% trans %}Forum Terms of Service{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-certificate"></i></a></li>{% endif %}
-          {{ hook_primary_menu_append|safe }}
-        </ul>
-        {% if not user.is_crawler() %}
-        {% if user.is_authenticated() %}
-        <ul id="fancy-user-nav" class="nav navbar-blocks navbar-compact pull-right">
-          {{ hook_user_menu_important_prepend|safe }}
-          {% if acl.reports.can_handle() and monitor.reported_posts %}
-          <li><a href="{{ url('reports') }}" title="{% trans %}There are unresolved reports!{% endtrans %}" class="tooltip-bottom"><i class="icon-fire"></i><span class="label label-important">{{ monitor.reported_posts }}</span></a></li>
-          {% endif %}
-          {% if user.alerts %}
-          <li><a href="{{ url('alerts') }}" title="{% trans %}You have new notifications!{% endtrans %}" class="tooltip-bottom"><i class="icon-asterisk"></i><span class="label label-important">{{ user.alerts }}</span></a></li>
-          {% endif %}
-          {% if settings.enable_private_threads and acl.private_threads.can_participate() and user.unread_pds %}
-          <li><a href="{{ url('private_threads') }}" title="{% trans %}There are unread Private Threads!{% endtrans %}" class="tooltip-bottom"><i class="icon-inbox"></i><span class="label label-important">{{ user.unread_pds }}</span></a></li>
-          {% endif %}
-          {{ hook_user_menu_important_append|safe }}
-          <li class="user-profile dropdown">
-            <a href="{{ url('user', user=user.id, username=user.username_slug) }}" class="dropdown-toggle" data-toggle="dropdown"><div>{{ user.username }} <img src="{{ user.get_avatar(28) }}" alt=""><span class="caret-border"><b class="caret"></b></span></div></a>
-            <ul class="dropdown-menu">
-              <li><a href="{{ url('user', user=user.id, username=user.username_slug) }}"><i class="icon-user"></i> {% trans %}Your profile{% endtrans %}</a></li>
-              <li><a href="{{ url('usercp') }}"><i class="icon-cog"></i> {% trans %}Change options{% endtrans %}</a></li>
-              <li role="presentation" class="divider"></li>
-              {% if acl.reports.can_handle() %}
-              <li><a href="{{ url('reports') }}">{% if monitor.reported_posts %}<span class="label">{{ monitor.reported_posts }}</span>{% endif %}<i class="icon-fire"></i> {% trans %}Reported Posts{% endtrans %}</a></li>
-              {% endif %}
-              <li><a href="{{ url('alerts') }}">{% if user.alerts %}<span class="label">{{ user.alerts }}</span>{% endif %}<i class="icon-asterisk"></i> {% trans %}Notifications{% endtrans %}</a></li>
-              {{ hook_user_menu_dropdown_prepend|safe }}
-              {% if settings.enable_private_threads and acl.private_threads.can_participate() %}
-              <li><a href="{{ url('private_threads') }}">{% if user.unread_pds %}<span class="label">{{ user.unread_pds }}</span>{% endif %}<i class="icon-inbox"></i> {% trans %}Private Threads{% endtrans %}</a></li>
-              {% endif %}
-              <li><a href="{{ url('newsfeed') }}"><i class="icon-signal"></i> {% trans %}News Feed{% endtrans %}</a></li>
-              <li><a href="{{ url('watched_threads') }}"><i class="icon-bookmark"></i> {% trans %}Watched Threads{% endtrans %}</a></li>
-              {{ hook_user_menu_dropdown_prepend|safe }}
-              <li role="presentation" class="divider"></li>
-              <li><form action="{{ url('sign_out') }}" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn btn-link danger"><i class="icon-off"></i> {% trans %}Sign out{% endtrans %}</button></form></li>
-            </ul>
-          </li>
-        </ul>
-        <noscript>
-          <ul class="nav navbar-blocks pull-right">
-            {{ hook_user_menu_prepend|safe }}
-            <li><a href="{{ url('alerts') }}" title="{% if user.alerts %}{% trans %}You have new notifications!{% endtrans %}{% else %}{% trans %}Your Notifications{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-asterisk"></i>{% if user.alerts %}<span class="label label-important">{{ user.alerts }}</span>{% endif %}</a></li>
-            {% if acl.reports.can_handle() %}
-            <li><a href="{{ url('reports') }}" title="{% if monitor.reported_posts %}{% trans %}There are unresolved reports!{% endtrans %}{% else %}{% trans %}Reports{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-fire"></i>{% if monitor.reported_posts %}<span class="label label-important">{{ monitor.reported_posts }}</span>{% endif %}</a></li>
-            {% endif %}
-            {% if settings.enable_private_threads and acl.private_threads.can_participate() %}
-            <li><a href="{{ url('private_threads') }}" title="{% if user.unread_pds %}{% trans %}There are unread Private Threads!{% endtrans %}{% else %}{% trans %}Your Private Threads{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-inbox"></i>{% if user.unread_pds %}<span class="label label-important">{{ user.unread_pds }}</span>{% endif %}</a></li>
-            {% endif %}
-            <li><a href="{{ url('newsfeed') }}" title="{% trans %}Your News Feed{% endtrans %}" class="tooltip-bottom"><i class="icon-signal"></i></a></li>
-            <li><a href="{{ url('watched_threads') }}" title="{% trans %}Threads you are watching{% endtrans %}" class="tooltip-bottom"><i class="icon-bookmark"></i></a></li>
-            <li><a href="{{ url('usercp') }}" title="{% trans %}Edit your profile options{% endtrans %}" class="tooltip-bottom"><i class="icon-cog"></i></a></li>
-            {{ hook_user_menu_append|safe }}
-            <li class="user-profile"><a href="{{ url('user', user=user.id, username=user.username_slug) }}" title="{% trans %}Go to your profile{% endtrans %}" class="tooltip-bottom"><div><img src="{{ user.get_avatar(28) }}" alt=""> {{ user.username }}</div></a></li>
-            <li><form action="{{ url('sign_out') }}" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" title="{% trans %}Sign Out and browse as guest{% endtrans %}" class="btn btn-link danger tooltip-bottom"><i class="icon-off"></i></button></form></li>
-          </ul>
-        </noscript>
-        {% else %}
-        <ul class="nav navbar-user-nav pull-right">
-          {{ hook_guest_menu_prepend|safe }}
-          <li><a href="{{ url('sign_in') }}" title="{% trans %}Sign In to Your Account{% endtrans %}" class="tooltip-bottom btn btn-danger"><i class="icon-check"></i> {% trans %}Sign In{% endtrans %}</a></li>{% if settings.account_activation != 'block' %}
-          <li><a href="{{ url('register') }}" title="{% trans %}Register new account{% endtrans %}" class="tooltip-bottom btn btn-inverse"><i class="icon-edit"></i> {% trans %}Register{% endtrans %}</a></li>{% endif %}
-          {{ hook_guest_menu_append|safe }}
-        </ul>
-        {% endif %}
-        {% endif %}
-      </div>
-    </div>
-  </div>
-
-  {% block container %}
-  <div class="container container-primary">
-    {{ messages_list(messages) }}
-    
-    {% block content %}
-    {% endblock %}
-  </div>
-  {% endblock %}
-
-</div><!--/#wrap -->
-
-<footer>
-  <div class="container">
-    <ul class="breadcrumb">
-      {% block breadcrumb %}<li class="first"><a href="{{ url('index') }}">{{ settings.board_name }}</a>{% endblock %}</li>
-      {{ hook_foot_menu_prepend|safe }}
-      <li class="pull-right"><i class="icon-move"></i> <a href="{{ url('forum_map') }}">{% trans %}Forum Map{% endtrans %}</a></li>
-      {{ hook_foot_menu_append|safe }}
-    </ul>
-    <hr>
-    <div class="credits">
-      <p>
-        {% if settings.board_credits %}
-        {{ settings.board_credits|safe }}<br>
-        {% endif %}
-        <a href="http://misago-project.org">This community is powered by Misago forum software by Rafał Pitoń</a>
-      </p>
-      {{ hook_html_credits_side|safe }}
-    </div>
-  </div>
+{% extends "cranefly/base.html" %}
+{% from "cranefly/macros.html" import messages_list %}
+
+{% block body %}
+<div id="wrap">
+  <div class="navbar navbar-header navbar-static-top">
+    <div class="navbar-inner">
+      <div class="container">
+        <a href="{{ url('index') }}" class="brand">{% if settings.board_header %}{{ settings.board_header }}{% else %}{{ settings.board_name }}{% endif %}</a>
+        {% if acl.search.can_search() and not user.is_crawler() %}
+        <form action="{{ url('search') }}" method="post" class="navbar-form pull-left">
+          <div class="navbar-search-form">
+            <div id="navbar-search" class="navbar-search-border">
+              <div class="navbar-search-text">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                {% if thread is defined %}
+                <input type="hidden" name="search_thread" value="{{ thread.pk }}">
+                {% elif search_thread is defined %}
+                <input type="hidden" name="search_thread" value="{{ search_thread.pk }}">
+                {% endif %}
+                <i class="icon-search"></i>
+                <input type="text" id="search-field" name="search_query" placeholder="{% trans %}Search...{% endtrans %}"{% if search_query is defined and search_query %} value="{{ search_query }}"{% endif %}>
+              </div>
+              <div class="extra">
+                <div class="extra-form">
+                  <div class="control">
+                    <label>{% trans %}Search in{% endtrans %}:</label>
+                    <select name="search_in">
+                      <option value="forums"{% if not search_in is defined or search_in == 'forums' %} selected="selected"{% endif %}>{% trans %}Forums{% endtrans%}</option>
+                      {% if settings.enable_private_threads and acl.private_threads.can_participate()%}
+                      <option value="private"{% if search_in == 'private' %} selected="selected"{% endif %}>{% trans %}Private Threads{% endtrans %}</option>
+                      {% endif %}
+                      {% if acl.reports.can_handle() %}
+                      <option value="reports"{% if search_in == 'reports' %} selected="selected"{% endif %}>{% trans %}Reports{% endtrans %}</option>
+                      {% endif %}
+                      {% if thread is defined %}
+                      <option value="thread"{% if search_in == 'thread' %} selected="selected"{% endif %}>{% trans %}This thread{% endtrans %}</option>
+                      {% elif search_thread is defined %}
+                      <option value="thread"{% if search_in == 'thread' %} selected="selected"{% endif %}>{% trans thread=search_thread.name %}In thread "{{ thread }}"{% endtrans %}</option>
+                      {% endif %}
+                    </select>
+                  </div>
+                  <div class="control">
+                    <label>{% trans %}Author name{% endtrans %}:</label>
+                    <input type="text" name="search_author" placeholder="{% trans %}User name...{% endtrans %}"{% if search_author is defined and search_author %} value="{{ search_author }}"{% endif %}>
+                  </div>
+                  <div class="control">
+                    <label class="checkbox">
+                      <input name="search_thread_titles" type="checkbox"{% if search_thread_titles is defined and search_thread_titles %} checked="checked"{% endif %}> {% trans %}Search only in threads titles{% endtrans %}
+                    </label>
+                  </div>
+                </div>
+                <div class="form-actions">
+                  <button type="submit" class="btn btn-primary"><i class="icon-search"></i> {% trans %}Search{% endtrans%}</button>
+                  {#<a href="#">{% trans %}Advanced Search{% endtrans %}</a>#}
+                </div>
+              </div>
+            </div>
+          </div>
+        </form>
+        {% endif %}
+        <ul class="nav navbar-blocks pull-left">
+          <li><a href="{{ url('index') }}" title="{% trans %}Forum Home{% endtrans %}" class="tooltip-bottom"><i class="icon-th-list"></i></a></li>
+          {{ hook_primary_menu_prepend|safe }}
+          <li><a href="{{ url('popular_threads') }}" title="{% trans %}Popular Threads{% endtrans %}" class="hot tooltip-bottom"><i class="icon-fire"></i></a></li>
+          <li><a href="{{ url('new_threads') }}" title="{% trans %}New Threads{% endtrans %}" class="fresh tooltip-bottom"><i class="icon-leaf"></i></a></li>{% if not user.crawler %}
+          {% if 1==2 and acl.search.can_search() and not user.is_crawler() %}
+          <li><a href="{{ url('search') }}" title="{% trans %}Search Forums{% endtrans %}" class="tooltip-bottom"><i class="icon-search"></i></a></li>{% endif %}
+          {% endif %}
+          <li><a href="{{ url('users') }}" title="{% trans %}Browse Users{% endtrans %}" class="tooltip-bottom"><i class="icon-user"></i></a></li>
+          {% if settings.tos_url or settings.tos_content %}<li><a href="{% if settings.tos_url %}{{ settings.tos_url }}{% else %}{{ url('tos') }}{% endif %}" title="{% if settings.tos_title %}{{ settings.tos_title }}{% else %}{% trans %}Forum Terms of Service{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-certificate"></i></a></li>{% endif %}
+          {{ hook_primary_menu_append|safe }}
+        </ul>
+        {% if not user.is_crawler() %}
+        {% if user.is_authenticated() %}
+        <ul id="fancy-user-nav" class="nav navbar-blocks navbar-compact pull-right">
+          {{ hook_user_menu_important_prepend|safe }}
+          {% if acl.reports.can_handle() and monitor.reported_posts %}
+          <li><a href="{{ url('reports') }}" title="{% trans %}There are unresolved reports!{% endtrans %}" class="tooltip-bottom"><i class="icon-fire"></i><span class="label label-important">{{ monitor.reported_posts }}</span></a></li>
+          {% endif %}
+          {% if user.alerts %}
+          <li><a href="{{ url('alerts') }}" title="{% trans %}You have new notifications!{% endtrans %}" class="tooltip-bottom"><i class="icon-asterisk"></i><span class="label label-important">{{ user.alerts }}</span></a></li>
+          {% endif %}
+          {% if settings.enable_private_threads and acl.private_threads.can_participate() and user.unread_pds %}
+          <li><a href="{{ url('private_threads') }}" title="{% trans %}There are unread Private Threads!{% endtrans %}" class="tooltip-bottom"><i class="icon-inbox"></i><span class="label label-important">{{ user.unread_pds }}</span></a></li>
+          {% endif %}
+          {{ hook_user_menu_important_append|safe }}
+          <li class="user-profile dropdown">
+            <a href="{{ url('user', user=user.id, username=user.username_slug) }}" class="dropdown-toggle" data-toggle="dropdown"><div>{{ user.username }} <img src="{{ user.get_avatar(28) }}" alt=""><span class="caret-border"><b class="caret"></b></span></div></a>
+            <ul class="dropdown-menu">
+              <li><a href="{{ url('user', user=user.id, username=user.username_slug) }}"><i class="icon-user"></i> {% trans %}Your profile{% endtrans %}</a></li>
+              <li><a href="{{ url('usercp') }}"><i class="icon-cog"></i> {% trans %}Change options{% endtrans %}</a></li>
+              <li role="presentation" class="divider"></li>
+              {% if acl.reports.can_handle() %}
+              <li><a href="{{ url('reports') }}">{% if monitor.reported_posts %}<span class="label">{{ monitor.reported_posts }}</span>{% endif %}<i class="icon-fire"></i> {% trans %}Reported Posts{% endtrans %}</a></li>
+              {% endif %}
+              <li><a href="{{ url('alerts') }}">{% if user.alerts %}<span class="label">{{ user.alerts }}</span>{% endif %}<i class="icon-asterisk"></i> {% trans %}Notifications{% endtrans %}</a></li>
+              {{ hook_user_menu_dropdown_prepend|safe }}
+              {% if settings.enable_private_threads and acl.private_threads.can_participate() %}
+              <li><a href="{{ url('private_threads') }}">{% if user.unread_pds %}<span class="label">{{ user.unread_pds }}</span>{% endif %}<i class="icon-inbox"></i> {% trans %}Private Threads{% endtrans %}</a></li>
+              {% endif %}
+              <li><a href="{{ url('newsfeed') }}"><i class="icon-signal"></i> {% trans %}News Feed{% endtrans %}</a></li>
+              <li><a href="{{ url('watched_threads') }}"><i class="icon-bookmark"></i> {% trans %}Watched Threads{% endtrans %}</a></li>
+              {{ hook_user_menu_dropdown_prepend|safe }}
+              <li role="presentation" class="divider"></li>
+              <li><form action="{{ url('sign_out') }}" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn btn-link danger"><i class="icon-off"></i> {% trans %}Sign out{% endtrans %}</button></form></li>
+            </ul>
+          </li>
+        </ul>
+        <noscript>
+          <ul class="nav navbar-blocks pull-right">
+            {{ hook_user_menu_prepend|safe }}
+            <li><a href="{{ url('alerts') }}" title="{% if user.alerts %}{% trans %}You have new notifications!{% endtrans %}{% else %}{% trans %}Your Notifications{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-asterisk"></i>{% if user.alerts %}<span class="label label-important">{{ user.alerts }}</span>{% endif %}</a></li>
+            {% if acl.reports.can_handle() %}
+            <li><a href="{{ url('reports') }}" title="{% if monitor.reported_posts %}{% trans %}There are unresolved reports!{% endtrans %}{% else %}{% trans %}Reports{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-fire"></i>{% if monitor.reported_posts %}<span class="label label-important">{{ monitor.reported_posts }}</span>{% endif %}</a></li>
+            {% endif %}
+            {% if settings.enable_private_threads and acl.private_threads.can_participate() %}
+            <li><a href="{{ url('private_threads') }}" title="{% if user.unread_pds %}{% trans %}There are unread Private Threads!{% endtrans %}{% else %}{% trans %}Your Private Threads{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-inbox"></i>{% if user.unread_pds %}<span class="label label-important">{{ user.unread_pds }}</span>{% endif %}</a></li>
+            {% endif %}
+            <li><a href="{{ url('newsfeed') }}" title="{% trans %}Your News Feed{% endtrans %}" class="tooltip-bottom"><i class="icon-signal"></i></a></li>
+            <li><a href="{{ url('watched_threads') }}" title="{% trans %}Threads you are watching{% endtrans %}" class="tooltip-bottom"><i class="icon-bookmark"></i></a></li>
+            <li><a href="{{ url('usercp') }}" title="{% trans %}Edit your profile options{% endtrans %}" class="tooltip-bottom"><i class="icon-cog"></i></a></li>
+            {{ hook_user_menu_append|safe }}
+            <li class="user-profile"><a href="{{ url('user', user=user.id, username=user.username_slug) }}" title="{% trans %}Go to your profile{% endtrans %}" class="tooltip-bottom"><div><img src="{{ user.get_avatar(28) }}" alt=""> {{ user.username }}</div></a></li>
+            <li><form action="{{ url('sign_out') }}" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" title="{% trans %}Sign Out and browse as guest{% endtrans %}" class="btn btn-link danger tooltip-bottom"><i class="icon-off"></i></button></form></li>
+          </ul>
+        </noscript>
+        {% else %}
+        <ul class="nav navbar-user-nav pull-right">
+          {{ hook_guest_menu_prepend|safe }}
+          <li><a href="{{ url('sign_in') }}" title="{% trans %}Sign In to Your Account{% endtrans %}" class="tooltip-bottom btn btn-danger"><i class="icon-check"></i> {% trans %}Sign In{% endtrans %}</a></li>{% if settings.account_activation != 'block' %}
+          <li><a href="{{ url('register') }}" title="{% trans %}Register new account{% endtrans %}" class="tooltip-bottom btn btn-inverse"><i class="icon-edit"></i> {% trans %}Register{% endtrans %}</a></li>{% endif %}
+          {{ hook_guest_menu_append|safe }}
+        </ul>
+        {% endif %}
+        {% endif %}
+      </div>
+    </div>
+  </div>
+
+  {% block container %}
+  <div class="container container-primary">
+    {{ messages_list(messages) }}
+    
+    {% block content %}
+    {% endblock %}
+  </div>
+  {% endblock %}
+
+</div><!--/#wrap -->
+
+<footer>
+  <div class="container">
+    <ul class="breadcrumb">
+      {% block breadcrumb %}<li class="first"><a href="{{ url('index') }}">{{ settings.board_name }}</a>{% endblock %}</li>
+      {{ hook_foot_menu_prepend|safe }}
+      <li class="pull-right"><i class="icon-move"></i> <a href="{{ url('forum_map') }}">{% trans %}Forum Map{% endtrans %}</a></li>
+      {{ hook_foot_menu_append|safe }}
+    </ul>
+    <hr>
+    <div class="credits">
+      <p>
+        {% if settings.board_credits %}
+        {{ settings.board_credits|safe }}<br>
+        {% endif %}
+        <a href="http://misago-project.org">This community is powered by Misago forum software by Rafał Pitoń</a>
+      </p>
+      {{ hook_html_credits_side|safe }}
+    </div>
+  </div>
 </footer>{% endblock %}
 </footer>{% endblock %}

+ 81 - 81
templates/cranefly/macros.html

@@ -1,82 +1,82 @@
-{% macro page_title(title='', parent='', page=0) -%}
-{% if title %}{{ title }}{% if page > 1 %} | {% trans page=page %}Page {{ page }}{% endtrans %}{% endif %} | {% if parent %}{{ parent }} | {% endif %}{% endif %}{{ settings.board_name }}
-{%- endmacro %}
-
-{# Guest avatar #}
-{% macro avatar_guest(size=None) -%}
-{{ STATIC_URL }}avatars/{% if size %}{{ size }}_{% endif %}avatar_guest.jpg
-{%- endmacro %}
-
-{# Messages list macro #}
-{% macro messages_list(messages) %}
-{% if messages %}
-<div class="messages-list">
-  {% for message in messages %}
-  {{ draw_message(message) }}
-  {% endfor %}
-</div>
-{% endif %}
-{% endmacro %}
-
-{# Render single message #}
-{% macro draw_message(message, class='') %}
-  <div class="alert alert-{{ message.type }}{% if class %} {{ class }}{% endif %}">
-  	{{ draw_message_icon(message) }} <p><strong>{{ message.message }}</strong></p>
-  </div>
-{%- endmacro %}
-
-{# Render icon #}
-{% macro draw_message_icon(message) -%}
-  	<div class="alert-icon"><span><i class="icon-{% if message.type == 'error' -%}remove
-  		{%- elif message.type == 'success' -%}ok
-  		{%- elif message.type == 'info' -%}info-sign
-  		{%- else -%}warning-sign
-  		{%- endif %} icon-white"></i></span></div>
-{%- endmacro %}
-
-{# Render pagination label #}
-{% macro pager_label(pagination) -%}
-{%- trans current_page=('<strong>' ~ pagination['page'] ~ '</strong>')|safe, pages=('<strong>' ~ pagination['total'] ~ '</strong>')|safe -%}
-    Page {{ current_page }} of {{ pages }}
-{%- endtrans -%}
-{%- endmacro %}
-
-{% macro itemprop_bread() -%}
-itemprop="breadcrumb"
-{%- endmacro %}
-
-{% macro parents_list(forums) -%}
-{% for forum in forums %}
-<li><a href="{% if loop.first %}{{ url('index') }}#{{ forum.slug }}{% else %}{{ url(forum.type, forum=forum.pk, slug=forum.slug) }}{% endif %}">{{ forum.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{% endfor %}
-{%- endmacro %}
-
-{% macro wrap(item, wrap, extra='') -%}
-<{{ wrap }}{% if extra %} {{ extra|safe }}{% endif %}>{{ item }}</{{ wrap }}>
-{%- endmacro %}
-
-{% macro thread_flags(thread) -%}
-<ul class="unstyled thread-flags">
-  {% if thread.replies_reported and ((forum is defined and acl.threads.can_mod_posts(forum)) or acl.threads.can_mod_posts(thread.forum)) %}
-  <li class="flag-reported"><i class="icon-warning-sign tooltip-top" title="{% trans %}This thread has reported replies{% endtrans %}"></i></li>
-  {% endif %}
-  {% if thread.replies_moderated %}
-  <li class="flag-notreviewed"><i class="icon-question-sign tooltip-top" title="{% trans %}This thread has unreviewed replies{% endtrans %}"></i></li>
-  {% endif %}
-  {% if thread.weight == 2 %}
-  <li class="flag-announcement"><i class="icon-star tooltip-top" title="{% trans %}This thread is an annoucement{% endtrans %}"></i></li>
-  {% endif %}
-  {% if thread.weight == 1 %}
-  <li class="flag-sticky"><i class="icon-star-empty tooltip-top" title="{% trans %}This thread is sticky{% endtrans %}"></i></li>
-  {% endif %}
-  {% if thread.moderated  %}
-  <li class="flag-notreviewed"><i class="icon-eye-close tooltip-top" title="{% trans %}This thread awaits review{% endtrans %}"></i></li>
-  {% endif %}
-  {% if thread.deleted %}
-  <li class="flag-deleted"><i class="icon-trash tooltip-top" title="{% trans %}This thread is deleted{% endtrans %}"></i></li>
-  {% endif %}
-  {% if thread.closed %}
-  <li class="flag-closed"><i class="icon-lock tooltip-top" title="{% trans %}This thread is closed{% endtrans %}"></i></li>
-  {% endif %}
-</ul>
+{% macro page_title(title='', parent='', page=0) -%}
+{% if title %}{{ title }}{% if page > 1 %} | {% trans page=page %}Page {{ page }}{% endtrans %}{% endif %} | {% if parent %}{{ parent }} | {% endif %}{% endif %}{{ settings.board_name }}
+{%- endmacro %}
+
+{# Guest avatar #}
+{% macro avatar_guest(size=None) -%}
+{{ STATIC_URL }}avatars/{% if size %}{{ size }}_{% endif %}avatar_guest.jpg
+{%- endmacro %}
+
+{# Messages list macro #}
+{% macro messages_list(messages) %}
+{% if messages %}
+<div class="messages-list">
+  {% for message in messages %}
+  {{ draw_message(message) }}
+  {% endfor %}
+</div>
+{% endif %}
+{% endmacro %}
+
+{# Render single message #}
+{% macro draw_message(message, class='') %}
+  <div class="alert alert-{{ message.type }}{% if class %} {{ class }}{% endif %}">
+  	{{ draw_message_icon(message) }} <p><strong>{{ message.message }}</strong></p>
+  </div>
+{%- endmacro %}
+
+{# Render icon #}
+{% macro draw_message_icon(message) -%}
+  	<div class="alert-icon"><span><i class="icon-{% if message.type == 'error' -%}remove
+  		{%- elif message.type == 'success' -%}ok
+  		{%- elif message.type == 'info' -%}info-sign
+  		{%- else -%}warning-sign
+  		{%- endif %} icon-white"></i></span></div>
+{%- endmacro %}
+
+{# Render pagination label #}
+{% macro pager_label(pagination) -%}
+{%- trans current_page=('<strong>' ~ pagination['page'] ~ '</strong>')|safe, pages=('<strong>' ~ pagination['total'] ~ '</strong>')|safe -%}
+    Page {{ current_page }} of {{ pages }}
+{%- endtrans -%}
+{%- endmacro %}
+
+{% macro itemprop_bread() -%}
+itemprop="breadcrumb"
+{%- endmacro %}
+
+{% macro parents_list(forums) -%}
+{% for forum in forums %}
+<li><a href="{% if loop.first %}{{ url('index') }}#{{ forum.slug }}{% else %}{{ url(forum.type, forum=forum.pk, slug=forum.slug) }}{% endif %}">{{ forum.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{% endfor %}
+{%- endmacro %}
+
+{% macro wrap(item, wrap, extra='') -%}
+<{{ wrap }}{% if extra %} {{ extra|safe }}{% endif %}>{{ item }}</{{ wrap }}>
+{%- endmacro %}
+
+{% macro thread_flags(thread) -%}
+<ul class="unstyled thread-flags">
+  {% if thread.replies_reported and ((forum is defined and acl.threads.can_mod_posts(forum)) or acl.threads.can_mod_posts(thread.forum)) %}
+  <li class="flag-reported"><i class="icon-warning-sign tooltip-top" title="{% trans %}This thread has reported replies{% endtrans %}"></i></li>
+  {% endif %}
+  {% if thread.replies_moderated %}
+  <li class="flag-notreviewed"><i class="icon-question-sign tooltip-top" title="{% trans %}This thread has unreviewed replies{% endtrans %}"></i></li>
+  {% endif %}
+  {% if thread.weight == 2 %}
+  <li class="flag-announcement"><i class="icon-star tooltip-top" title="{% trans %}This thread is an annoucement{% endtrans %}"></i></li>
+  {% endif %}
+  {% if thread.weight == 1 %}
+  <li class="flag-sticky"><i class="icon-star-empty tooltip-top" title="{% trans %}This thread is sticky{% endtrans %}"></i></li>
+  {% endif %}
+  {% if thread.moderated  %}
+  <li class="flag-notreviewed"><i class="icon-eye-close tooltip-top" title="{% trans %}This thread awaits review{% endtrans %}"></i></li>
+  {% endif %}
+  {% if thread.deleted %}
+  <li class="flag-deleted"><i class="icon-trash tooltip-top" title="{% trans %}This thread is deleted{% endtrans %}"></i></li>
+  {% endif %}
+  {% if thread.closed %}
+  <li class="flag-closed"><i class="icon-lock tooltip-top" title="{% trans %}This thread is closed{% endtrans %}"></i></li>
+  {% endif %}
+</ul>
 {%- endmacro %}
 {%- endmacro %}

+ 103 - 103
templates/cranefly/new_threads.html

@@ -1,104 +1,104 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('New Threads')) }}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <h1>{% trans %}New Threads{% endtrans %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if threads %}
-  <div class="forum-threads-list">
-    <div class="header">
-      <div class="row-fluid">
-        <div class="span7">{% trans %}Thread{% endtrans %}</div>
-        <div class="span5 thread-activity">
-          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
-        </div>
-      </div>
-    </div>
-    {% for thread in threads %}
-    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
-      <div class="row-fluid">
-        <div class="span7">
-          {% if thread.is_read %}
-          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
-          {% else %}
-          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
-          {% endif %}
-
-          <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="thread-name">{{ thread.name }}</a>
-
-          {{ macros.thread_flags(thread) }}
-          
-          <div class="thread-details">
-            {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
-          </div>
-
-        </div>
-        <div class="span5 thread-activity">
-
-          {% if settings.avatars_on_threads_list %}
-          <div class="thread-last-avatar">
-            {% if thread.last_poster_id %}
-            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
-            {% else %}
-            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
-            {% endif %}
-          </div>
-          {% endif %}
-
-          <div class="thread-replies">
-            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
-            {{ replies(thread.replies) }}, <span{% if (thread.upvotes-thread.downvotes) > 0 %} class="text-success"{% elif (thread.upvotes-thread.downvotes) < 0 %} class="text-error"{% endif %}><strong>{% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}</strong>{% trans rating=(thread.upvotes-thread.downvotes)|abs|intcomma %}{{ rating }} thread rating{% endtrans %}</span>
-          </div>
-          
-        </div>
-      </div>
-    </div>
-    {% endfor %}
-  </div>
-  {{ pager() }}
-  {% else %}
-  <p class="lead">{% trans %}No new threads were started in last 48 hours.{% endtrans %}</p>
-  {% endif %}
-</div>
-{% endblock %}
-
-
-{% macro replies(thread_replies) -%}
-{% trans count=thread_replies, replies=thread_replies|intcomma -%}
-{{ replies }} reply
-{%- pluralize -%}
-{{ replies }} replies
-{%- endtrans %}
-{%- endmacro %}
-
-{% macro thread_starter(thread) -%}
-{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro thread_forum(thread) -%}
-<a href="{{ url('forum', forum=thread.forum_id, slug=thread.forum.slug) }}" class="forum-link">{{ thread.forum.name }}</a>
-{%- endmacro %}
-
-{% macro thread_reply(thread) -%}
-{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro pager() -%}
-{% if items_total > 0 and pagination['total'] > 1 %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('new_threads', page=pagination['prev']) }}{% else %}{{ url('new_threads') }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('new_threads', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('New Threads')) }}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% trans %}New Threads{% endtrans %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if threads %}
+  <div class="forum-threads-list">
+    <div class="header">
+      <div class="row-fluid">
+        <div class="span7">{% trans %}Thread{% endtrans %}</div>
+        <div class="span5 thread-activity">
+          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
+        </div>
+      </div>
+    </div>
+    {% for thread in threads %}
+    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
+      <div class="row-fluid">
+        <div class="span7">
+          {% if thread.is_read %}
+          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
+          {% else %}
+          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
+          {% endif %}
+
+          <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="thread-name">{{ thread.name }}</a>
+
+          {{ macros.thread_flags(thread) }}
+          
+          <div class="thread-details">
+            {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
+          </div>
+
+        </div>
+        <div class="span5 thread-activity">
+
+          {% if settings.avatars_on_threads_list %}
+          <div class="thread-last-avatar">
+            {% if thread.last_poster_id %}
+            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
+            {% else %}
+            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
+            {% endif %}
+          </div>
+          {% endif %}
+
+          <div class="thread-replies">
+            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
+            {{ replies(thread.replies) }}, <span{% if (thread.upvotes-thread.downvotes) > 0 %} class="text-success"{% elif (thread.upvotes-thread.downvotes) < 0 %} class="text-error"{% endif %}><strong>{% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}</strong>{% trans rating=(thread.upvotes-thread.downvotes)|abs|intcomma %}{{ rating }} thread rating{% endtrans %}</span>
+          </div>
+          
+        </div>
+      </div>
+    </div>
+    {% endfor %}
+  </div>
+  {{ pager() }}
+  {% else %}
+  <p class="lead">{% trans %}No new threads were started in last 48 hours.{% endtrans %}</p>
+  {% endif %}
+</div>
+{% endblock %}
+
+
+{% macro replies(thread_replies) -%}
+{% trans count=thread_replies, replies=thread_replies|intcomma -%}
+{{ replies }} reply
+{%- pluralize -%}
+{{ replies }} replies
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro thread_starter(thread) -%}
+{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro thread_forum(thread) -%}
+<a href="{{ url('forum', forum=thread.forum_id, slug=thread.forum.slug) }}" class="forum-link">{{ thread.forum.name }}</a>
+{%- endmacro %}
+
+{% macro thread_reply(thread) -%}
+{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro pager() -%}
+{% if items_total > 0 and pagination['total'] > 1 %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('new_threads', page=pagination['prev']) }}{% else %}{{ url('new_threads') }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('new_threads', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
 {%- endmacro %}
 {%- endmacro %}

+ 54 - 54
templates/cranefly/newsfeed.html

@@ -1,54 +1,54 @@
-{% extends "cranefly/profiles/profile.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(_('Your News Feed')) }}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <h1>{% trans %}Your News Feed{% endtrans %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if follows %}
-  {% if posts %}
-  <div class="news-feed">
-    {% for item in posts %}
-    <div class="media">
-      <a href="{{ url('user', user=item.user.pk, username=item.user.username_slug) }}" class="pull-left">
-        <img class="media-object" src="{{ item.user.get_avatar(52) }}">
-      </a>
-      <div class="media-body">
-        <a href="{{ url('thread_find', thread=item.thread.pk, slug=item.thread.slug, post=item.pk) }}" class="post-preview">{{ item.post_preparsed|markdown_short(300) }}</a>
-        <div class="media-footer">{% if item.thread.start_post_id == item.pk -%}
-        {% trans thread=thread(item), forum=forum(item.forum), user=username(item.user), date=item.date|reltimesince|low %}Thread {{ thread }} posted in {{ forum }} by {{ user }} {{ date }}{% endtrans %}
-        {%- else -%}
-        {% trans thread=thread(item), forum=forum(item.forum), user=username(item.user), date=item.date|reltimesince|low %}Reply to {{ thread }} posted in {{ forum }} by {{ user }} {{ date }}{% endtrans %}
-        {%- endif %}</div>
-      </div>
-    </div>
-    <hr>
-    {% endfor %}
-  </div>
-  {% else %}
-  <p class="lead">{% trans username=user.username %}{{ username }}, there is nothing to display in your news feed... yet!{% endtrans %}</p>
-  {% endif %}
-  {% else %}
-  <p class="lead">{% trans username=user.username %}{{ username }}, you have to follow other users in order to fill your news feed.{% endtrans %}</p>
-  {% endif %}
-</div>
-{% endblock %}
-
-{% macro thread(item) -%}
-<a href="{{ url('thread_find', thread=item.thread.pk, slug=item.thread.slug, post=item.pk) }}" class="thread-link">{{ item.thread.name }}</a>
-{%- endmacro %}
-
-{% macro forum(forum) -%}
-<a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" class="forum-link">{{ forum.name }}</a>
-{%- endmacro %}
-
-{% macro username(user) -%}
-<a href="{{ url('user', user=user.pk, username=user.username_slug) }}" class="user-link">{{ user.username }}</a>
-{%- endmacro %}
+{% extends "cranefly/profiles/profile.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(_('Your News Feed')) }}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% trans %}Your News Feed{% endtrans %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if follows %}
+  {% if posts %}
+  <div class="news-feed">
+    {% for item in posts %}
+    <div class="media">
+      <a href="{{ url('user', user=item.user.pk, username=item.user.username_slug) }}" class="pull-left">
+        <img class="media-object" src="{{ item.user.get_avatar(52) }}">
+      </a>
+      <div class="media-body">
+        <a href="{{ url('thread_find', thread=item.thread.pk, slug=item.thread.slug, post=item.pk) }}" class="post-preview">{{ item.post_preparsed|markdown_short(300) }}</a>
+        <div class="media-footer">{% if item.thread.start_post_id == item.pk -%}
+        {% trans thread=thread(item), forum=forum(item.forum), user=username(item.user), date=item.date|reltimesince|low %}Thread {{ thread }} posted in {{ forum }} by {{ user }} {{ date }}{% endtrans %}
+        {%- else -%}
+        {% trans thread=thread(item), forum=forum(item.forum), user=username(item.user), date=item.date|reltimesince|low %}Reply to {{ thread }} posted in {{ forum }} by {{ user }} {{ date }}{% endtrans %}
+        {%- endif %}</div>
+      </div>
+    </div>
+    <hr>
+    {% endfor %}
+  </div>
+  {% else %}
+  <p class="lead">{% trans username=user.username %}{{ username }}, there is nothing to display in your news feed... yet!{% endtrans %}</p>
+  {% endif %}
+  {% else %}
+  <p class="lead">{% trans username=user.username %}{{ username }}, you have to follow other users in order to fill your news feed.{% endtrans %}</p>
+  {% endif %}
+</div>
+{% endblock %}
+
+{% macro thread(item) -%}
+<a href="{{ url('thread_find', thread=item.thread.pk, slug=item.thread.slug, post=item.pk) }}" class="thread-link">{{ item.thread.name }}</a>
+{%- endmacro %}
+
+{% macro forum(forum) -%}
+<a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" class="forum-link">{{ forum.name }}</a>
+{%- endmacro %}
+
+{% macro username(user) -%}
+<a href="{{ url('user', user=user.pk, username=user.username_slug) }}" class="user-link">{{ user.username }}</a>
+{%- endmacro %}

+ 103 - 103
templates/cranefly/popular_threads.html

@@ -1,104 +1,104 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Popular Threads')) }}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <h1>{% trans %}Popular Threads{% endtrans %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if threads %}
-  <div class="forum-threads-list">
-    <div class="header">
-      <div class="row-fluid">
-        <div class="span7">{% trans %}Thread{% endtrans %}</div>
-        <div class="span5 thread-activity">
-          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
-        </div>
-      </div>
-    </div>
-    {% for thread in threads %}
-    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
-      <div class="row-fluid">
-        <div class="span7">
-          {% if thread.is_read %}
-          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
-          {% else %}
-          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
-          {% endif %}
-
-          {{ macros.thread_flags(thread) }}
-
-          <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="thread-name">{{ thread.name }}</a>
-          
-          <div class="thread-details">
-            {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
-          </div>
-
-        </div>
-        <div class="span5 thread-activity">
-
-          {% if settings.avatars_on_threads_list %}
-          <div class="thread-last-avatar">
-            {% if thread.last_poster_id %}
-            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
-            {% else %}
-            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
-            {% endif %}
-          </div>
-          {% endif %}
-
-          <div class="thread-replies">
-            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
-            {{ replies(thread.replies) }}, <span{% if (thread.upvotes-thread.downvotes) > 0 %} class="text-success"{% elif (thread.upvotes-thread.downvotes) < 0 %} class="text-error"{% endif %}><strong>{% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}</strong>{% trans rating=(thread.upvotes-thread.downvotes)|abs|intcomma %}{{ rating }} thread rating{% endtrans %}</span>
-          </div>
-
-        </div>
-      </div>
-    </div>
-    {% endfor %}
-  </div>
-  {{ pager() }}
-  {% else %}
-  <p class="lead">{% trans %}Looks like there are no popular threads... yet!{% endtrans %}</p>
-  {% endif %}
-</div>
-{% endblock %}
-
-
-{% macro replies(thread_replies) -%}
-{% trans count=thread_replies, replies=thread_replies|intcomma -%}
-{{ replies }} reply
-{%- pluralize -%}
-{{ replies }} replies
-{%- endtrans %}
-{%- endmacro %}
-
-{% macro thread_starter(thread) -%}
-{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro thread_forum(thread) -%}
-<a href="{{ url('forum', forum=thread.forum_id, slug=thread.forum.slug) }}" class="forum-link">{{ thread.forum.name }}</a>
-{%- endmacro %}
-
-{% macro thread_reply(thread) -%}
-{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro pager() -%}
-{% if items_total > 0 and pagination['total'] > 1 %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('popular_threads', page=pagination['prev']) }}{% else %}{{ url('popular_threads') }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('popular_threads', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Popular Threads')) }}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% trans %}Popular Threads{% endtrans %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if threads %}
+  <div class="forum-threads-list">
+    <div class="header">
+      <div class="row-fluid">
+        <div class="span7">{% trans %}Thread{% endtrans %}</div>
+        <div class="span5 thread-activity">
+          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
+        </div>
+      </div>
+    </div>
+    {% for thread in threads %}
+    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
+      <div class="row-fluid">
+        <div class="span7">
+          {% if thread.is_read %}
+          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
+          {% else %}
+          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
+          {% endif %}
+
+          {{ macros.thread_flags(thread) }}
+
+          <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="thread-name">{{ thread.name }}</a>
+          
+          <div class="thread-details">
+            {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
+          </div>
+
+        </div>
+        <div class="span5 thread-activity">
+
+          {% if settings.avatars_on_threads_list %}
+          <div class="thread-last-avatar">
+            {% if thread.last_poster_id %}
+            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
+            {% else %}
+            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
+            {% endif %}
+          </div>
+          {% endif %}
+
+          <div class="thread-replies">
+            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
+            {{ replies(thread.replies) }}, <span{% if (thread.upvotes-thread.downvotes) > 0 %} class="text-success"{% elif (thread.upvotes-thread.downvotes) < 0 %} class="text-error"{% endif %}><strong>{% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}</strong>{% trans rating=(thread.upvotes-thread.downvotes)|abs|intcomma %}{{ rating }} thread rating{% endtrans %}</span>
+          </div>
+
+        </div>
+      </div>
+    </div>
+    {% endfor %}
+  </div>
+  {{ pager() }}
+  {% else %}
+  <p class="lead">{% trans %}Looks like there are no popular threads... yet!{% endtrans %}</p>
+  {% endif %}
+</div>
+{% endblock %}
+
+
+{% macro replies(thread_replies) -%}
+{% trans count=thread_replies, replies=thread_replies|intcomma -%}
+{{ replies }} reply
+{%- pluralize -%}
+{{ replies }} replies
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro thread_starter(thread) -%}
+{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro thread_forum(thread) -%}
+<a href="{{ url('forum', forum=thread.forum_id, slug=thread.forum.slug) }}" class="forum-link">{{ thread.forum.name }}</a>
+{%- endmacro %}
+
+{% macro thread_reply(thread) -%}
+{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro pager() -%}
+{% if items_total > 0 and pagination['total'] > 1 %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('popular_threads', page=pagination['prev']) }}{% else %}{{ url('popular_threads') }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('popular_threads', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
 {%- endmacro %}
 {%- endmacro %}

+ 80 - 80
templates/cranefly/private_threads/changelog.html

@@ -1,81 +1,81 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %} <small>{{ thread.name }}</small></h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
-      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="post-changelog">
-    {% if edits %}
-    <table class="table table-striped">
-      <thead>
-        <tr>
-          <th style="width: 1%;">&nbsp;</th>
-          <th>{% trans %}Change Log{% endtrans %}</th>
-        </tr>
-      </thead>
-      <tbody>
-        {% for edit in edits %}
-        <tr>
-          <td>
-            <span class="change-{% if edit.change > 0 %}added{% elif edit.change < 0 %}removed{% else %}none{% endif %}{% if not edit.reason %} change-small{% endif %}">
-              {% if edit.change > 0 %}+{% endif %}{{ edit.change }}
-            </span>
-          </td>
-          <td>
-            <a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}" class="change-no">#{{ loop.revindex }}</a>
-            {% if edit.reason %}
-            <div class="change-reason">
-              <a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">{{ edit.reason }}</a>
-            </div>{% endif %}
-            <div class="change-description">
-              <a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">
-              {% if edit.change != 0 %}{% if edit.change > 0 -%}
-              {% trans chars=edit.change %}Added one character to post.{% pluralize %}Added {{ chars }} characters to post.{% endtrans %}
-              {%- elif edit.change < 0 -%}
-              {% trans chars=edit.change|abs %}Removed one character from post.{% pluralize %}Removed {{ chars }} characters from post.{% endtrans %}
-              {%- else -%}
-              {% trans %}No change in message's length.{% endtrans %}
-              {%- endif %}{% endif %}{% if edit.thread_name_old %} {% trans old=edit.thread_name_old, new=edit.thread_name_new %}Changed thread name from "{{ old }}" to "{{ new }}".{% endtrans %}{% endif %}</a>
-              <span class="change-details">
-                {% trans user=edit_user(edit), date=edit.date|reldate|low %}By {{ user }} {{ date }}{% endtrans %}
-              </span>
-            </div>
-          </td>
-        </tr>
-        {% endfor %}
-      </tbody>
-    </table>
-    {% else %}
-    <p class="lead">{% trans %}This post was never edited.{% endtrans %}</p>
-    {% endif %}
-  </div>
-</div>
-{% endblock %}
-
-
-{% macro edit_user(edit) -%}
-{% if edit.user_id %}<a href="{{ url('user', user=edit.user_id, username=edit.user_slug) }}">{{ edit.user_name }}</a>{% else %}{{ edit.user_name }}{% endif %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %} <small>{{ thread.name }}</small></h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
+      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="post-changelog">
+    {% if edits %}
+    <table class="table table-striped">
+      <thead>
+        <tr>
+          <th style="width: 1%;">&nbsp;</th>
+          <th>{% trans %}Change Log{% endtrans %}</th>
+        </tr>
+      </thead>
+      <tbody>
+        {% for edit in edits %}
+        <tr>
+          <td>
+            <span class="change-{% if edit.change > 0 %}added{% elif edit.change < 0 %}removed{% else %}none{% endif %}{% if not edit.reason %} change-small{% endif %}">
+              {% if edit.change > 0 %}+{% endif %}{{ edit.change }}
+            </span>
+          </td>
+          <td>
+            <a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}" class="change-no">#{{ loop.revindex }}</a>
+            {% if edit.reason %}
+            <div class="change-reason">
+              <a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">{{ edit.reason }}</a>
+            </div>{% endif %}
+            <div class="change-description">
+              <a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">
+              {% if edit.change != 0 %}{% if edit.change > 0 -%}
+              {% trans chars=edit.change %}Added one character to post.{% pluralize %}Added {{ chars }} characters to post.{% endtrans %}
+              {%- elif edit.change < 0 -%}
+              {% trans chars=edit.change|abs %}Removed one character from post.{% pluralize %}Removed {{ chars }} characters from post.{% endtrans %}
+              {%- else -%}
+              {% trans %}No change in message's length.{% endtrans %}
+              {%- endif %}{% endif %}{% if edit.thread_name_old %} {% trans old=edit.thread_name_old, new=edit.thread_name_new %}Changed thread name from "{{ old }}" to "{{ new }}".{% endtrans %}{% endif %}</a>
+              <span class="change-details">
+                {% trans user=edit_user(edit), date=edit.date|reldate|low %}By {{ user }} {{ date }}{% endtrans %}
+              </span>
+            </div>
+          </td>
+        </tr>
+        {% endfor %}
+      </tbody>
+    </table>
+    {% else %}
+    <p class="lead">{% trans %}This post was never edited.{% endtrans %}</p>
+    {% endif %}
+  </div>
+</div>
+{% endblock %}
+
+
+{% macro edit_user(edit) -%}
+{% if edit.user_id %}<a href="{{ url('user', user=edit.user_id, username=edit.user_slug) }}">{{ edit.user_name }}</a>{% else %}{{ edit.user_name }}{% endif %}
 {%- endmacro %}
 {%- endmacro %}

+ 95 - 95
templates/cranefly/private_threads/changelog_diff.html

@@ -1,95 +1,95 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %} <small>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</small></h1>
-    <ul class="unstyled header-stats pull-left">
-      <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if change.user_id %}<a href="{{ url('user', user=change.user_id, username=change.user_slug) }}">{{ change.user_name }}</a>{% else %}{{ change.user_name }}{% endif %}</li>
-      {% if acl.users.can_see_users_trails() %}
-      <li><i class="icon-globe"></i> {{ change.ip }}</li>
-      <li><i class="icon-qrcode"></i> {{ change.agent }}</li>
-      {% endif %}
-      {% if change.change != 0 %}<li><i class="icon-{% if change.change > 0 %}plus{% elif change.change < 0 %}minus{% endif %}"></i> {% if change.change > 0 -%}
-      {% trans chars=change.change %}Added one character{% pluralize %}Added {{ chars }} characters{% endtrans %}
-      {%- elif change.change < 0 -%}
-      {% trans chars=change.change|abs %}Removed one character{% pluralize %}Removed {{ chars }} characters{% endtrans %}
-      {%- endif %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="post-diff">
-    {% if message %}
-    <div class="messages-list">
-      {{ macros.draw_message(message) }}
-    </div>
-    {% endif %}
-
-    {% if change.reason %}
-    <p class="lead">{{ change.reason }}</p>
-    {% endif %}
-
-    {% if acl.threads.can_edit_reply(user, forum, thread, post) or prev or next %}
-    <div class="diff-extra">
-      {{ pager() }}
-      {% if user.is_authenticated() and acl.threads.can_make_revert(forum, thread) %}
-      <form class="form-inline pull-right" action="{{ url('private_thread_changelog_revert', thread=thread.pk, slug=thread.slug, post=post.pk, change=change.pk) }}" method="post">
-        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-        <button type="submit" class="btn btn-danger">{% trans %}Revert this edit{% endtrans %}</button></li>
-      </form>
-      {%- endif %}
-    </div>
-    {% endif %}
-
-    <div class="post-diff-details">
-      <table>
-        <tbody>
-          {% for line in diff %}{% if line[0] != "?" %}
-          <tr>
-            <td class="line"><a href="#{{ l }}">{{ l }}.</a></td>
-            <td class="{% if line[0] == '+' %}added{% elif line[0] == '-' %}removed{% else %}stag{% endif %}{% if l is even %} even{% endif %}">{% if line[2:] %}{{ line[2:] }}{% else %}&nbsp;{% endif %}</td>
-          </tr>
-          {% set l = l + 1 %}
-          {% endif %}{% endfor %}
-        </tbody>
-      </table>
-    </div>
-
-    {% if prev or next %}
-    <div class="diff-extra">
-      {{ pager() }}
-    </div>
-    {% endif %}
-
-  </div>
-</div>
-{% endblock %}
-
-
-{% macro pager() %}
-{% if prev or prev %}
-<div class="pagination pull-left">
-  <ul>
-    {% if prev %}<li><a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=prev.pk) }}"><i class="icon-chevron-left"></i> {{ prev.date|reldate }}</a></li>{% endif %}
-    {% if next %}<li><a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=next.pk) }}">{{ next.date|reldate }} <i class="icon-chevron-right"></i></a></li>{% endif %}
-  </ul>
-</div>
-{% endif %}
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %} <small>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</small></h1>
+    <ul class="unstyled header-stats pull-left">
+      <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if change.user_id %}<a href="{{ url('user', user=change.user_id, username=change.user_slug) }}">{{ change.user_name }}</a>{% else %}{{ change.user_name }}{% endif %}</li>
+      {% if acl.users.can_see_users_trails() %}
+      <li><i class="icon-globe"></i> {{ change.ip }}</li>
+      <li><i class="icon-qrcode"></i> {{ change.agent }}</li>
+      {% endif %}
+      {% if change.change != 0 %}<li><i class="icon-{% if change.change > 0 %}plus{% elif change.change < 0 %}minus{% endif %}"></i> {% if change.change > 0 -%}
+      {% trans chars=change.change %}Added one character{% pluralize %}Added {{ chars }} characters{% endtrans %}
+      {%- elif change.change < 0 -%}
+      {% trans chars=change.change|abs %}Removed one character{% pluralize %}Removed {{ chars }} characters{% endtrans %}
+      {%- endif %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="post-diff">
+    {% if message %}
+    <div class="messages-list">
+      {{ macros.draw_message(message) }}
+    </div>
+    {% endif %}
+
+    {% if change.reason %}
+    <p class="lead">{{ change.reason }}</p>
+    {% endif %}
+
+    {% if acl.threads.can_edit_reply(user, forum, thread, post) or prev or next %}
+    <div class="diff-extra">
+      {{ pager() }}
+      {% if user.is_authenticated() and acl.threads.can_make_revert(forum, thread) %}
+      <form class="form-inline pull-right" action="{{ url('private_thread_changelog_revert', thread=thread.pk, slug=thread.slug, post=post.pk, change=change.pk) }}" method="post">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        <button type="submit" class="btn btn-danger">{% trans %}Revert this edit{% endtrans %}</button></li>
+      </form>
+      {%- endif %}
+    </div>
+    {% endif %}
+
+    <div class="post-diff-details">
+      <table>
+        <tbody>
+          {% for line in diff %}{% if line[0] != "?" %}
+          <tr>
+            <td class="line"><a href="#{{ l }}">{{ l }}.</a></td>
+            <td class="{% if line[0] == '+' %}added{% elif line[0] == '-' %}removed{% else %}stag{% endif %}{% if l is even %} even{% endif %}">{% if line[2:] %}{{ line[2:] }}{% else %}&nbsp;{% endif %}</td>
+          </tr>
+          {% set l = l + 1 %}
+          {% endif %}{% endfor %}
+        </tbody>
+      </table>
+    </div>
+
+    {% if prev or next %}
+    <div class="diff-extra">
+      {{ pager() }}
+    </div>
+    {% endif %}
+
+  </div>
+</div>
+{% endblock %}
+
+
+{% macro pager() %}
+{% if prev or prev %}
+<div class="pagination pull-left">
+  <ul>
+    {% if prev %}<li><a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=prev.pk) }}"><i class="icon-chevron-left"></i> {{ prev.date|reldate }}</a></li>{% endif %}
+    {% if next %}<li><a href="{{ url('private_thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=next.pk) }}">{{ next.date|reldate }} <i class="icon-chevron-right"></i></a></li>{% endif %}
+  </ul>
+</div>
+{% endif %}
+{% endmacro %}

+ 34 - 34
templates/cranefly/private_threads/details.html

@@ -1,35 +1,35 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Info") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %} <small>{{ thread.name }}</small></h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
-      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <h2>{% trans %}IP Address{% endtrans %}</h2>
-  <p class="lead">{{ post.ip }}</p>
-  <h2>{% trans %}UserAgent{% endtrans %}</h2>
-  <p class="lead">{{ post.agent }}</p>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Info") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %} <small>{{ thread.name }}</small></h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
+      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <h2>{% trans %}IP Address{% endtrans %}</h2>
+  <p class="lead">{{ post.ip }}</p>
+  <h2>{% trans %}UserAgent{% endtrans %}</h2>
+  <p class="lead">{{ post.agent }}</p>
+</div>
 {% endblock %}
 {% endblock %}

+ 169 - 169
templates/cranefly/private_threads/list.html

@@ -1,169 +1,169 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_("Private Threads"),page=pagination['page']) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans %}Private Threads{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans %}Private Threads{% endtrans %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <div class="forum-threads-extra extra-top">
-    {{ pager() }}
-    {% if acl.threads.can_start_threads(forum) %}
-    <a href="{{ url('private_thread_start') }}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
-    {% endif %}
-  </div>
-
-  <div class="forum-threads-list">
-    <div class="header">
-      <div class="row-fluid">
-        <div class="span7">{% trans %}Thread{% endtrans %}</div>
-        <div class="span5 thread-activity">
-          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
-          {% if list_form %}
-          <div class="pull-right check-cell">
-            <label class="checkbox"><input type="checkbox" class="checkbox-master"></label>
-          </div>
-          {% endif %}
-        </div>
-      </div>
-    </div>
-    {% for thread in threads %}
-    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
-      <div class="row-fluid">
-        <div class="span7">
-          {% if thread.is_read %}
-          <a href="{{ url('private_thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
-          {% else %}
-          <a href="{{ url('private_thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
-          {% endif %}
-
-          <a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}" class="thread-name">{{ thread.name }}</a>
-
-          {{ macros.thread_flags(thread) }}
-
-          <div class="thread-details">
-            {% trans user=thread_starter(thread), start=thread.start|reldate|low %}by {{ user }}, {{ start }}{% endtrans %}
-          </div>
-
-        </div>
-        <div class="span5 thread-activity">
-
-          {% if settings.avatars_on_threads_list %}
-          <div class="thread-last-avatar">
-            {% if thread.last_poster_id %}
-            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
-            {% else %}
-            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
-            {% endif %}
-          </div>
-          {% endif %}
-
-          <div class="thread-replies">
-            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
-            {{ replies(thread.replies) }}
-          </div>
-
-          {% if list_form %}
-          <label class="thread-select checkbox"><input form="threads_form" name="{{ list_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ thread.pk }}"{% if list_form['list_items']['has_value'] and ('' ~ thread.pk) in list_form['list_items']['value'] %} checked="checked"{% endif %}></label>
-          {% endif %}
-
-        </div>
-      </div>
-    </div>
-    {% else %}
-    <div class="thread-row threads-list-empty">
-      {% trans %}You are not participating in any private discussions.{% endtrans %}
-    </div>
-    {% endfor %}
-    
-    {% if list_form %}
-    <div class="threads-actions">
-      <form id="threads_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
-        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-        {{ form_theme.input_select(list_form['list_action'],width=3) }}
-        <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-      </form>
-    </div>
-    {% endif %}
-  </div>
-
-  <div class="forum-threads-extra">
-    {{ pager() }}
-    {% if acl.threads.can_start_threads(forum) %}
-    <a href="{{ url('private_thread_start') }}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
-    {% endif %}
-  </div>
-
-</div>
-{% endblock %}
-
-
-{% macro replies(thread_replies) -%}
-{% trans count=thread_replies, replies=thread_replies|intcomma -%}
-{{ replies }} reply
-{%- pluralize -%}
-{{ replies }} replies
-{%- endtrans %}
-{%- endmacro %}
-
-{% macro thread_starter(thread) -%}
-{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro thread_reply(thread) -%}
-{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro pager() %}
-{% if pagination['total'] > 0 %}
-<div class="pagination pull-left">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('private_threads') }}" class="tooltip-top" title="{% trans %}First Page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('private_threads', page=pagination['prev']) }}{% else %}{{ url('private_threads') }}{% endif %}" class="tooltip-top" title="{% trans %}Newest Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('private_threads', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
-{% endmacro %}
-
-{% block javascripts -%}
-{{ super() }}
-{%- if list_form %}
-  <script type="text/javascript">
-    $(function () {
-      $('#threads_form').submit(function() {
-        if ($('.check-cell[]:checked').length == 0) {
-          alert("{% trans %}You have to select at least one thread.{% endtrans %}");
-          return false;
-        }
-        if ($('#id_list_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete selected threads? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });
-    });
-  </script>{% endif %}
-{%- endblock %}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_("Private Threads"),page=pagination['page']) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans %}Private Threads{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans %}Private Threads{% endtrans %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="forum-threads-extra extra-top">
+    {{ pager() }}
+    {% if acl.threads.can_start_threads(forum) %}
+    <a href="{{ url('private_thread_start') }}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
+    {% endif %}
+  </div>
+
+  <div class="forum-threads-list">
+    <div class="header">
+      <div class="row-fluid">
+        <div class="span7">{% trans %}Thread{% endtrans %}</div>
+        <div class="span5 thread-activity">
+          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
+          {% if list_form %}
+          <div class="pull-right check-cell">
+            <label class="checkbox"><input type="checkbox" class="checkbox-master"></label>
+          </div>
+          {% endif %}
+        </div>
+      </div>
+    </div>
+    {% for thread in threads %}
+    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
+      <div class="row-fluid">
+        <div class="span7">
+          {% if thread.is_read %}
+          <a href="{{ url('private_thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
+          {% else %}
+          <a href="{{ url('private_thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
+          {% endif %}
+
+          <a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}" class="thread-name">{{ thread.name }}</a>
+
+          {{ macros.thread_flags(thread) }}
+
+          <div class="thread-details">
+            {% trans user=thread_starter(thread), start=thread.start|reldate|low %}by {{ user }}, {{ start }}{% endtrans %}
+          </div>
+
+        </div>
+        <div class="span5 thread-activity">
+
+          {% if settings.avatars_on_threads_list %}
+          <div class="thread-last-avatar">
+            {% if thread.last_poster_id %}
+            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
+            {% else %}
+            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
+            {% endif %}
+          </div>
+          {% endif %}
+
+          <div class="thread-replies">
+            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
+            {{ replies(thread.replies) }}
+          </div>
+
+          {% if list_form %}
+          <label class="thread-select checkbox"><input form="threads_form" name="{{ list_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ thread.pk }}"{% if list_form['list_items']['has_value'] and ('' ~ thread.pk) in list_form['list_items']['value'] %} checked="checked"{% endif %}></label>
+          {% endif %}
+
+        </div>
+      </div>
+    </div>
+    {% else %}
+    <div class="thread-row threads-list-empty">
+      {% trans %}You are not participating in any private discussions.{% endtrans %}
+    </div>
+    {% endfor %}
+    
+    {% if list_form %}
+    <div class="threads-actions">
+      <form id="threads_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        {{ form_theme.input_select(list_form['list_action'],width=3) }}
+        <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+      </form>
+    </div>
+    {% endif %}
+  </div>
+
+  <div class="forum-threads-extra">
+    {{ pager() }}
+    {% if acl.threads.can_start_threads(forum) %}
+    <a href="{{ url('private_thread_start') }}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
+    {% endif %}
+  </div>
+
+</div>
+{% endblock %}
+
+
+{% macro replies(thread_replies) -%}
+{% trans count=thread_replies, replies=thread_replies|intcomma -%}
+{{ replies }} reply
+{%- pluralize -%}
+{{ replies }} replies
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro thread_starter(thread) -%}
+{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro thread_reply(thread) -%}
+{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro pager() %}
+{% if pagination['total'] > 0 %}
+<div class="pagination pull-left">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('private_threads') }}" class="tooltip-top" title="{% trans %}First Page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('private_threads', page=pagination['prev']) }}{% else %}{{ url('private_threads') }}{% endif %}" class="tooltip-top" title="{% trans %}Newest Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('private_threads', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{% endmacro %}
+
+{% block javascripts -%}
+{{ super() }}
+{%- if list_form %}
+  <script type="text/javascript">
+    $(function () {
+      $('#threads_form').submit(function() {
+        if ($('.check-cell[]:checked').length == 0) {
+          alert("{% trans %}You have to select at least one thread.{% endtrans %}");
+          return false;
+        }
+        if ($('#id_list_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete selected threads? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });
+    });
+  </script>{% endif %}
+{%- endblock %}

+ 181 - 181
templates/cranefly/private_threads/posting.html

@@ -1,181 +1,181 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/editor.html" as editor with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{% if thread -%}
-{{ macros.page_title(title=_(get_title()), parent=thread.name) }}
-{%- else -%}
-{{ macros.page_title(title=_(get_title()), parent=_("Private Threads")) }}
-{%- endif %}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{% if thread %}<li><a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>{% endif %}
-<li class="active">{{ get_title() }}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{{ get_title() }} <small>{% if thread %}{{ thread.name }}{% else %}{% trans %}Private Threads{% endtrans %}{% endif %}</small></h1>
-    {% if thread %}
-    <ul class="unstyled header-stats">
-      {{ get_info() }}
-    </ul>
-    {% endif %}
-  </div>
-</div>
-<div class="container container-primary">
-  <div class="row">
-    <div class="span8 offset2">
-      <div class="posting">
-        <div class="form-container">
-
-          <div class="form-header">
-            <h1>{{ get_title() }}</h1>
-          </div>
-
-          {% if message %}
-          <div class="messages-list">
-            {{ macros.draw_message(message) }}
-          </div>
-          {% endif %}
-
-          {% if preview %}
-          <div class="form-preview">
-            <div class="markdown js-extra">
-              <article>
-                {{ preview|markdown_final|safe }}
-              </article>
-            </div>
-          </div>
-          {% endif %}
-
-          <form action="{{ get_action() }}" method="post">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            {% if 'thread_name' in form.fields %}
-            {{ form_theme.row_widget(form.fields.thread_name, width=8) }}
-            {% endif %}
-            {% if action == 'new_thread' and 'invite_users' in form.fields %}
-            {{ form_theme.row_widget(form.fields.invite_users, width=8) }}
-            {% endif %}
-            {% if 'thread_name' in form.fields or (action == 'new_thread' and 'invite_users' in form.fields) %}
-            <hr>
-            <h4>{% trans %}Message Body{% endtrans %}</h4>
-            {% endif %}
-            {{ editor.editor(form.fields.post, get_button(), rows=8, extra=get_extra()) }}
-            {% if 'edit_reason' in form.fields or (action == 'new_reply' and 'invite_users' in form.fields) %}
-            <hr>
-            {% if action == 'new_reply' and 'invite_users' in form.fields %}
-            {{ form_theme.row_widget(form.fields.invite_users, width=8) }}
-            {% endif %}
-            {% if 'edit_reason' in form.fields %}
-            {{ form_theme.row_widget(form.fields.edit_reason, width=8) }}
-            {% endif %}
-
-            <div class="form-actions">
-              <button type="submit" class="btn btn-primary">{{ get_button() }}</button>
-              <button id="editor-preview" name="preview" type="submit" class="btn">{% trans %}Preview{% endtrans %}</button>
-            </div>
-            {% endif %}
-          </form>
-
-        </div>
-      </div>
-    </div>
-  </div>
-</div>
-{% endblock %}
-
-{% block stylesheets %}{{ super() }}
-<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
-{% endblock %}
-
-{% block javascripts %}{{ super() }}
-  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
-  <script type="text/javascript">
-    hljs.tabReplace = '    ';
-    hljs.initHighlightingOnLoad();
-    EnhancePostsMD();
-  </script>
-  {{ editor.js() }}
-{% endblock %}
-
-
-{% macro get_action() -%}
-{% if action == 'new_thread' -%}
-{{ url('private_thread_start') }}
-{%- elif action == 'edit_thread' -%}
-{{ url('private_thread_edit', thread=thread.pk, slug=thread.slug) }}
-{%- elif action in 'new_reply' -%}
-{%- if quote -%}
-{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug, quote=quote.pk) }}
-{%- else -%}
-{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug) }}
-{%- endif -%}
-{%- elif action == 'edit_reply' -%}
-{{ url('private_post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_title() -%}
-{% if action == 'new_thread' -%}
-{% trans %}Post New Thread{% endtrans %}
-{%- elif action == 'edit_thread' -%}
-{% trans %}Edit Thread{% endtrans %}
-{%- elif action == 'new_reply' -%}
-{% trans %}Post New Reply{% endtrans %}
-{%- elif action == 'edit_reply' -%}
-{% trans %}Edit Reply{% endtrans %}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_info() -%}
-{% if action == 'edit_reply' -%}
-    {% if post.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
-    <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-    <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-    <li><i class="icon-pencil"></i> {% if post.edits > 0 -%}
-      {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}
-    {%- else -%}
-      {% trans %}First edit{% endtrans %}
-    {%- endif %}</li>
-{%- else -%}
-    {% if thread.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
-    {% if action == 'edit_thread' %}
-    <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=thread.start_post_id) }}">{{ thread.start|reltimesince }}</a></li>
-    {% else %}
-    <li><i class="icon-time"></i> <a href="{{ url('private_thread_new', thread=thread.pk, slug=thread.slug) }}">{{ thread.last|reltimesince }}</a></li>
-    {% endif %}
-    <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
-    <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
-      {% trans count=thread.replies, replies=thread.replies|intcomma %}One reply{% pluralize %}{{ replies }} replies{% endtrans %}
-    {%- else -%}
-      {% trans %}No replies{% endtrans %}
-    {%- endif %}</li>
-{%- endif %}
-    {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
-{%- endmacro %}
-
-
-{% macro get_button() -%}
-{% if action == 'new_thread' -%}
-{% trans %}Post Thread{% endtrans %}
-{%- elif action == 'new_reply' -%}
-{% trans %}Post Reply{% endtrans %}
-{%- else -%}
-{% trans %}Save Changes{% endtrans %}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_extra() %}
-  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Preview{% endtrans %}</button>
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/editor.html" as editor with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{% if thread -%}
+{{ macros.page_title(title=_(get_title()), parent=thread.name) }}
+{%- else -%}
+{{ macros.page_title(title=_(get_title()), parent=_("Private Threads")) }}
+{%- endif %}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{% if thread %}<li><a href="{{ url('private_thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>{% endif %}
+<li class="active">{{ get_title() }}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{{ get_title() }} <small>{% if thread %}{{ thread.name }}{% else %}{% trans %}Private Threads{% endtrans %}{% endif %}</small></h1>
+    {% if thread %}
+    <ul class="unstyled header-stats">
+      {{ get_info() }}
+    </ul>
+    {% endif %}
+  </div>
+</div>
+<div class="container container-primary">
+  <div class="row">
+    <div class="span8 offset2">
+      <div class="posting">
+        <div class="form-container">
+
+          <div class="form-header">
+            <h1>{{ get_title() }}</h1>
+          </div>
+
+          {% if message %}
+          <div class="messages-list">
+            {{ macros.draw_message(message) }}
+          </div>
+          {% endif %}
+
+          {% if preview %}
+          <div class="form-preview">
+            <div class="markdown js-extra">
+              <article>
+                {{ preview|markdown_final|safe }}
+              </article>
+            </div>
+          </div>
+          {% endif %}
+
+          <form action="{{ get_action() }}" method="post">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            {% if 'thread_name' in form.fields %}
+            {{ form_theme.row_widget(form.fields.thread_name, width=8) }}
+            {% endif %}
+            {% if action == 'new_thread' and 'invite_users' in form.fields %}
+            {{ form_theme.row_widget(form.fields.invite_users, width=8) }}
+            {% endif %}
+            {% if 'thread_name' in form.fields or (action == 'new_thread' and 'invite_users' in form.fields) %}
+            <hr>
+            <h4>{% trans %}Message Body{% endtrans %}</h4>
+            {% endif %}
+            {{ editor.editor(form.fields.post, get_button(), rows=8, extra=get_extra()) }}
+            {% if 'edit_reason' in form.fields or (action == 'new_reply' and 'invite_users' in form.fields) %}
+            <hr>
+            {% if action == 'new_reply' and 'invite_users' in form.fields %}
+            {{ form_theme.row_widget(form.fields.invite_users, width=8) }}
+            {% endif %}
+            {% if 'edit_reason' in form.fields %}
+            {{ form_theme.row_widget(form.fields.edit_reason, width=8) }}
+            {% endif %}
+
+            <div class="form-actions">
+              <button type="submit" class="btn btn-primary">{{ get_button() }}</button>
+              <button id="editor-preview" name="preview" type="submit" class="btn">{% trans %}Preview{% endtrans %}</button>
+            </div>
+            {% endif %}
+          </form>
+
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock %}
+
+{% block stylesheets %}{{ super() }}
+<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
+{% endblock %}
+
+{% block javascripts %}{{ super() }}
+  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
+  <script type="text/javascript">
+    hljs.tabReplace = '    ';
+    hljs.initHighlightingOnLoad();
+    EnhancePostsMD();
+  </script>
+  {{ editor.js() }}
+{% endblock %}
+
+
+{% macro get_action() -%}
+{% if action == 'new_thread' -%}
+{{ url('private_thread_start') }}
+{%- elif action == 'edit_thread' -%}
+{{ url('private_thread_edit', thread=thread.pk, slug=thread.slug) }}
+{%- elif action in 'new_reply' -%}
+{%- if quote -%}
+{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug, quote=quote.pk) }}
+{%- else -%}
+{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug) }}
+{%- endif -%}
+{%- elif action == 'edit_reply' -%}
+{{ url('private_post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_title() -%}
+{% if action == 'new_thread' -%}
+{% trans %}Post New Thread{% endtrans %}
+{%- elif action == 'edit_thread' -%}
+{% trans %}Edit Thread{% endtrans %}
+{%- elif action == 'new_reply' -%}
+{% trans %}Post New Reply{% endtrans %}
+{%- elif action == 'edit_reply' -%}
+{% trans %}Edit Reply{% endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_info() -%}
+{% if action == 'edit_reply' -%}
+    {% if post.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
+    <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+    <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+    <li><i class="icon-pencil"></i> {% if post.edits > 0 -%}
+      {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}
+    {%- else -%}
+      {% trans %}First edit{% endtrans %}
+    {%- endif %}</li>
+{%- else -%}
+    {% if thread.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
+    {% if action == 'edit_thread' %}
+    <li><i class="icon-time"></i> <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=thread.start_post_id) }}">{{ thread.start|reltimesince }}</a></li>
+    {% else %}
+    <li><i class="icon-time"></i> <a href="{{ url('private_thread_new', thread=thread.pk, slug=thread.slug) }}">{{ thread.last|reltimesince }}</a></li>
+    {% endif %}
+    <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
+    <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
+      {% trans count=thread.replies, replies=thread.replies|intcomma %}One reply{% pluralize %}{{ replies }} replies{% endtrans %}
+    {%- else -%}
+      {% trans %}No replies{% endtrans %}
+    {%- endif %}</li>
+{%- endif %}
+    {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
+{%- endmacro %}
+
+
+{% macro get_button() -%}
+{% if action == 'new_thread' -%}
+{% trans %}Post Thread{% endtrans %}
+{%- elif action == 'new_reply' -%}
+{% trans %}Post Reply{% endtrans %}
+{%- else -%}
+{% trans %}Save Changes{% endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_extra() %}
+  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Preview{% endtrans %}</button>
+{% endmacro %}

+ 582 - 582
templates/cranefly/private_threads/thread.html

@@ -1,583 +1,583 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/editor.html" as editor with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=thread.name,parent=_("Private Threads"),page=pagination['page']) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{{ thread.name }}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{{ thread.name }}</h1>
-    <ul class="unstyled header-stats">
-      {% if thread.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
-      <li><i class="icon-time"></i> {{ thread.last|reltimesince }}</li>
-      <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
-      <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
-        {% trans count=thread.replies, replies=thread.replies|intcomma %}One reply{% pluralize %}{{ replies }} replies{% endtrans %}
-      {%- else -%}
-        {% trans %}No replies{% endtrans %}
-      {%- endif %}</li>
-      {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
-      <li class="stats-form">
-        <form class="leave-form" action="{{ url('private_thread_remove_user', thread=thread.pk, slug=thread.slug) }}" method="post">
-          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-          <input type="hidden" name="user" value="{{ user.pk }}">
-          <button type="submit" class="btn"><i class="icon-remove"></i> {% trans %}Leave Thread{% endtrans %}</button>
-        </form>
-      </li>
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <div class="row">
-    <div class="span9">
-
-      <div class="thread-buttons">
-        {{ pager() }} 
-        {% if acl.threads.can_reply(forum, thread) and (acl.private_threads.is_mod() or participants|length > 1) %}
-        <a href="{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Reply{% endtrans %}</a>
-        {% endif %}
-        {% if watcher %}
-        <form action="{{ url('private_thread_unwatch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Remove thread from watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
-        {% if watcher.email %}
-        <form action="{{ url('private_thread_unwatch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Don't e-mail me anymore if anyone replies to this thread{% endtrans %}"><i class="icon-envelope"></i></button></form>
-        {% else %}
-        <form action="{{ url('private_thread_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}E-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
-        {% endif %}
-        {% else %}
-        <form action="{{ url('private_thread_watch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
-        <form action="{{ url('private_thread_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list and e-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
-        {% endif %}
-        {% if ignored_posts %}
-        <form action="{{ url('private_thread_show_hidden', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn"><i class="icon-eye-open"></i> {% trans %}Show Hidden Replies{% endtrans %}</button></form>
-        {% endif %}
-      </div>
-
-      <div class="thread-body">
-        {% for post in posts %}
-        <div id="post-{{ post.pk }}" class="post-wrapper">
-          {% if post.message %}
-          <div class="messages-list">
-            {{ macros.draw_message(post.message) }}
-          </div>
-          {% endif %}
-          {% if post.deleted and not acl.threads.can_see_deleted_posts(forum) %}
-          <div class="post-body post-muted">
-            {% if post.user_id %}
-            <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(50) }}" alt="" class="user-avatar"></a>
-            {% else %}
-            <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
-            {% endif %}
-            <div class="post-content">
-              <div class="post-header">
-                <div class="post-header-compact">
-                  {% if post.user_id %}
-                  <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
-                  {% else %}
-                  <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
-                  {% endif %}
-                  <span class="separator">&ndash;</span>
-                  <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
-                  {% if post.edits %}
-                  <span class="separator">&ndash;</span>
-                  {% if acl.threads.can_see_changelog(user, forum, post) %}
-                  <a href="{{ url('private_thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
-                  {% else %}
-                  <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
-                  {% endif %}
-                  {% endif %}
-                </div>
-
-                <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
-
-                {% if not post.is_read %}
-                <div class="post-extra">
-                  <span class="label label-warning">
-                    {% trans %}New{% endtrans %}
-                  </span>
-                </div>
-                {% endif %}
-
-              </div>
-              <div class="post-message">
-                {% trans user=edit_user(post), date=post.delete_date|reltimesince|low %}{{ user }} has deleted this reply {{ date }}{% endtrans %}
-              </div>
-            </dv>
-          </div>
-          {% elif post.ignored %}
-          <div class="post-body post-muted">
-            <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
-            <div class="post-arrow"></div>
-            <div class="post-content">
-              <div class="post-header">
-                <div class="post-header-compact">
-                  <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
-                </div>
-
-                <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
-
-                {% if not post.is_read %}
-                <div class="post-extra">
-                  <span class="label label-warning">
-                    {% trans %}New{% endtrans %}
-                  </span>
-                </div>
-                {% endif %}
-
-              </div>
-              <div class="post-message">
-                {% trans %}This reply was posted by user that is on your ignored list.{% endtrans %}
-              </div>
-            </dv>
-          </div>
-          {% else %}
-          <div class="post-body">
-            {% if post.user_id %}
-            <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(100) }}" alt="" class="user-avatar"></a>
-            {% else %}
-            <img src="{{ macros.avatar_guest(100) }}" alt="" class="user-avatar">
-            {% endif %}
-            <div class="post-arrow"></div>
-            <div class="post-content">
-              <div class="post-header">
-                {% if post.user_id %}
-                <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
-                {% else %}
-                <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
-                {% endif %}
-                <span class="separator">&ndash;</span>
-                <a href="{% if pagination['page'] > 1 -%}
-                {{ url('private_thread', thread=thread.pk, slug=thread.slug, page=pagination['page']) }}
-                {%- else -%}
-                {{ url('private_thread', thread=thread.pk, slug=thread.slug) }}
-                {%- endif %}#post-{{ post.pk }}" class="post-date">{{ post.date|reltimesince }}</a>
-                {% if post.edits %}
-                <span class="separator">&ndash;</span>
-                {% if acl.threads.can_see_changelog(user, forum, post) %}
-                <a href="{{ url('private_thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
-                {% else %}
-                <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
-                {% endif %}
-                {% endif %}
-
-                {% if posts_form %}
-                <label class="checkbox post-checkbox"><input form="posts_form" name="{{ posts_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ post.pk }}"{% if posts_form['list_items']['has_value'] and ('' ~ post.pk) in posts_form['list_items']['value'] %} checked="checked"{% endif %}></label>
-                {% endif %}
-
-                <a href="{% if pagination['page'] > 1 -%}
-                {{ url('private_thread', thread=thread.pk, slug=thread.slug, page=pagination['page']) }}
-                {%- else -%}
-                {{ url('private_thread', thread=thread.pk, slug=thread.slug) }}
-                {%- endif %}#post-{{ post.pk }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
-
-                <div class="post-extra">
-                  {% if post.protected and acl.threads.can_protect(forum) %}
-                  <span class="label label-info">
-                    {% trans %}Protected{% endtrans %}
-                  </span>
-                  {% endif %}
-
-                  {% if post.deleted %}
-                  <span class="label label-inverse">
-                    {% trans %}Deleted{% endtrans %}
-                  </span>
-                  {% endif %}
-
-                  {% if post.moderated %}
-                  <span class="label label-purple">
-                    {% trans %}Unreviewed{% endtrans %}
-                  </span>
-                  {% endif %}
-
-                  {% if acl.threads.can_mod_posts(forum) and post.reported %}
-                  <span class="label label-important">
-                    {% trans %}Reported{% endtrans %}
-                  </span>
-                  {% endif %}
-
-                  {% if not post.is_read %}
-                  <span class="label label-warning">
-                    {% trans %}New{% endtrans %}
-                  </span>
-                  {% endif %}
-                </div>
-              </div>
-              <div class="post-message">
-                <div class="markdown js-extra">
-                  <article>
-                    {{ post.post_preparsed|markdown_final|safe }}
-                  </article>
-                </div>
-                {% if post.user.signature %}
-                <div class="post-signature">
-                  <div class="markdown">
-                    {{ post.user.signature_preparsed|markdown_final|safe }}
-                  </div>
-                </div>
-                {% endif %}
-              </div>
-              <div class="post-footer">{% filter trim %}
-                <div class="post-actions">              
-                  {% if acl.users.can_see_users_trails() -%}
-                  <a href="{{ url('private_post_info', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-trail">{% trans %}Info{% endtrans %}</a>
-                  {% endif %}
-                  {% if post.reported and acl.reports.can_handle() and acl.threads.can_mod_posts(forum) %}
-                  <a href="{{ url('private_post_report_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans %}Show report{% endtrans %}</a>
-                  {% endif %}
-                  {% if acl.reports.can_report() %}
-                  {% if post.reported_by(user) %}
-                  <a href="{{ url('private_post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="tooltip-top" title="{% trans %}You have already reported this post.{% endtrans %}" disabled="disabled">{% trans %}Reported{% endtrans %}</a>
-                  {% else %}
-                  <form action="{{ url('private_post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-report" method="post" autocomplete="off">
-                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                    <button type="submit" class="btn btn-link btn-report tooltip-top" title="{% trans %}Bring this post to moderator attention.{% endtrans %}">{% trans %}Report{% endtrans %}</button>
-                  </form>
-                  {% endif %}
-                  {% endif %}
-                  {% if acl.threads.can_edit_thread(user, forum, thread, post) and thread.start_post_id == post.pk %}
-                  <a href="{{ url('private_thread_edit', thread=thread.pk, slug=thread.slug) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
-                  {% elif acl.threads.can_edit_reply(user, forum, thread, post) %}
-                  <a href="{{ url('private_post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
-                  {%- endif %}
-                  {% if acl.threads.can_reply(forum, thread) and (acl.private_threads.is_mod() or participants|length > 1) %}
-                  <a href="{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug, quote=post.pk) }}" class="post-reply">{% trans %}Reply{% endtrans %}</a>
-                  {% endif %}
-                </div>
-                {% if post.pk == thread.start_post_id %}
-                <div class="post-actions">
-                  {% if acl.threads.can_delete_thread(user, forum, thread, post) %}
-                  {% if post.deleted %}
-                  <form action="{{ url('private_thread_show', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
-                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                    <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this thread visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
-                  </form>
-                  {% else %}
-                  <form action="{{ url('private_thread_hide', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
-                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                    <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this thread from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
-                  </form>
-                  {% endif %}
-                  {% endif %}
-                  {% if acl.threads.can_delete_thread(user, forum, thread, post) == 2 %}
-                  <form action="{{ url('private_thread_delete', thread=thread.pk, slug=thread.slug) }}" class="form-inline prompt-delete-thread" method="post">
-                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                    <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this thread for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
-                  </form>
-                  {% endif %}
-                </div>
-                {% elif post.pk != thread.start_post_id and acl.threads.can_delete_post(user, forum, thread, post) %}
-                <div class="post-actions">
-                  {% if acl.threads.can_delete_post(user, forum, thread, post) %}
-                  {% if post.deleted %}
-                  <form action="{{ url('private_post_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
-                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                    <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this reply visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
-                  </form>
-                  {% else %}
-                  <form action="{{ url('private_post_hide', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
-                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                    <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this reply from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
-                  </form>
-                  {% endif %}
-                  {% endif %}
-                  {% if acl.threads.can_delete_post(user, forum, thread, post) == 2 -%}
-                  <form action="{{ url('private_post_delete', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline prompt-delete-post" method="post">
-                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                    <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this reply for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
-                  </form>
-                  {% endif %}
-                </div>
-                {% endif %}
-              {% endfilter %}</div>
-            </div>
-          </div>
-          {% endif %}
-        </div>
-
-        {% if post.checkpoints_visible %}
-        <div class="post-checkpoints">
-          {% for checkpoint in post.checkpoints_visible %}
-          <div class="post-checkpoint">
-            <hr>
-            <span>
-              {%- if checkpoint.action == 'limit' -%}
-              <i class="icon-lock"></i> {% trans  %}This thread has reached its post limit and has been closed.{% endtrans %}
-              {%- elif checkpoint.action == 'accepted' -%}
-              <i class="icon-ok"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} accepted this thread {{ date }}{% endtrans %}
-              {%- elif checkpoint.action == 'closed' -%}
-              <i class="icon-lock"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} closed this thread {{ date }}{% endtrans %}
-              {%- elif checkpoint.action == 'opened' -%}
-              <i class="icon-lock"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} opened this thread {{ date }}{% endtrans %}
-              {%- elif checkpoint.action == 'deleted' -%}
-              <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} deleted this thread {{ date }}{% endtrans %}
-              {%- elif checkpoint.action == 'undeleted' -%}
-              <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} restored this thread {{ date }}{% endtrans %}
-              {%- elif checkpoint.action == 'invited' -%}
-              <i class="icon-plus-sign"></i> {% trans user=checkpoint_user(checkpoint), invited=checkpoint_target(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} added {{ invited }} to thread {{ date }}{% endtrans %}
-              {%- elif checkpoint.action == 'removed' -%}
-              <i class="icon-remove-sign"></i> {% trans user=checkpoint_user(checkpoint), removed=checkpoint_target(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} removed {{ removed }} from thread {{ date }}{% endtrans %}
-              {%- elif checkpoint.action == 'left' -%}
-              <i class="icon-remove-sign"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} left thread {{ date }}{% endtrans %}
-              {%- endif -%}
-              {% if user.is_authenticated() %}
-              {% if acl.threads.can_delete_checkpoint(forum) %}
-              {% if checkpoint.deleted %}
-              <form action="{{ url('private_post_checkpoint_show', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-                <button type="submit" class="btn btn-link btn-show">{% trans %}Restore{% endtrans %}</button>
-              </form>
-              {% else %}
-              <form action="{{ url('private_post_checkpoint_hide', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-                <button type="submit" class="btn btn-link btn-hide">{% trans %}Hide{% endtrans %}</button>
-              </form>
-              {% endif %}
-              {% endif %}
-              {% if acl.threads.can_delete_checkpoint(forum) == 2 %}
-              <form action="{{ url('private_post_checkpoint_delete', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline prompt-delete-checkpoint">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-                <button type="submit" class="btn btn-link btn-delete">{% trans %}Delete{% endtrans %}</button>
-              </form>
-              {% endif %}
-              {% endif %}
-            </span>
-          </div>
-          {% endfor %}
-        </div>
-        {% endif %}
-        {% endfor %}
-      </div>
-
-      {% if thread_form or posts_form %}
-      <div class="thread-moderation">
-        {% if thread_form%}
-        <form id="thread_form" class="form-inline pull-left" action="{{ request_path }}" method="POST">
-          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-          <input type="hidden" name="origin" value="thread_form">
-          {{ form_theme.input_select(thread_form['thread_action'],width=3) }}
-          <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-        </form>
-        {% endif %}
-        {% if posts_form%}
-        <form id="posts_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
-          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-          <input type="hidden" name="origin" value="posts_form">
-          {{ form_theme.input_select(posts_form['list_action'],width=3) }}
-          <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-        </form>
-        {% endif %}
-      </div>
-      {% endif %}
-
-      <div class="thread-buttons">
-        {{ pager(false) }}
-        {% if acl.threads.can_reply(forum, thread) and (acl.private_threads.is_mod() or participants|length > 1) %}
-        <a href="{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Reply{% endtrans %}</a>
-        {% else %}
-        <p class="lead thread-signin-message">{% trans %}This thread has too few participants.{% endtrans %}</p>
-        {% endif %}
-      </div>
-
-      {% if acl.threads.can_reply(forum, thread) and (acl.private_threads.is_mod() or participants|length > 1) %}
-      <div class="thread-quick-reply">
-        <form action="{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug) }}" method="post">
-          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-          <input type="hidden" name="quick_reply" value="1">
-          <img src="{{ user.get_avatar(100) }}" alt="{% trans %}Your Avatar{% endtrans %}" class="user-avatar">
-          {{ editor.editor(quick_reply.post, _('Post Reply'), extra=editor_extra()) }}
-        </form>
-      </div>
-      {% endif %}
-
-    </div>
-    <div class="span3">
-      <div class="thread-participants">
-        <h3>{% trans participants=participants|length -%}
-          One participant
-          {%- pluralize -%}
-          {{ participants }} participants
-          {%- endtrans %}</h3>
-        <ul class="unstyled">{% for participant in participants %}
-          <li>
-            <img src="{{ participant.get_avatar(24) }}" alt="" class="avatar-small">
-            <a href="{{ url('user', username=participant.username_slug, user=participant.pk) }}">{{ participant.username }}</a>
-            {% if user.pk == thread.start_poster_id or acl.private_threads.is_mod() %}
-            <form class="form-inline {% if user.pk == participant.pk %}leave-form{% else %}kick-form{% endif %} tooltip-left" action="{{ url('private_thread_remove_user', thread=thread.pk, slug=thread.slug) }}" method="post" title="{% if participant.pk == user.pk %}{% trans %}Leave this thread{% endtrans %}{% else %}{% trans %}Remove from this thread{% endtrans %}{% endif %}">
-              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-              <input type="hidden" name="retreat" value="{{ request_path }}">
-              <input type="hidden" name="user" value="{{ participant.pk }}">
-              <button type="submit" class="btn btn-{% if participant.pk == user.pk %}danger{% else %}inverse{% endif %} btn-small"><i class="icon-remove"></i></button>
-            </form>
-            {% endif %}
-          </li>
-          {% endfor %}
-        </ul>
-
-        {% if participants|length < 2 %}
-        <p class="no-participants">{% trans %}This thread has too few participants. Invite other users to open it for new replies.{% endtrans %}</p>
-        {% endif %}
-
-        <h4>{% trans %}Invite User{% endtrans %}</h4>
-        <div class="invite-participant">
-          <form class="form-inline" action="{{ url('private_thread_invite_user', thread=thread.pk, slug=thread.slug) }}" method="post">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}">
-            {{ form_theme.input_text(invite_form.fields.username, width="2", attrs={'placeholder':lang_user_to_invite()}) }}
-            <button class="btn" type="submit"><i class="icon-plus"></i></button>
-          </form>
-        </div>
-      </div>
-    </div>
-  </div>
-
-</div>
-{% endblock %}
-
-{% block stylesheets %}{{ super() }}
-<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
-{% endblock %}
-
-{% block javascripts -%}{{ super() }}
-  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
-  <script type="text/javascript">
-    var l_post_reported = "{{ _('Reported!') }}";
-    hljs.tabReplace = '    ';
-    hljs.initHighlightingOnLoad();
-    EnhancePostsMD();
-    $(function () {
-      $('.leave-form').submit(function() {
-        var decision = confirm("{% if participants|length == 1 -%}
-          {% trans %}Are you sure you want to leave this thread? It will be deleted after you leave!{% endtrans %}
-          {%- else -%}
-          {% trans %}Are you sure you want to leave this thread?{% endtrans %}
-          {%- endif %}");
-        return decision;
-      });
-      $('.kick-form').submit(function() {
-        var decision = confirm("{% trans %}Are you sure you want to remove this member from this thread?{% endtrans %}");
-        return decision;
-      });
-      $('#thread_form').submit(function() {
-        if ($('#id_thread_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete this thread? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });
-      $('#posts_form').submit(function() {
-        if ($('.post-checkbox[]:checked').length == 0) {
-          alert("{% trans %}You have to select at least one post.{% endtrans %}");
-          return false;
-        }
-        if ($('#id_list_action').val() == 'merge') {
-          if ($('.post-checkbox[]:checked').length < 2) {
-              alert("{% trans %}You have to select at least two posts you want to merge.{% endtrans %}");
-              return false;
-          }
-          var decision = confirm("{% trans %}Are you sure you want to merge selected posts? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        if ($('#id_list_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete selected posts? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });
-      $('.prompt-delete-thread').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this thread?{% endtrans %}");
-          return decision;
-      });
-      $('.prompt-delete-post').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this post?{% endtrans %}");
-          return decision;
-      });
-      $('.prompt-delete-checkpoint').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this checkpoint?{% endtrans %}");
-          return decision;
-      });
-    });
-  </script>
-  {% if acl.threads.can_reply(forum, thread) %}
-  {{ editor.js() }}
-  {% endif %}
-{%- endblock %}
-
-
-{% macro user_label(user) -%}
-<{% if user.rank and user.rank.as_tab %}a href="{{ url('users', slug=user.rank.slug) }}"{% else %}span{% endif %} class="label post-author-label{% if user.rank and user.rank.style %} post-label-{{ user.rank.style }}{% endif %}">{{ user.get_title() }}</{% if user.rank and user.rank.as_tab%}a{% else %}span{% endif %}>
-{%- endmacro %}
-
-
-{% macro pager(extra=true) %}
-<div class="pagination pull-left">
-  <ul>
-    {% if pagination['total'] > 0 %}
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('private_thread', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('private_thread', slug=thread.slug, thread=thread.id, page=pagination['prev']) }}{% else %}{{ url('private_thread', slug=thread.slug, thread=thread.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('private_thread', slug=thread.slug, thread=thread.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Newest Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 and pagination['next'] < pagination['total'] %}<li><a href="{{ url('private_thread', slug=thread.slug, thread=thread.id, page=pagination['total']) }}" class="tooltip-top" title="{% trans %}Go to last page{% endtrans %}">{% trans %}Last{% endtrans %} <i class="icon-chevron-right"></i></a></li>{% endif -%}
-    {% endif %}
-    {% if extra %}
-    {% if not is_read %}<li><a href="{{ url('private_thread_new', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first unread{% endtrans %}"><i class="icon-star"></i> {% trans %}First Unread{% endtrans %}</a></li>{% endif %}
-    {% endif %}
-    {% if thread.replies_reported > 0 and acl.threads.can_mod_posts(thread) %}<li><a href="{{ url('private_thread_reported', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first reported post{% endtrans %}"><i class="icon-fire"></i> {% trans %}First Reported{% endtrans %}</a></li>{% endif %}
-  </ul>
-</div>
-{% endmacro %}
-
-
-{% macro checkpoint_user(checkpoint) -%}
-{%- if checkpoint.user_id -%}
-<a href="{{ url('user', user=checkpoint.user_id, username=checkpoint.user_slug) }}">{{ checkpoint.user_name }}</a>
-{%- else -%}
-<strong>{{ checkpoint.user_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro checkpoint_target(checkpoint) -%}
-{%- if checkpoint.target_user_id -%}
-<a href="{{ url('user', user=checkpoint.target_user_id, username=checkpoint.target_user_slug) }}">{{ checkpoint.target_user_name }}</a>
-{%- else -%}
-<strong>{{ checkpoint.target_user_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro edit_user(post) -%}
-{%- if post.edit_user_id -%}
-<a href="{{ url('user', user=post.edit_user_id, username=post.edit_user_slug) }}">{{ post.edit_user_name }}</a>
-{%- else -%}
-<strong>{{ post.edit_user_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro editor_extra() %}
-  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Full Editor{% endtrans %}</button>
-{% endmacro %}
-
-
-{# Language strings macros #}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/editor.html" as editor with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=thread.name,parent=_("Private Threads"),page=pagination['page']) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('private_threads') }}">{% trans %}Private Threads{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{{ thread.name }}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{{ thread.name }}</h1>
+    <ul class="unstyled header-stats">
+      {% if thread.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
+      <li><i class="icon-time"></i> {{ thread.last|reltimesince }}</li>
+      <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
+      <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
+        {% trans count=thread.replies, replies=thread.replies|intcomma %}One reply{% pluralize %}{{ replies }} replies{% endtrans %}
+      {%- else -%}
+        {% trans %}No replies{% endtrans %}
+      {%- endif %}</li>
+      {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
+      <li class="stats-form">
+        <form class="leave-form" action="{{ url('private_thread_remove_user', thread=thread.pk, slug=thread.slug) }}" method="post">
+          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+          <input type="hidden" name="user" value="{{ user.pk }}">
+          <button type="submit" class="btn"><i class="icon-remove"></i> {% trans %}Leave Thread{% endtrans %}</button>
+        </form>
+      </li>
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="row">
+    <div class="span9">
+
+      <div class="thread-buttons">
+        {{ pager() }} 
+        {% if acl.threads.can_reply(forum, thread) and (acl.private_threads.is_mod() or participants|length > 1) %}
+        <a href="{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Reply{% endtrans %}</a>
+        {% endif %}
+        {% if watcher %}
+        <form action="{{ url('private_thread_unwatch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Remove thread from watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
+        {% if watcher.email %}
+        <form action="{{ url('private_thread_unwatch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Don't e-mail me anymore if anyone replies to this thread{% endtrans %}"><i class="icon-envelope"></i></button></form>
+        {% else %}
+        <form action="{{ url('private_thread_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}E-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
+        {% endif %}
+        {% else %}
+        <form action="{{ url('private_thread_watch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
+        <form action="{{ url('private_thread_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list and e-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
+        {% endif %}
+        {% if ignored_posts %}
+        <form action="{{ url('private_thread_show_hidden', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn"><i class="icon-eye-open"></i> {% trans %}Show Hidden Replies{% endtrans %}</button></form>
+        {% endif %}
+      </div>
+
+      <div class="thread-body">
+        {% for post in posts %}
+        <div id="post-{{ post.pk }}" class="post-wrapper">
+          {% if post.message %}
+          <div class="messages-list">
+            {{ macros.draw_message(post.message) }}
+          </div>
+          {% endif %}
+          {% if post.deleted and not acl.threads.can_see_deleted_posts(forum) %}
+          <div class="post-body post-muted">
+            {% if post.user_id %}
+            <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(50) }}" alt="" class="user-avatar"></a>
+            {% else %}
+            <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
+            {% endif %}
+            <div class="post-content">
+              <div class="post-header">
+                <div class="post-header-compact">
+                  {% if post.user_id %}
+                  <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
+                  {% else %}
+                  <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
+                  {% endif %}
+                  <span class="separator">&ndash;</span>
+                  <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
+                  {% if post.edits %}
+                  <span class="separator">&ndash;</span>
+                  {% if acl.threads.can_see_changelog(user, forum, post) %}
+                  <a href="{{ url('private_thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
+                  {% else %}
+                  <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
+                  {% endif %}
+                  {% endif %}
+                </div>
+
+                <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
+
+                {% if not post.is_read %}
+                <div class="post-extra">
+                  <span class="label label-warning">
+                    {% trans %}New{% endtrans %}
+                  </span>
+                </div>
+                {% endif %}
+
+              </div>
+              <div class="post-message">
+                {% trans user=edit_user(post), date=post.delete_date|reltimesince|low %}{{ user }} has deleted this reply {{ date }}{% endtrans %}
+              </div>
+            </dv>
+          </div>
+          {% elif post.ignored %}
+          <div class="post-body post-muted">
+            <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
+            <div class="post-arrow"></div>
+            <div class="post-content">
+              <div class="post-header">
+                <div class="post-header-compact">
+                  <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
+                </div>
+
+                <a href="{{ url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
+
+                {% if not post.is_read %}
+                <div class="post-extra">
+                  <span class="label label-warning">
+                    {% trans %}New{% endtrans %}
+                  </span>
+                </div>
+                {% endif %}
+
+              </div>
+              <div class="post-message">
+                {% trans %}This reply was posted by user that is on your ignored list.{% endtrans %}
+              </div>
+            </dv>
+          </div>
+          {% else %}
+          <div class="post-body">
+            {% if post.user_id %}
+            <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(100) }}" alt="" class="user-avatar"></a>
+            {% else %}
+            <img src="{{ macros.avatar_guest(100) }}" alt="" class="user-avatar">
+            {% endif %}
+            <div class="post-arrow"></div>
+            <div class="post-content">
+              <div class="post-header">
+                {% if post.user_id %}
+                <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
+                {% else %}
+                <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
+                {% endif %}
+                <span class="separator">&ndash;</span>
+                <a href="{% if pagination['page'] > 1 -%}
+                {{ url('private_thread', thread=thread.pk, slug=thread.slug, page=pagination['page']) }}
+                {%- else -%}
+                {{ url('private_thread', thread=thread.pk, slug=thread.slug) }}
+                {%- endif %}#post-{{ post.pk }}" class="post-date">{{ post.date|reltimesince }}</a>
+                {% if post.edits %}
+                <span class="separator">&ndash;</span>
+                {% if acl.threads.can_see_changelog(user, forum, post) %}
+                <a href="{{ url('private_thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
+                {% else %}
+                <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
+                {% endif %}
+                {% endif %}
+
+                {% if posts_form %}
+                <label class="checkbox post-checkbox"><input form="posts_form" name="{{ posts_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ post.pk }}"{% if posts_form['list_items']['has_value'] and ('' ~ post.pk) in posts_form['list_items']['value'] %} checked="checked"{% endif %}></label>
+                {% endif %}
+
+                <a href="{% if pagination['page'] > 1 -%}
+                {{ url('private_thread', thread=thread.pk, slug=thread.slug, page=pagination['page']) }}
+                {%- else -%}
+                {{ url('private_thread', thread=thread.pk, slug=thread.slug) }}
+                {%- endif %}#post-{{ post.pk }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
+
+                <div class="post-extra">
+                  {% if post.protected and acl.threads.can_protect(forum) %}
+                  <span class="label label-info">
+                    {% trans %}Protected{% endtrans %}
+                  </span>
+                  {% endif %}
+
+                  {% if post.deleted %}
+                  <span class="label label-inverse">
+                    {% trans %}Deleted{% endtrans %}
+                  </span>
+                  {% endif %}
+
+                  {% if post.moderated %}
+                  <span class="label label-purple">
+                    {% trans %}Unreviewed{% endtrans %}
+                  </span>
+                  {% endif %}
+
+                  {% if acl.threads.can_mod_posts(forum) and post.reported %}
+                  <span class="label label-important">
+                    {% trans %}Reported{% endtrans %}
+                  </span>
+                  {% endif %}
+
+                  {% if not post.is_read %}
+                  <span class="label label-warning">
+                    {% trans %}New{% endtrans %}
+                  </span>
+                  {% endif %}
+                </div>
+              </div>
+              <div class="post-message">
+                <div class="markdown js-extra">
+                  <article>
+                    {{ post.post_preparsed|markdown_final|safe }}
+                  </article>
+                </div>
+                {% if post.user.signature %}
+                <div class="post-signature">
+                  <div class="markdown">
+                    {{ post.user.signature_preparsed|markdown_final|safe }}
+                  </div>
+                </div>
+                {% endif %}
+              </div>
+              <div class="post-footer">{% filter trim %}
+                <div class="post-actions">              
+                  {% if acl.users.can_see_users_trails() -%}
+                  <a href="{{ url('private_post_info', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-trail">{% trans %}Info{% endtrans %}</a>
+                  {% endif %}
+                  {% if post.reported and acl.reports.can_handle() and acl.threads.can_mod_posts(forum) %}
+                  <a href="{{ url('private_post_report_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans %}Show report{% endtrans %}</a>
+                  {% endif %}
+                  {% if acl.reports.can_report() %}
+                  {% if post.reported_by(user) %}
+                  <a href="{{ url('private_post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="tooltip-top" title="{% trans %}You have already reported this post.{% endtrans %}" disabled="disabled">{% trans %}Reported{% endtrans %}</a>
+                  {% else %}
+                  <form action="{{ url('private_post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-report" method="post" autocomplete="off">
+                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                    <button type="submit" class="btn btn-link btn-report tooltip-top" title="{% trans %}Bring this post to moderator attention.{% endtrans %}">{% trans %}Report{% endtrans %}</button>
+                  </form>
+                  {% endif %}
+                  {% endif %}
+                  {% if acl.threads.can_edit_thread(user, forum, thread, post) and thread.start_post_id == post.pk %}
+                  <a href="{{ url('private_thread_edit', thread=thread.pk, slug=thread.slug) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
+                  {% elif acl.threads.can_edit_reply(user, forum, thread, post) %}
+                  <a href="{{ url('private_post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
+                  {%- endif %}
+                  {% if acl.threads.can_reply(forum, thread) and (acl.private_threads.is_mod() or participants|length > 1) %}
+                  <a href="{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug, quote=post.pk) }}" class="post-reply">{% trans %}Reply{% endtrans %}</a>
+                  {% endif %}
+                </div>
+                {% if post.pk == thread.start_post_id %}
+                <div class="post-actions">
+                  {% if acl.threads.can_delete_thread(user, forum, thread, post) %}
+                  {% if post.deleted %}
+                  <form action="{{ url('private_thread_show', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
+                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                    <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this thread visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
+                  </form>
+                  {% else %}
+                  <form action="{{ url('private_thread_hide', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
+                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                    <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this thread from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
+                  </form>
+                  {% endif %}
+                  {% endif %}
+                  {% if acl.threads.can_delete_thread(user, forum, thread, post) == 2 %}
+                  <form action="{{ url('private_thread_delete', thread=thread.pk, slug=thread.slug) }}" class="form-inline prompt-delete-thread" method="post">
+                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                    <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this thread for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
+                  </form>
+                  {% endif %}
+                </div>
+                {% elif post.pk != thread.start_post_id and acl.threads.can_delete_post(user, forum, thread, post) %}
+                <div class="post-actions">
+                  {% if acl.threads.can_delete_post(user, forum, thread, post) %}
+                  {% if post.deleted %}
+                  <form action="{{ url('private_post_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
+                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                    <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this reply visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
+                  </form>
+                  {% else %}
+                  <form action="{{ url('private_post_hide', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
+                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                    <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this reply from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
+                  </form>
+                  {% endif %}
+                  {% endif %}
+                  {% if acl.threads.can_delete_post(user, forum, thread, post) == 2 -%}
+                  <form action="{{ url('private_post_delete', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline prompt-delete-post" method="post">
+                    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                    <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this reply for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
+                  </form>
+                  {% endif %}
+                </div>
+                {% endif %}
+              {% endfilter %}</div>
+            </div>
+          </div>
+          {% endif %}
+        </div>
+
+        {% if post.checkpoints_visible %}
+        <div class="post-checkpoints">
+          {% for checkpoint in post.checkpoints_visible %}
+          <div class="post-checkpoint">
+            <hr>
+            <span>
+              {%- if checkpoint.action == 'limit' -%}
+              <i class="icon-lock"></i> {% trans  %}This thread has reached its post limit and has been closed.{% endtrans %}
+              {%- elif checkpoint.action == 'accepted' -%}
+              <i class="icon-ok"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} accepted this thread {{ date }}{% endtrans %}
+              {%- elif checkpoint.action == 'closed' -%}
+              <i class="icon-lock"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} closed this thread {{ date }}{% endtrans %}
+              {%- elif checkpoint.action == 'opened' -%}
+              <i class="icon-lock"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} opened this thread {{ date }}{% endtrans %}
+              {%- elif checkpoint.action == 'deleted' -%}
+              <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} deleted this thread {{ date }}{% endtrans %}
+              {%- elif checkpoint.action == 'undeleted' -%}
+              <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} restored this thread {{ date }}{% endtrans %}
+              {%- elif checkpoint.action == 'invited' -%}
+              <i class="icon-plus-sign"></i> {% trans user=checkpoint_user(checkpoint), invited=checkpoint_target(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} added {{ invited }} to thread {{ date }}{% endtrans %}
+              {%- elif checkpoint.action == 'removed' -%}
+              <i class="icon-remove-sign"></i> {% trans user=checkpoint_user(checkpoint), removed=checkpoint_target(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} removed {{ removed }} from thread {{ date }}{% endtrans %}
+              {%- elif checkpoint.action == 'left' -%}
+              <i class="icon-remove-sign"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} left thread {{ date }}{% endtrans %}
+              {%- endif -%}
+              {% if user.is_authenticated() %}
+              {% if acl.threads.can_delete_checkpoint(forum) %}
+              {% if checkpoint.deleted %}
+              <form action="{{ url('private_post_checkpoint_show', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+                <button type="submit" class="btn btn-link btn-show">{% trans %}Restore{% endtrans %}</button>
+              </form>
+              {% else %}
+              <form action="{{ url('private_post_checkpoint_hide', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+                <button type="submit" class="btn btn-link btn-hide">{% trans %}Hide{% endtrans %}</button>
+              </form>
+              {% endif %}
+              {% endif %}
+              {% if acl.threads.can_delete_checkpoint(forum) == 2 %}
+              <form action="{{ url('private_post_checkpoint_delete', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline prompt-delete-checkpoint">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+                <button type="submit" class="btn btn-link btn-delete">{% trans %}Delete{% endtrans %}</button>
+              </form>
+              {% endif %}
+              {% endif %}
+            </span>
+          </div>
+          {% endfor %}
+        </div>
+        {% endif %}
+        {% endfor %}
+      </div>
+
+      {% if thread_form or posts_form %}
+      <div class="thread-moderation">
+        {% if thread_form%}
+        <form id="thread_form" class="form-inline pull-left" action="{{ request_path }}" method="POST">
+          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+          <input type="hidden" name="origin" value="thread_form">
+          {{ form_theme.input_select(thread_form['thread_action'],width=3) }}
+          <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+        </form>
+        {% endif %}
+        {% if posts_form%}
+        <form id="posts_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
+          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+          <input type="hidden" name="origin" value="posts_form">
+          {{ form_theme.input_select(posts_form['list_action'],width=3) }}
+          <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+        </form>
+        {% endif %}
+      </div>
+      {% endif %}
+
+      <div class="thread-buttons">
+        {{ pager(false) }}
+        {% if acl.threads.can_reply(forum, thread) and (acl.private_threads.is_mod() or participants|length > 1) %}
+        <a href="{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Reply{% endtrans %}</a>
+        {% else %}
+        <p class="lead thread-signin-message">{% trans %}This thread has too few participants.{% endtrans %}</p>
+        {% endif %}
+      </div>
+
+      {% if acl.threads.can_reply(forum, thread) and (acl.private_threads.is_mod() or participants|length > 1) %}
+      <div class="thread-quick-reply">
+        <form action="{{ url('private_thread_reply', thread=thread.pk, slug=thread.slug) }}" method="post">
+          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+          <input type="hidden" name="quick_reply" value="1">
+          <img src="{{ user.get_avatar(100) }}" alt="{% trans %}Your Avatar{% endtrans %}" class="user-avatar">
+          {{ editor.editor(quick_reply.post, _('Post Reply'), extra=editor_extra()) }}
+        </form>
+      </div>
+      {% endif %}
+
+    </div>
+    <div class="span3">
+      <div class="thread-participants">
+        <h3>{% trans participants=participants|length -%}
+          One participant
+          {%- pluralize -%}
+          {{ participants }} participants
+          {%- endtrans %}</h3>
+        <ul class="unstyled">{% for participant in participants %}
+          <li>
+            <img src="{{ participant.get_avatar(24) }}" alt="" class="avatar-small">
+            <a href="{{ url('user', username=participant.username_slug, user=participant.pk) }}">{{ participant.username }}</a>
+            {% if user.pk == thread.start_poster_id or acl.private_threads.is_mod() %}
+            <form class="form-inline {% if user.pk == participant.pk %}leave-form{% else %}kick-form{% endif %} tooltip-left" action="{{ url('private_thread_remove_user', thread=thread.pk, slug=thread.slug) }}" method="post" title="{% if participant.pk == user.pk %}{% trans %}Leave this thread{% endtrans %}{% else %}{% trans %}Remove from this thread{% endtrans %}{% endif %}">
+              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+              <input type="hidden" name="retreat" value="{{ request_path }}">
+              <input type="hidden" name="user" value="{{ participant.pk }}">
+              <button type="submit" class="btn btn-{% if participant.pk == user.pk %}danger{% else %}inverse{% endif %} btn-small"><i class="icon-remove"></i></button>
+            </form>
+            {% endif %}
+          </li>
+          {% endfor %}
+        </ul>
+
+        {% if participants|length < 2 %}
+        <p class="no-participants">{% trans %}This thread has too few participants. Invite other users to open it for new replies.{% endtrans %}</p>
+        {% endif %}
+
+        <h4>{% trans %}Invite User{% endtrans %}</h4>
+        <div class="invite-participant">
+          <form class="form-inline" action="{{ url('private_thread_invite_user', thread=thread.pk, slug=thread.slug) }}" method="post">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}">
+            {{ form_theme.input_text(invite_form.fields.username, width="2", attrs={'placeholder':lang_user_to_invite()}) }}
+            <button class="btn" type="submit"><i class="icon-plus"></i></button>
+          </form>
+        </div>
+      </div>
+    </div>
+  </div>
+
+</div>
+{% endblock %}
+
+{% block stylesheets %}{{ super() }}
+<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
+{% endblock %}
+
+{% block javascripts -%}{{ super() }}
+  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
+  <script type="text/javascript">
+    var l_post_reported = "{{ _('Reported!') }}";
+    hljs.tabReplace = '    ';
+    hljs.initHighlightingOnLoad();
+    EnhancePostsMD();
+    $(function () {
+      $('.leave-form').submit(function() {
+        var decision = confirm("{% if participants|length == 1 -%}
+          {% trans %}Are you sure you want to leave this thread? It will be deleted after you leave!{% endtrans %}
+          {%- else -%}
+          {% trans %}Are you sure you want to leave this thread?{% endtrans %}
+          {%- endif %}");
+        return decision;
+      });
+      $('.kick-form').submit(function() {
+        var decision = confirm("{% trans %}Are you sure you want to remove this member from this thread?{% endtrans %}");
+        return decision;
+      });
+      $('#thread_form').submit(function() {
+        if ($('#id_thread_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete this thread? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });
+      $('#posts_form').submit(function() {
+        if ($('.post-checkbox[]:checked').length == 0) {
+          alert("{% trans %}You have to select at least one post.{% endtrans %}");
+          return false;
+        }
+        if ($('#id_list_action').val() == 'merge') {
+          if ($('.post-checkbox[]:checked').length < 2) {
+              alert("{% trans %}You have to select at least two posts you want to merge.{% endtrans %}");
+              return false;
+          }
+          var decision = confirm("{% trans %}Are you sure you want to merge selected posts? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        if ($('#id_list_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete selected posts? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });
+      $('.prompt-delete-thread').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this thread?{% endtrans %}");
+          return decision;
+      });
+      $('.prompt-delete-post').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this post?{% endtrans %}");
+          return decision;
+      });
+      $('.prompt-delete-checkpoint').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this checkpoint?{% endtrans %}");
+          return decision;
+      });
+    });
+  </script>
+  {% if acl.threads.can_reply(forum, thread) %}
+  {{ editor.js() }}
+  {% endif %}
+{%- endblock %}
+
+
+{% macro user_label(user) -%}
+<{% if user.rank and user.rank.as_tab %}a href="{{ url('users', slug=user.rank.slug) }}"{% else %}span{% endif %} class="label post-author-label{% if user.rank and user.rank.style %} post-label-{{ user.rank.style }}{% endif %}">{{ user.get_title() }}</{% if user.rank and user.rank.as_tab%}a{% else %}span{% endif %}>
+{%- endmacro %}
+
+
+{% macro pager(extra=true) %}
+<div class="pagination pull-left">
+  <ul>
+    {% if pagination['total'] > 0 %}
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('private_thread', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('private_thread', slug=thread.slug, thread=thread.id, page=pagination['prev']) }}{% else %}{{ url('private_thread', slug=thread.slug, thread=thread.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('private_thread', slug=thread.slug, thread=thread.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Newest Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 and pagination['next'] < pagination['total'] %}<li><a href="{{ url('private_thread', slug=thread.slug, thread=thread.id, page=pagination['total']) }}" class="tooltip-top" title="{% trans %}Go to last page{% endtrans %}">{% trans %}Last{% endtrans %} <i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {% endif %}
+    {% if extra %}
+    {% if not is_read %}<li><a href="{{ url('private_thread_new', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first unread{% endtrans %}"><i class="icon-star"></i> {% trans %}First Unread{% endtrans %}</a></li>{% endif %}
+    {% endif %}
+    {% if thread.replies_reported > 0 and acl.threads.can_mod_posts(thread) %}<li><a href="{{ url('private_thread_reported', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first reported post{% endtrans %}"><i class="icon-fire"></i> {% trans %}First Reported{% endtrans %}</a></li>{% endif %}
+  </ul>
+</div>
+{% endmacro %}
+
+
+{% macro checkpoint_user(checkpoint) -%}
+{%- if checkpoint.user_id -%}
+<a href="{{ url('user', user=checkpoint.user_id, username=checkpoint.user_slug) }}">{{ checkpoint.user_name }}</a>
+{%- else -%}
+<strong>{{ checkpoint.user_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro checkpoint_target(checkpoint) -%}
+{%- if checkpoint.target_user_id -%}
+<a href="{{ url('user', user=checkpoint.target_user_id, username=checkpoint.target_user_slug) }}">{{ checkpoint.target_user_name }}</a>
+{%- else -%}
+<strong>{{ checkpoint.target_user_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro edit_user(post) -%}
+{%- if post.edit_user_id -%}
+<a href="{{ url('user', user=post.edit_user_id, username=post.edit_user_slug) }}">{{ post.edit_user_name }}</a>
+{%- else -%}
+<strong>{{ post.edit_user_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro editor_extra() %}
+  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Full Editor{% endtrans %}</button>
+{% endmacro %}
+
+
+{# Language strings macros #}
 {% macro lang_user_to_invite() -%}{% trans %}User to invite...{% endtrans %}{%- endmacro %}
 {% macro lang_user_to_invite() -%}{% trans %}User to invite...{% endtrans %}{%- endmacro %}

+ 243 - 243
templates/cranefly/profiles/details.html

@@ -1,244 +1,244 @@
-{% extends "cranefly/profiles/profile.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(lang_member_details(), profile.username) }}{% endblock %}
-
-{% block tab %}
-<div class="user-details">
-  <div class="row">
-    <div class="span6">
-    	
-      <table class="table table-striped">
-        <thead>
-          <tr>
-            <th colspan="2">
-            	{% trans %}Account Details{% endtrans %}
-            </th>
-          </tr>
-        </thead>
-        <tbody>
-          {% if acl.users.can_see_users_emails() %}
-          <tr>
-            <td class="span2">
-               <strong>{% trans %}E-mail Address{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-              <a href="mailto:{{ profile.email }}">{{ profile.email }}</a>
-            </td>
-          </tr>
-          {% endif %}
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Member Since{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	{{ profile.join_date|date }}
-            </td>
-          </tr>
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Last Seen{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-              {% if not hidden or acl.users.can_see_hidden_users() %}
-            	{{ profile.last_date|reltimesince }} <span class="muted">{{ profile.last_date|date("DATETIME_FORMAT") }}</span>
-            	{% else %}
-            	<em class="muted">{% trans %}Hidden{% endtrans %}</em>
-            	{% endif %}
-            </td>
-          </tr>
-        </tbody>
-      </table>
-    	
-      <table class="table table-striped">
-        <thead>
-          <tr>
-            <th colspan="2">
-            	{% trans %}Forums Activity{% endtrans %}
-            </th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Posts Written{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	{{ profile.posts|intcomma }}
-            </td>
-          </tr>
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Threads Started{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	{{ profile.threads|intcomma }}
-            </td>
-          </tr>
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Votes Cast{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	{{ profile.votes|intcomma }}
-            </td>
-          </tr>
-        </tbody>
-      </table>
-      
-    </div>
-    <div class="span6">
-    	
-      <table class="table table-striped">
-        <thead>
-          <tr>
-            <th colspan="2">
-            	{% trans %}Ranking Performance{% endtrans %}
-            </th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Rank{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	{% if profile.rank %}{{ _(profile.rank.name) }}{% else %}<em>{% trans %}Not Ranked{% endtrans %}</em>{% endif %}
-            </td>
-          </tr>
-          {% if settings.ranking_positions_visible or settings.ranking_scores_visible %}
-          <tr>
-            <td class="span2">
-               <strong>{% if settings.ranking_positions_visible %}{% trans %}Ranking Position{% endtrans %}{% else %}{% trans %}Score{% endtrans %}{% endif %}</strong>
-            </td>
-            <td class="span4">
-              {% if settings.ranking_positions_visible %}
-              #{{ profile.get_ranking()|intcomma }}{% if settings.ranking_scores_visible %} <span class="muted">{{ profile.score|intcomma }}</span>{% endif %}
-              {% else %}{{ profile.score|intcomma }}
-              {% endif %}
-            </td>
-          </tr>
-          {% endif %}
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Karma Received{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	<span class="label label-success">+ {{ profile.karma_p }}</span> / <span class="label label-important">- {{ profile.karma_n }}</span></strong>
-            </td>
-          </tr>
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Karma Given{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	<span class="label label-success">+ {{ profile.karma_given_p }}</span> / <span class="label label-important">- {{ profile.karma_given_n }}</span></strong>
-            </td>
-          </tr>
-        </tbody>
-      </table>
-    	
-      <table class="table table-striped">
-        <thead>
-          <tr>
-            <th colspan="2">
-            	{% trans %}Interactions{% endtrans %}
-            </th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Followers{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	{{ profile.followers|intcomma }}
-            </td>
-          </tr>
-          <tr>
-            <td class="span2">
-            	 <strong>{% trans %}Following{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-            	{{ profile.following|intcomma }}
-            </td>
-          </tr>
-        </tbody>
-      </table>
-      
-    </div>
-  </div>
-      
-  {% if acl.users.can_see_users_trails() %}
-  <hr>
-  <div class="row">
-    <div class="span6">
-
-      <table class="table table-striped">
-        <thead>
-          <tr>
-            <th colspan="2">
-              {% trans %}Registration Details{% endtrans %}
-            </th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr>
-            <td class="span2">
-               <strong>{% trans %}IP Address{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-              {{ profile.join_ip }}
-            </td>
-          </tr>
-          <tr>
-            <td class="span2">
-               <strong>{% trans %}UserAgent String{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-              {% if profile.join_agent %}{{ profile.join_agent }}{% else %}<em class="muted">{% trans %}Created from console{% endtrans %}</em>{% endif %}
-            </td>
-          </tr>
-        </tbody>
-      </table>
-      
-    </div>
-    <div class="span6">
-        
-      <table class="table table-striped">
-        <thead>
-          <tr>
-            <th colspan="2">
-              {% trans %}Last Visit Details{% endtrans %}
-            </th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr>
-            <td class="span2">
-               <strong>{% trans %}IP Address{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-              {{ profile.last_ip }}
-            </td>
-          </tr>
-          <tr>
-            <td class="span2">
-               <strong>{% trans %}UserAgent String{% endtrans %}</strong>
-            </td>
-            <td class="span4">
-              {% if profile.last_agent %}{{ profile.last_agent }}{% else %}<em class="muted">{% trans %}Created from console{% endtrans %}</em>{% endif %}
-            </td>
-          </tr>
-        </tbody>
-      </table>
-      
-    </div>
-  </div>
-</div>
-{% endif %}
-{% endblock %}
-
-{# Language strings macros #}
+{% extends "cranefly/profiles/profile.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(lang_member_details(), profile.username) }}{% endblock %}
+
+{% block tab %}
+<div class="user-details">
+  <div class="row">
+    <div class="span6">
+    	
+      <table class="table table-striped">
+        <thead>
+          <tr>
+            <th colspan="2">
+            	{% trans %}Account Details{% endtrans %}
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          {% if acl.users.can_see_users_emails() %}
+          <tr>
+            <td class="span2">
+               <strong>{% trans %}E-mail Address{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+              <a href="mailto:{{ profile.email }}">{{ profile.email }}</a>
+            </td>
+          </tr>
+          {% endif %}
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Member Since{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	{{ profile.join_date|date }}
+            </td>
+          </tr>
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Last Seen{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+              {% if not hidden or acl.users.can_see_hidden_users() %}
+            	{{ profile.last_date|reltimesince }} <span class="muted">{{ profile.last_date|date("DATETIME_FORMAT") }}</span>
+            	{% else %}
+            	<em class="muted">{% trans %}Hidden{% endtrans %}</em>
+            	{% endif %}
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    	
+      <table class="table table-striped">
+        <thead>
+          <tr>
+            <th colspan="2">
+            	{% trans %}Forums Activity{% endtrans %}
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Posts Written{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	{{ profile.posts|intcomma }}
+            </td>
+          </tr>
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Threads Started{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	{{ profile.threads|intcomma }}
+            </td>
+          </tr>
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Votes Cast{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	{{ profile.votes|intcomma }}
+            </td>
+          </tr>
+        </tbody>
+      </table>
+      
+    </div>
+    <div class="span6">
+    	
+      <table class="table table-striped">
+        <thead>
+          <tr>
+            <th colspan="2">
+            	{% trans %}Ranking Performance{% endtrans %}
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Rank{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	{% if profile.rank %}{{ _(profile.rank.name) }}{% else %}<em>{% trans %}Not Ranked{% endtrans %}</em>{% endif %}
+            </td>
+          </tr>
+          {% if settings.ranking_positions_visible or settings.ranking_scores_visible %}
+          <tr>
+            <td class="span2">
+               <strong>{% if settings.ranking_positions_visible %}{% trans %}Ranking Position{% endtrans %}{% else %}{% trans %}Score{% endtrans %}{% endif %}</strong>
+            </td>
+            <td class="span4">
+              {% if settings.ranking_positions_visible %}
+              #{{ profile.get_ranking()|intcomma }}{% if settings.ranking_scores_visible %} <span class="muted">{{ profile.score|intcomma }}</span>{% endif %}
+              {% else %}{{ profile.score|intcomma }}
+              {% endif %}
+            </td>
+          </tr>
+          {% endif %}
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Karma Received{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	<span class="label label-success">+ {{ profile.karma_p }}</span> / <span class="label label-important">- {{ profile.karma_n }}</span></strong>
+            </td>
+          </tr>
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Karma Given{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	<span class="label label-success">+ {{ profile.karma_given_p }}</span> / <span class="label label-important">- {{ profile.karma_given_n }}</span></strong>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    	
+      <table class="table table-striped">
+        <thead>
+          <tr>
+            <th colspan="2">
+            	{% trans %}Interactions{% endtrans %}
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Followers{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	{{ profile.followers|intcomma }}
+            </td>
+          </tr>
+          <tr>
+            <td class="span2">
+            	 <strong>{% trans %}Following{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+            	{{ profile.following|intcomma }}
+            </td>
+          </tr>
+        </tbody>
+      </table>
+      
+    </div>
+  </div>
+      
+  {% if acl.users.can_see_users_trails() %}
+  <hr>
+  <div class="row">
+    <div class="span6">
+
+      <table class="table table-striped">
+        <thead>
+          <tr>
+            <th colspan="2">
+              {% trans %}Registration Details{% endtrans %}
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td class="span2">
+               <strong>{% trans %}IP Address{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+              {{ profile.join_ip }}
+            </td>
+          </tr>
+          <tr>
+            <td class="span2">
+               <strong>{% trans %}UserAgent String{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+              {% if profile.join_agent %}{{ profile.join_agent }}{% else %}<em class="muted">{% trans %}Created from console{% endtrans %}</em>{% endif %}
+            </td>
+          </tr>
+        </tbody>
+      </table>
+      
+    </div>
+    <div class="span6">
+        
+      <table class="table table-striped">
+        <thead>
+          <tr>
+            <th colspan="2">
+              {% trans %}Last Visit Details{% endtrans %}
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td class="span2">
+               <strong>{% trans %}IP Address{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+              {{ profile.last_ip }}
+            </td>
+          </tr>
+          <tr>
+            <td class="span2">
+               <strong>{% trans %}UserAgent String{% endtrans %}</strong>
+            </td>
+            <td class="span4">
+              {% if profile.last_agent %}{{ profile.last_agent }}{% else %}<em class="muted">{% trans %}Created from console{% endtrans %}</em>{% endif %}
+            </td>
+          </tr>
+        </tbody>
+      </table>
+      
+    </div>
+  </div>
+</div>
+{% endif %}
+{% endblock %}
+
+{# Language strings macros #}
 {% macro lang_member_details() -%}{% trans %}Member Details{% endtrans %}{%- endmacro %}
 {% macro lang_member_details() -%}{% trans %}Member Details{% endtrans %}{%- endmacro %}

+ 58 - 58
templates/cranefly/profiles/followers.html

@@ -1,58 +1,58 @@
-{% extends "cranefly/profiles/profile.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(_('Followers'), profile.username) }}{% endblock %}
-
-{% block tab %}
-<h2>{% if items_total %}
-    {%- trans count=items_total, total=items_total|intcomma, username=profile.username -%}
-    {{ username }} is followed by one member
-    {%- pluralize -%}
-    {{ username }} is followed by {{ total }} members
-    {%- endtrans -%}
-    {%- else -%}
-    {% trans username=profile.username %}{{ username }} has no followers{% endtrans %}
-    {%- endif %}</h2>
-
-{% if items_total %}
-<div class="user-list user-followers">
-  <table class="table table-striped">
-    <thead>
-      <tr>
-        <th colspan="4">{% trans username=profile.username %}Users that are following {{ username }}{% endtrans %}</th>
-      </tr>
-    </thead>
-    <tbody>
-      {% for row in items|batch(4, '') %}
-      <tr>
-        {% for user in row %}
-        <td class="span3 user-cell">
-          {% if user %}
-          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-avatar"><img src="{{ user.get_avatar(36) }}" alt=""></a>
-          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-name">{{ user.username }}</a>
-          {% else %}
-          &nbsp;
-          {% endif %}
-        </td>
-        {% endfor %}
-      </tr>
-      {% endfor %}
-    </tbody>
-  </table>
-  {{ pager() }}
-</div>
-{% endif %}
-{% endblock %}
-
-{% macro pager() -%}
-{% if pagination['total'] > 1 %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('user_followers', user=profile.id, username=profile.username_slug, page=pagination['prev']) }}{% else %}{{ url('user_followers', user=profile.id, username=profile.username_slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('user_followers', user=profile.id, username=profile.username_slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
-{%- endmacro %}
+{% extends "cranefly/profiles/profile.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(_('Followers'), profile.username) }}{% endblock %}
+
+{% block tab %}
+<h2>{% if items_total %}
+    {%- trans count=items_total, total=items_total|intcomma, username=profile.username -%}
+    {{ username }} is followed by one member
+    {%- pluralize -%}
+    {{ username }} is followed by {{ total }} members
+    {%- endtrans -%}
+    {%- else -%}
+    {% trans username=profile.username %}{{ username }} has no followers{% endtrans %}
+    {%- endif %}</h2>
+
+{% if items_total %}
+<div class="user-list user-followers">
+  <table class="table table-striped">
+    <thead>
+      <tr>
+        <th colspan="4">{% trans username=profile.username %}Users that are following {{ username }}{% endtrans %}</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for row in items|batch(4, '') %}
+      <tr>
+        {% for user in row %}
+        <td class="span3 user-cell">
+          {% if user %}
+          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-avatar"><img src="{{ user.get_avatar(36) }}" alt=""></a>
+          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-name">{{ user.username }}</a>
+          {% else %}
+          &nbsp;
+          {% endif %}
+        </td>
+        {% endfor %}
+      </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+  {{ pager() }}
+</div>
+{% endif %}
+{% endblock %}
+
+{% macro pager() -%}
+{% if pagination['total'] > 1 %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('user_followers', user=profile.id, username=profile.username_slug, page=pagination['prev']) }}{% else %}{{ url('user_followers', user=profile.id, username=profile.username_slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('user_followers', user=profile.id, username=profile.username_slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{%- endmacro %}

+ 58 - 58
templates/cranefly/profiles/follows.html

@@ -1,58 +1,58 @@
-{% extends "cranefly/profiles/profile.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(_('Follows'), profile.username) }}{% endblock %}
-
-{% block tab %}
-<h2>{% if items_total %}
-    {%- trans count=items_total, total=items_total|intcomma, username=profile.username -%}
-    {{ username }} is following one member
-    {%- pluralize -%}
-    {{ username }} is following {{ total }} members
-    {%- endtrans -%}
-    {%- else -%}
-    {% trans username=profile.username %}{{ username }} is not following anybody{% endtrans %}
-    {%- endif %}</h2>
-
-{% if items_total %}
-<div class="user-list user-follows">
-  <table class="table table-striped">
-    <thead>
-      <tr>
-        <th colspan="4">{% trans username=profile.username %}Users {{ username }} is following{% endtrans %}</th>
-      </tr>
-    </thead>
-    <tbody>
-      {% for row in items|batch(4, '') %}
-      <tr>
-        {% for user in row %}
-        <td class="span3 user-cell">
-          {% if user %}
-          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-avatar"><img src="{{ user.get_avatar(36) }}" alt=""></a>
-          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-name">{{ user.username }}</a>
-          {% else %}
-          &nbsp;
-          {% endif %}
-        </td>
-        {% endfor %}
-      </tr>
-      {% endfor %}
-    </tbody>
-  </table>
-  {{ pager() }}
-</div>
-{% endif %}
-{% endblock %}
-
-{% macro pager() -%}
-{% if pagination['total'] > 1 %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('user_follows', user=profile.id, username=profile.username_slug, page=pagination['prev']) }}{% else %}{{ url('user_follows', user=profile.id, username=profile.username_slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('user_follows', user=profile.id, username=profile.username_slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
-{%- endmacro %}
+{% extends "cranefly/profiles/profile.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(_('Follows'), profile.username) }}{% endblock %}
+
+{% block tab %}
+<h2>{% if items_total %}
+    {%- trans count=items_total, total=items_total|intcomma, username=profile.username -%}
+    {{ username }} is following one member
+    {%- pluralize -%}
+    {{ username }} is following {{ total }} members
+    {%- endtrans -%}
+    {%- else -%}
+    {% trans username=profile.username %}{{ username }} is not following anybody{% endtrans %}
+    {%- endif %}</h2>
+
+{% if items_total %}
+<div class="user-list user-follows">
+  <table class="table table-striped">
+    <thead>
+      <tr>
+        <th colspan="4">{% trans username=profile.username %}Users {{ username }} is following{% endtrans %}</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for row in items|batch(4, '') %}
+      <tr>
+        {% for user in row %}
+        <td class="span3 user-cell">
+          {% if user %}
+          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-avatar"><img src="{{ user.get_avatar(36) }}" alt=""></a>
+          <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-name">{{ user.username }}</a>
+          {% else %}
+          &nbsp;
+          {% endif %}
+        </td>
+        {% endfor %}
+      </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+  {{ pager() }}
+</div>
+{% endif %}
+{% endblock %}
+
+{% macro pager() -%}
+{% if pagination['total'] > 1 %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('user_follows', user=profile.id, username=profile.username_slug, page=pagination['prev']) }}{% else %}{{ url('user_follows', user=profile.id, username=profile.username_slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('user_follows', user=profile.id, username=profile.username_slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{%- endmacro %}

+ 113 - 113
templates/cranefly/profiles/list.html

@@ -1,114 +1,114 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{% if in_search -%}
-{{ macros.page_title(title=_('Search Users'), parent=_('Users List')) }}
-{% elif active_rank %}
-{{ macros.page_title(title=_(active_rank.name), parent=_('Users List')) }}
-{%- else -%}
-{{ macros.page_title(title=_('Users List')) }}
-{%- endif %}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <h1>{% trans %}Users List{% endtrans %} <small>{% trans %}Browse notable user groups or find specific user{% endtrans %}</small></h1>
-    <ul class="nav nav-tabs header-tabs">
-      {% for rank in ranks %}
-      <li{% if active_rank.id == rank.id %} class="active"{% endif %}><a href="{% if loop.first %}{{ url('users') }}{% else %}{{ url('users', slug=rank.slug) }}{% endif %}">{{ _(rank.name) }}</a></li>
-      {% endfor %}
-      {% if acl.users.can_search_users() and not user.is_crawler() %}
-      <li class="pull-right">
-        <form action="{{ url('users') }}" class="form-inline" method="post">
-          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-          {{ form_theme.field_widget(search_form.username, width=2, attrs={'placeholder': lang_find_user()}) }}
-          <button type="submit" class="btn btn-icon"><i class="icon-search"></i></button>
-        </form>
-      </li>
-      {% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="profiles-list">
-
-    {% if in_search %}<h2>{% trans %}Search Users{% endtrans %}</h2>{% endif %}
-
-    {% if message %}
-    <div class="messages-list">
-      {{ macros.draw_message(message, 'alert-form') }}
-    </div>
-    {% endif %}
-
-    {% if in_search and not message and users|length > 0 %}
-    <p class="lead">{% trans %}We couldn't find a member with name you entered, so we present you with some other members with names similiar to one you searched for in hopes that one of them will turn out to be member you are looking for.{% endtrans %}</p>
-    {% elif active_rank and active_rank.description %}
-    <div class="markdown lead">{{ active_rank.description }}</div>
-    {% endif %}
-
-    {% if users|length > 0 %}
-    <table class="table table-striped">
-      <thead>
-        <tr>
-          <th colspan="4">{% if in_search %}{% trans %}Found Users{% endtrans %}{% else %}{% trans %}Users in this group{% endtrans %}{% endif %}</th>
-        </tr>
-      </thead>
-      <tbody>
-        {% for row in users|batch(4, '') %}
-        <tr>
-          {% for user in row %}
-          <td class="span3 user-cell{% if user and user.get_style() %} user-cell-{{ user.get_style() }}{% endif %}">
-            {% if user %}
-            <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-avatar"><img src="{{ user.get_avatar(36) }}" alt=""></a>
-            <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-name">{{ user.username }}</a>
-            {% else %}
-            &nbsp;
-            {% endif %}
-          </td>
-          {% endfor %}
-        </tr>
-        {% endfor %}
-      </tbody>
-    </table>
-    {{ pager() }}
-    {% elif not message %}
-    <p class="lead">
-      {%- if in_search -%}
-      {% trans %}We couldn't find any members with specified name.{% endtrans %}
-      {%- else -%}
-      {% trans %}Looks like this group has no members...{% endtrans %}
-      {%- endif -%}
-    </p>
-    {% endif %}
-
-  </div>
-</div>
-{% endblock %}
-
-{% macro pager() -%}
-{% if pagination and pagination['total'] > 1 %}
-{% if default_rank %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('users', page=pagination['prev']) }}{% else %}{{ url('users') }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('users', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% else %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('users', slug=active_rank.slug, page=pagination['prev']) }}{% else %}{{ url('users', slug=active_rank.slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('users', slug=active_rank.slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
-{% endif %}
-{%- endmacro %}
-
-{# Language strings macros #}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{% if in_search -%}
+{{ macros.page_title(title=_('Search Users'), parent=_('Users List')) }}
+{% elif active_rank %}
+{{ macros.page_title(title=_(active_rank.name), parent=_('Users List')) }}
+{%- else -%}
+{{ macros.page_title(title=_('Users List')) }}
+{%- endif %}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% trans %}Users List{% endtrans %} <small>{% trans %}Browse notable user groups or find specific user{% endtrans %}</small></h1>
+    <ul class="nav nav-tabs header-tabs">
+      {% for rank in ranks %}
+      <li{% if active_rank.id == rank.id %} class="active"{% endif %}><a href="{% if loop.first %}{{ url('users') }}{% else %}{{ url('users', slug=rank.slug) }}{% endif %}">{{ _(rank.name) }}</a></li>
+      {% endfor %}
+      {% if acl.users.can_search_users() and not user.is_crawler() %}
+      <li class="pull-right">
+        <form action="{{ url('users') }}" class="form-inline" method="post">
+          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+          {{ form_theme.field_widget(search_form.username, width=2, attrs={'placeholder': lang_find_user()}) }}
+          <button type="submit" class="btn btn-icon"><i class="icon-search"></i></button>
+        </form>
+      </li>
+      {% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="profiles-list">
+
+    {% if in_search %}<h2>{% trans %}Search Users{% endtrans %}</h2>{% endif %}
+
+    {% if message %}
+    <div class="messages-list">
+      {{ macros.draw_message(message, 'alert-form') }}
+    </div>
+    {% endif %}
+
+    {% if in_search and not message and users|length > 0 %}
+    <p class="lead">{% trans %}We couldn't find a member with name you entered, so we present you with some other members with names similiar to one you searched for in hopes that one of them will turn out to be member you are looking for.{% endtrans %}</p>
+    {% elif active_rank and active_rank.description %}
+    <div class="markdown lead">{{ active_rank.description }}</div>
+    {% endif %}
+
+    {% if users|length > 0 %}
+    <table class="table table-striped">
+      <thead>
+        <tr>
+          <th colspan="4">{% if in_search %}{% trans %}Found Users{% endtrans %}{% else %}{% trans %}Users in this group{% endtrans %}{% endif %}</th>
+        </tr>
+      </thead>
+      <tbody>
+        {% for row in users|batch(4, '') %}
+        <tr>
+          {% for user in row %}
+          <td class="span3 user-cell{% if user and user.get_style() %} user-cell-{{ user.get_style() }}{% endif %}">
+            {% if user %}
+            <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-avatar"><img src="{{ user.get_avatar(36) }}" alt=""></a>
+            <a href="{{ url('user', username=user.username_slug, user=user.pk) }}" class="user-name">{{ user.username }}</a>
+            {% else %}
+            &nbsp;
+            {% endif %}
+          </td>
+          {% endfor %}
+        </tr>
+        {% endfor %}
+      </tbody>
+    </table>
+    {{ pager() }}
+    {% elif not message %}
+    <p class="lead">
+      {%- if in_search -%}
+      {% trans %}We couldn't find any members with specified name.{% endtrans %}
+      {%- else -%}
+      {% trans %}Looks like this group has no members...{% endtrans %}
+      {%- endif -%}
+    </p>
+    {% endif %}
+
+  </div>
+</div>
+{% endblock %}
+
+{% macro pager() -%}
+{% if pagination and pagination['total'] > 1 %}
+{% if default_rank %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('users', page=pagination['prev']) }}{% else %}{{ url('users') }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('users', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% else %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('users', slug=active_rank.slug, page=pagination['prev']) }}{% else %}{{ url('users', slug=active_rank.slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Previous Page{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('users', slug=active_rank.slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Next Page{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{% endif %}
+{%- endmacro %}
+
+{# Language strings macros #}
 {% macro lang_find_user() -%}{% trans %}Find User...{% endtrans %}{%- endmacro %}
 {% macro lang_find_user() -%}{% trans %}Find User...{% endtrans %}{%- endmacro %}

+ 70 - 70
templates/cranefly/profiles/posts.html

@@ -1,70 +1,70 @@
-{% extends "cranefly/profiles/profile.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(_('Posts'), profile.username) }}{% endblock %}
-
-{% block tab %}
-<div class="stat-header">
-  <div class="help-text">
-    {% trans user=profile.username %}Graph presents {{ user }}'s posting activity from last 100 days.{% endtrans %}
-  </div>
-  <div id="user-graph"></div>
-  <h2>{% if items_total -%}
-      {%- trans count=items_total, total=items_total|intcomma, username=profile.username -%}
-      {{ username }} has one post
-      {%- pluralize -%}
-      {{ username }} has {{ total }} posts
-      {%- endtrans -%}
-      {%- else -%}
-      {% trans username=profile.username %}{{ username }} has no posts{% endtrans %}
-      {%- endif %}</h2>
-</div>
-
-{% if items_total %}
-<div class="content-list user-posts">
-  {% for item in items %}
-  <div class="media">
-    <a href="{{ url('user', user=profile.pk, username=profile.username_slug) }}" class="pull-left">
-      <img class="media-object" src="{{ profile.get_avatar(52) }}">
-    </a>
-    <div class="media-body">
-      <a href="{{ url('thread_find', thread=item.thread.pk, slug=item.thread.slug, post=item.pk) }}" class="post-preview">{{ item.post_preparsed|markdown_short(300) }}</a>
-      <div class="media-footer">{% if item.thread.start_post_id == item.pk -%}
-      {% trans thread=thread(item), forum=forum(item.forum), date=item.date|reltimesince|low %}Thread {{ thread }} posted in {{ forum }} {{ date }}{% endtrans %}
-      {%- else -%}
-      {% trans thread=thread(item), forum=forum(item.forum), date=item.date|reltimesince|low %}Reply to {{ thread }} posted in {{ forum }} {{ date }}{% endtrans %}
-      {%- endif %}</div>
-    </div>
-  </div>
-  <hr>
-  {% endfor %}
-  {{ pager() }}
-</div>
-{% endif %}
-{% endblock %}
-
-{% macro pager() -%}
-{% if pagination['total'] > 1 %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('user_posts', user=profile.id, username=profile.username_slug) }}" class="tooltip-top" title="{% trans %}Lastest Posts{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}Latest{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('user_posts', user=profile.id, username=profile.username_slug, page=pagination['prev']) }}{% else %}{{ url('user_posts', user=profile.id, username=profile.username_slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('user_posts', user=profile.id, username=profile.username_slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
-{%- endmacro %}
-
-{% macro thread(item) -%}
-<a href="{{ url('thread_find', thread=item.thread.pk, slug=item.thread.slug, post=item.pk) }}">{{ item.thread.name }}</a>
-{%- endmacro %}
-
-{% macro forum(forum) -%}
-<a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a>
-{%- endmacro %}
-
-{% block javascripts %}
-{{ draw_graph() }}
-{% endblock %}
+{% extends "cranefly/profiles/profile.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(_('Posts'), profile.username) }}{% endblock %}
+
+{% block tab %}
+<div class="stat-header">
+  <div class="help-text">
+    {% trans user=profile.username %}Graph presents {{ user }}'s posting activity from last 100 days.{% endtrans %}
+  </div>
+  <div id="user-graph"></div>
+  <h2>{% if items_total -%}
+      {%- trans count=items_total, total=items_total|intcomma, username=profile.username -%}
+      {{ username }} has one post
+      {%- pluralize -%}
+      {{ username }} has {{ total }} posts
+      {%- endtrans -%}
+      {%- else -%}
+      {% trans username=profile.username %}{{ username }} has no posts{% endtrans %}
+      {%- endif %}</h2>
+</div>
+
+{% if items_total %}
+<div class="content-list user-posts">
+  {% for item in items %}
+  <div class="media">
+    <a href="{{ url('user', user=profile.pk, username=profile.username_slug) }}" class="pull-left">
+      <img class="media-object" src="{{ profile.get_avatar(52) }}">
+    </a>
+    <div class="media-body">
+      <a href="{{ url('thread_find', thread=item.thread.pk, slug=item.thread.slug, post=item.pk) }}" class="post-preview">{{ item.post_preparsed|markdown_short(300) }}</a>
+      <div class="media-footer">{% if item.thread.start_post_id == item.pk -%}
+      {% trans thread=thread(item), forum=forum(item.forum), date=item.date|reltimesince|low %}Thread {{ thread }} posted in {{ forum }} {{ date }}{% endtrans %}
+      {%- else -%}
+      {% trans thread=thread(item), forum=forum(item.forum), date=item.date|reltimesince|low %}Reply to {{ thread }} posted in {{ forum }} {{ date }}{% endtrans %}
+      {%- endif %}</div>
+    </div>
+  </div>
+  <hr>
+  {% endfor %}
+  {{ pager() }}
+</div>
+{% endif %}
+{% endblock %}
+
+{% macro pager() -%}
+{% if pagination['total'] > 1 %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('user_posts', user=profile.id, username=profile.username_slug) }}" class="tooltip-top" title="{% trans %}Lastest Posts{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}Latest{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('user_posts', user=profile.id, username=profile.username_slug, page=pagination['prev']) }}{% else %}{{ url('user_posts', user=profile.id, username=profile.username_slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('user_posts', user=profile.id, username=profile.username_slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{%- endmacro %}
+
+{% macro thread(item) -%}
+<a href="{{ url('thread_find', thread=item.thread.pk, slug=item.thread.slug, post=item.pk) }}">{{ item.thread.name }}</a>
+{%- endmacro %}
+
+{% macro forum(forum) -%}
+<a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a>
+{%- endmacro %}
+
+{% block javascripts %}
+{{ draw_graph() }}
+{% endblock %}

+ 108 - 108
templates/cranefly/profiles/profile.html

@@ -1,109 +1,109 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(profile.username) }}{% endblock %}
-
-{% block container %}
-<div class="user-profile{% if profile.get_style() %} user-profile-{{ profile.get_style() }}{% endif %}">
-  <div class="page-header header-primary user-profile-header">
-    <div class="container">
-      {{ messages_list(messages) }}
-      <div class="header-row">
-        {% if profile.id == user.id and not user.avatar_ban %}
-        <a href="{{ url('usercp_avatar') }}"><img src="{{ profile.get_avatar() }}" class="header-avatar tooltip-right" title="{% trans %}Click to jump to your Avatar Settings{% endtrans %}"></a>
-        {% else %}
-        <img src="{{ profile.get_avatar() }}" class="header-avatar">
-        {% endif %}
-        <div class="header-side">
-          <h1>{{ profile.username }} <small>{% if profile.title or profile.rank.title -%}
-            <strong>{% if profile.title %}{{ _(profile.title) }}{% elif profile.rank.title %}{{ _(profile.rank.title) }}{% endif %}</strong>; {% endif %}
-            {%- if online and (not hidden or acl.users.can_see_hidden_users()) -%}
-            {%- if profile.hide_activity -%}
-              {% trans %}Online, hidden{% endtrans %}
-            {%- else -%}
-              {% trans %}Online{% endtrans %}
-            {%- endif %}
-            {%- else -%}
-              {% trans %}Offline{% endtrans %}
-            {%- endif -%}, {% if not hidden or acl.users.can_see_hidden_users() -%}
-              {%- if online -%}
-                {% trans last_click=online.last|reltimesince|low %}last click {{ last_click }}{% endtrans %}
-              {%- elif profile.last_date -%}
-                {% trans last_visit=profile.last_date|reltimesince|low %}last seen {{ last_visit }}{% endtrans %}
-              {%- else -%}
-                {% trans %}never visited{% endtrans %}
-              {%- endif -%}
-            {%- else -%}
-              {% trans %}hiding activity{% endtrans %}
-            {%- endif %}</small></h1>
-          <ul class="nav nav-tabs header-tabs">
-            {% for link in tabs %}
-            <li{% if link.active %} class="active"{% endif %}>
-              <a href="{{ url(link.route, user=profile.pk, username=profile.username_slug) }}">{{ link.name }}</a>
-            </li>
-            {% endfor %}
-            {% if user.is_authenticated() and user.pk != profile.pk %}
-            <li class="pull-right">
-              <form class="form-inline" action="{% if ignores %}{{ url('unignore_user', user=profile.id) }}{% else %}{{ url('ignore_user', user=profile.id) }}{% endif %}" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <input type="hidden" name="fallback" value="{{ fallback }}">
-                <button type="submit" class="btn tooltip-top{% if ignores %} btn-inverse{% endif %}" title="{% if ignores %}{% trans user=profile.username %}Remove {{ user }} from ignored{% endtrans %}{% else %}{% trans user=profile.username %}Add {{ user }} to ignored{% endtrans %}{% endif %}">
-                  <i class="icon-ban-circle"></i>
-                </button>
-              </form>
-            </li>
-            <li class="pull-right">
-              <form class="form-inline" action="{% if follows %}{{ url('unfollow_user', user=profile.id) }}{% else %}{{ url('follow_user', user=profile.id) }}{% endif %}" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <input type="hidden" name="fallback" value="{{ fallback }}">
-                <button type="submit" class="btn tooltip-top{% if follows %} btn-success{% endif %}" title="{% if follows %}{% trans user=profile.username %}Stop following {{ user }}{% endtrans %}{% else %}{% trans user=profile.username %}Start following {{ user }}{% endtrans %}{% endif %}">
-                  <i class="icon-heart"></i>
-                </button>
-              </form>
-            </li>
-            {% if acl.private_threads.can_start() %}
-            <li class="pull-right">
-              <a href="{{ url('private_thread_start_with', username=profile.username_slug, user=profile.pk) }}" class="btn tooltip-top" title="{% trans user=profile.username %}Start private thread with {{ user }}{% endtrans %}"><i class="icon-envelope"></i></a>
-            </li>
-            {% endif %}
-            {% endif %}
-          </ul>
-        </div>
-      </div>
-    </div>
-  </div>
-  <div class="container container-primary">
-    {% block tab %}{% endblock %}
-  </div>
-</div>
-{% endblock %}
-
-{% macro draw_graph() %}
-    <script src="{{ STATIC_URL }}cranefly/js/d3.min.js"></script>
-    <script type="text/javascript">
-      var data = [{{ (',').join(graph) }}];
-      var max = {{ graph_max }};
-
-      var barWidth = 8;
-      var width = (barWidth + 2) * data.length;
-      var height = 40;
-
-      // add the canvas to the DOM
-      var barDemo = d3.select("#user-graph")
-        .append("svg")
-        .attr("preserveAspectRatio", 'none')
-        .attr("viewBox", "0 0 " + width + " " + height);
-
-      barDemo.selectAll("rect")
-        .data(data)
-        .enter()
-        .append("rect")
-        .attr("shape-rendering", 'crispEdges')
-        .attr("x", function(datum, index) { return (2 + barWidth) * index; })
-        .attr("y", function(datum) { return height - ((datum / max) * height) - 2; })
-        .attr("height", function(datum) { return ((datum / max) * (height)) + 2; })
-        .attr("width", barWidth)
-        .attr("fill", "#DDD");
-
-    </script>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(profile.username) }}{% endblock %}
+
+{% block container %}
+<div class="user-profile{% if profile.get_style() %} user-profile-{{ profile.get_style() }}{% endif %}">
+  <div class="page-header header-primary user-profile-header">
+    <div class="container">
+      {{ messages_list(messages) }}
+      <div class="header-row">
+        {% if profile.id == user.id and not user.avatar_ban %}
+        <a href="{{ url('usercp_avatar') }}"><img src="{{ profile.get_avatar() }}" class="header-avatar tooltip-right" title="{% trans %}Click to jump to your Avatar Settings{% endtrans %}"></a>
+        {% else %}
+        <img src="{{ profile.get_avatar() }}" class="header-avatar">
+        {% endif %}
+        <div class="header-side">
+          <h1>{{ profile.username }} <small>{% if profile.title or profile.rank.title -%}
+            <strong>{% if profile.title %}{{ _(profile.title) }}{% elif profile.rank.title %}{{ _(profile.rank.title) }}{% endif %}</strong>; {% endif %}
+            {%- if online and (not hidden or acl.users.can_see_hidden_users()) -%}
+            {%- if profile.hide_activity -%}
+              {% trans %}Online, hidden{% endtrans %}
+            {%- else -%}
+              {% trans %}Online{% endtrans %}
+            {%- endif %}
+            {%- else -%}
+              {% trans %}Offline{% endtrans %}
+            {%- endif -%}, {% if not hidden or acl.users.can_see_hidden_users() -%}
+              {%- if online -%}
+                {% trans last_click=online.last|reltimesince|low %}last click {{ last_click }}{% endtrans %}
+              {%- elif profile.last_date -%}
+                {% trans last_visit=profile.last_date|reltimesince|low %}last seen {{ last_visit }}{% endtrans %}
+              {%- else -%}
+                {% trans %}never visited{% endtrans %}
+              {%- endif -%}
+            {%- else -%}
+              {% trans %}hiding activity{% endtrans %}
+            {%- endif %}</small></h1>
+          <ul class="nav nav-tabs header-tabs">
+            {% for link in tabs %}
+            <li{% if link.active %} class="active"{% endif %}>
+              <a href="{{ url(link.route, user=profile.pk, username=profile.username_slug) }}">{{ link.name }}</a>
+            </li>
+            {% endfor %}
+            {% if user.is_authenticated() and user.pk != profile.pk %}
+            <li class="pull-right">
+              <form class="form-inline" action="{% if ignores %}{{ url('unignore_user', user=profile.id) }}{% else %}{{ url('ignore_user', user=profile.id) }}{% endif %}" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <input type="hidden" name="fallback" value="{{ fallback }}">
+                <button type="submit" class="btn tooltip-top{% if ignores %} btn-inverse{% endif %}" title="{% if ignores %}{% trans user=profile.username %}Remove {{ user }} from ignored{% endtrans %}{% else %}{% trans user=profile.username %}Add {{ user }} to ignored{% endtrans %}{% endif %}">
+                  <i class="icon-ban-circle"></i>
+                </button>
+              </form>
+            </li>
+            <li class="pull-right">
+              <form class="form-inline" action="{% if follows %}{{ url('unfollow_user', user=profile.id) }}{% else %}{{ url('follow_user', user=profile.id) }}{% endif %}" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <input type="hidden" name="fallback" value="{{ fallback }}">
+                <button type="submit" class="btn tooltip-top{% if follows %} btn-success{% endif %}" title="{% if follows %}{% trans user=profile.username %}Stop following {{ user }}{% endtrans %}{% else %}{% trans user=profile.username %}Start following {{ user }}{% endtrans %}{% endif %}">
+                  <i class="icon-heart"></i>
+                </button>
+              </form>
+            </li>
+            {% if acl.private_threads.can_start() %}
+            <li class="pull-right">
+              <a href="{{ url('private_thread_start_with', username=profile.username_slug, user=profile.pk) }}" class="btn tooltip-top" title="{% trans user=profile.username %}Start private thread with {{ user }}{% endtrans %}"><i class="icon-envelope"></i></a>
+            </li>
+            {% endif %}
+            {% endif %}
+          </ul>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="container container-primary">
+    {% block tab %}{% endblock %}
+  </div>
+</div>
+{% endblock %}
+
+{% macro draw_graph() %}
+    <script src="{{ STATIC_URL }}cranefly/js/d3.min.js"></script>
+    <script type="text/javascript">
+      var data = [{{ (',').join(graph) }}];
+      var max = {{ graph_max }};
+
+      var barWidth = 8;
+      var width = (barWidth + 2) * data.length;
+      var height = 40;
+
+      // add the canvas to the DOM
+      var barDemo = d3.select("#user-graph")
+        .append("svg")
+        .attr("preserveAspectRatio", 'none')
+        .attr("viewBox", "0 0 " + width + " " + height);
+
+      barDemo.selectAll("rect")
+        .data(data)
+        .enter()
+        .append("rect")
+        .attr("shape-rendering", 'crispEdges')
+        .attr("x", function(datum, index) { return (2 + barWidth) * index; })
+        .attr("y", function(datum) { return height - ((datum / max) * height) - 2; })
+        .attr("height", function(datum) { return ((datum / max) * (height)) + 2; })
+        .attr("width", barWidth)
+        .attr("fill", "#DDD");
+
+    </script>
 {% endmacro %}
 {% endmacro %}

+ 66 - 66
templates/cranefly/profiles/threads.html

@@ -1,66 +1,66 @@
-{% extends "cranefly/profiles/profile.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(_('Threads'), profile.username) }}{% endblock %}
-
-{% block tab %}
-<div class="stat-header">
-  <div class="help-text">
-    {% trans user=profile.username %}Graph presents threads started by {{ user }}'s during last 100 days.{% endtrans %}
-  </div>
-  <div id="user-graph"></div>
-  <h2>{% if items_total -%}
-      {%- trans count=items_total, total=items_total|intcomma, username=profile.username -%}
-      {{ username }} started one thread
-      {%- pluralize -%}
-      {{ username }} started {{ total }} threads
-      {%- endtrans -%}
-      {%- else -%}
-      {% trans username=profile.username %}{{ username }} started no threads{% endtrans %}
-      {%- endif %}</h2>
-</div>
-
-{% if items_total %}
-<div class="content-list user-threads">
-  {% for item in items %}
-  <div class="media">
-    <a href="{{ url('user', user=profile.pk, username=profile.username_slug) }}" class="pull-left">
-      <img class="media-object" src="{{ profile.get_avatar(52) }}">
-    </a>
-    <div class="media-body">
-      <a href="{{ url('thread', thread=item.pk, slug=item.slug) }}" class="post-preview">{{ item.start_post.post_preparsed|markdown_short(300) }}</a>
-      <div class="media-footer">{% trans thread=thread(item), forum=forum(item.forum), date=item.start|reltimesince|low %}Thread {{ thread }} posted in {{ forum }} {{ date }}{% endtrans %}</div>
-    </div>
-  </div>
-  <hr>
-  {% endfor %}
-  {{ pager() }}
-</div>
-{% endif %}
-{% endblock %}
-
-{% macro pager() -%}
-{% if pagination['total'] > 1 %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('user_threads', user=profile.id, username=profile.username_slug) }}" class="tooltip-top" title="{% trans %}Lastest Posts{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}Latest{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('user_threads', user=profile.id, username=profile.username_slug, page=pagination['prev']) }}{% else %}{{ url('user_threads', user=profile.id, username=profile.username_slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('user_threads', user=profile.id, username=profile.username_slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
-{%- endmacro %}
-
-{% block javascripts %}
-{{ draw_graph() }}
-{% endblock %}
-
-{% macro thread(item) -%}
-<a href="{{ url('thread', thread=item.pk, slug=item.slug) }}">{{ item.name }}</a>
-{%- endmacro %}
-
-{% macro forum(forum) -%}
-<a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a>
-{%- endmacro %}
+{% extends "cranefly/profiles/profile.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(_('Threads'), profile.username) }}{% endblock %}
+
+{% block tab %}
+<div class="stat-header">
+  <div class="help-text">
+    {% trans user=profile.username %}Graph presents threads started by {{ user }}'s during last 100 days.{% endtrans %}
+  </div>
+  <div id="user-graph"></div>
+  <h2>{% if items_total -%}
+      {%- trans count=items_total, total=items_total|intcomma, username=profile.username -%}
+      {{ username }} started one thread
+      {%- pluralize -%}
+      {{ username }} started {{ total }} threads
+      {%- endtrans -%}
+      {%- else -%}
+      {% trans username=profile.username %}{{ username }} started no threads{% endtrans %}
+      {%- endif %}</h2>
+</div>
+
+{% if items_total %}
+<div class="content-list user-threads">
+  {% for item in items %}
+  <div class="media">
+    <a href="{{ url('user', user=profile.pk, username=profile.username_slug) }}" class="pull-left">
+      <img class="media-object" src="{{ profile.get_avatar(52) }}">
+    </a>
+    <div class="media-body">
+      <a href="{{ url('thread', thread=item.pk, slug=item.slug) }}" class="post-preview">{{ item.start_post.post_preparsed|markdown_short(300) }}</a>
+      <div class="media-footer">{% trans thread=thread(item), forum=forum(item.forum), date=item.start|reltimesince|low %}Thread {{ thread }} posted in {{ forum }} {{ date }}{% endtrans %}</div>
+    </div>
+  </div>
+  <hr>
+  {% endfor %}
+  {{ pager() }}
+</div>
+{% endif %}
+{% endblock %}
+
+{% macro pager() -%}
+{% if pagination['total'] > 1 %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('user_threads', user=profile.id, username=profile.username_slug) }}" class="tooltip-top" title="{% trans %}Lastest Posts{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}Latest{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('user_threads', user=profile.id, username=profile.username_slug, page=pagination['prev']) }}{% else %}{{ url('user_threads', user=profile.id, username=profile.username_slug) }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('user_threads', user=profile.id, username=profile.username_slug, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{%- endmacro %}
+
+{% block javascripts %}
+{{ draw_graph() }}
+{% endblock %}
+
+{% macro thread(item) -%}
+<a href="{{ url('thread', thread=item.pk, slug=item.slug) }}">{{ item.name }}</a>
+{%- endmacro %}
+
+{% macro forum(forum) -%}
+<a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a>
+{%- endmacro %}

+ 62 - 62
templates/cranefly/register.html

@@ -1,63 +1,63 @@
-{% extends "cranefly/layout.html" %}
-{% import "forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Register new account')) }}{% endblock %}
-
-{% block content %}
-<div class="row">
-  <div class="span8 offset2">
-    <div class="form-container">
-
-      <div class="form-header">
-        <h1>{% trans %}Register new account{% endtrans %}</h1>
-      </div>
-
-      {% if message %}
-      <div class="messages-list">
-        {{ macros.draw_message(message) }}
-      </div>
-      {% endif %}
-
-      <form action="{{ url('register') }}" method="post">
-        {{ form_theme.hidden_fields(form) }}
-        <div class="form-fields">
-          <fieldset class="first">
-            {{ form_theme.row(form.username, attrs={'class': 'span8'}) }}
-          </fieldset>
-          <fieldset>
-            {{ form_theme.repeat(form,
-                                 (form.email, form.email_rep),
-                                 attrs=(
-                                        {'placeholder': _("Enter your e-mail"), 'class': 'span4'},
-                                        {'placeholder': _("Repeat your e-mail"), 'class': 'span4'}
-                                       )) }}
-          </fieldset>
-          <fieldset>
-            {{ form_theme.repeat(form,
-                                 (form.password, form.password_rep),
-                                 attrs=(
-                                        {'placeholder': _("Enter your password"), 'class': 'span4'},
-                                        {'placeholder': _("Repeat your password"), 'class': 'span4'}
-                                       )) }}
-          </fieldset>
-          {% if form.has_captcha %}
-          <fieldset{% if 'accept_tos' not in form.fields %} class="last"{% endif %}>
-            {{ form_theme.captcha(form, 8) }}
-          </fieldset>
-          {% endif %}
-          {% if 'accept_tos' in form.fields %}
-          <fieldset class="last">
-            {{ form_theme.row(form.accept_tos) }}
-          </fieldset>
-          {% endif %}
-        </div>
-        <div class="form-actions">
-          <button type="submit" class="btn btn-primary">{% trans %}Register account{% endtrans %}</button>
-        </div>
-      </form>
-
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Register new account')) }}{% endblock %}
+
+{% block content %}
+<div class="row">
+  <div class="span8 offset2">
+    <div class="form-container">
+
+      <div class="form-header">
+        <h1>{% trans %}Register new account{% endtrans %}</h1>
+      </div>
+
+      {% if message %}
+      <div class="messages-list">
+        {{ macros.draw_message(message) }}
+      </div>
+      {% endif %}
+
+      <form action="{{ url('register') }}" method="post">
+        {{ form_theme.hidden_fields(form) }}
+        <div class="form-fields">
+          <fieldset class="first">
+            {{ form_theme.row(form.username, attrs={'class': 'span8'}) }}
+          </fieldset>
+          <fieldset>
+            {{ form_theme.repeat(form,
+                                 (form.email, form.email_rep),
+                                 attrs=(
+                                        {'placeholder': _("Enter your e-mail"), 'class': 'span4'},
+                                        {'placeholder': _("Repeat your e-mail"), 'class': 'span4'}
+                                       )) }}
+          </fieldset>
+          <fieldset>
+            {{ form_theme.repeat(form,
+                                 (form.password, form.password_rep),
+                                 attrs=(
+                                        {'placeholder': _("Enter your password"), 'class': 'span4'},
+                                        {'placeholder': _("Repeat your password"), 'class': 'span4'}
+                                       )) }}
+          </fieldset>
+          {% if form.has_captcha %}
+          <fieldset{% if 'accept_tos' not in form.fields %} class="last"{% endif %}>
+            {{ form_theme.captcha(form, 8) }}
+          </fieldset>
+          {% endif %}
+          {% if 'accept_tos' in form.fields %}
+          <fieldset class="last">
+            {{ form_theme.row(form.accept_tos) }}
+          </fieldset>
+          {% endif %}
+        </div>
+        <div class="form-actions">
+          <button type="submit" class="btn btn-primary">{% trans %}Register account{% endtrans %}</button>
+        </div>
+      </form>
+
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 80 - 80
templates/cranefly/reports/changelog.html

@@ -1,81 +1,81 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %} <small>{{ thread.name }}</small></h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
-      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="post-changelog">
-    {% if edits %}
-    <table class="table table-striped">
-      <thead>
-        <tr>
-          <th style="width: 1%;">&nbsp;</th>
-          <th>{% trans %}Change Log{% endtrans %}</th>
-        </tr>
-      </thead>
-      <tbody>
-        {% for edit in edits %}
-        <tr>
-          <td>
-            <span class="change-{% if edit.change > 0 %}added{% elif edit.change < 0 %}removed{% else %}none{% endif %}{% if not edit.reason %} change-small{% endif %}">
-              {% if edit.change > 0 %}+{% endif %}{{ edit.change }}
-            </span>
-          </td>
-          <td>
-            <a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}" class="change-no">#{{ loop.revindex }}</a>
-            {% if edit.reason %}
-            <div class="change-reason">
-              <a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">{{ edit.reason }}</a>
-            </div>{% endif %}
-            <div class="change-description">
-              <a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">
-              {% if edit.change != 0 %}{% if edit.change > 0 -%}
-              {% trans chars=edit.change %}Added one character to post.{% pluralize %}Added {{ chars }} characters to post.{% endtrans %}
-              {%- elif edit.change < 0 -%}
-              {% trans chars=edit.change|abs %}Removed one character from post.{% pluralize %}Removed {{ chars }} characters from post.{% endtrans %}
-              {%- else -%}
-              {% trans %}No change in message's length.{% endtrans %}
-              {%- endif %}{% endif %}{% if edit.thread_name_old %} {% trans old=edit.thread_name_old, new=edit.thread_name_new %}Changed thread name from "{{ old }}" to "{{ new }}".{% endtrans %}{% endif %}</a>
-              <span class="change-details">
-                {% trans user=edit_user(edit), date=edit.date|reldate|low %}By {{ user }} {{ date }}{% endtrans %}
-              </span>
-            </div>
-          </td>
-        </tr>
-        {% endfor %}
-      </tbody>
-    </table>
-    {% else %}
-    <p class="lead">{% trans %}This post was never edited.{% endtrans %}</p>
-    {% endif %}
-  </div>
-</div>
-{% endblock %}
-
-
-{% macro edit_user(edit) -%}
-{% if edit.user_id %}<a href="{{ url('user', user=edit.user_id, username=edit.user_slug) }}">{{ edit.user_name }}</a>{% else %}{{ edit.user_name }}{% endif %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %} <small>{{ thread.name }}</small></h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
+      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="post-changelog">
+    {% if edits %}
+    <table class="table table-striped">
+      <thead>
+        <tr>
+          <th style="width: 1%;">&nbsp;</th>
+          <th>{% trans %}Change Log{% endtrans %}</th>
+        </tr>
+      </thead>
+      <tbody>
+        {% for edit in edits %}
+        <tr>
+          <td>
+            <span class="change-{% if edit.change > 0 %}added{% elif edit.change < 0 %}removed{% else %}none{% endif %}{% if not edit.reason %} change-small{% endif %}">
+              {% if edit.change > 0 %}+{% endif %}{{ edit.change }}
+            </span>
+          </td>
+          <td>
+            <a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}" class="change-no">#{{ loop.revindex }}</a>
+            {% if edit.reason %}
+            <div class="change-reason">
+              <a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">{{ edit.reason }}</a>
+            </div>{% endif %}
+            <div class="change-description">
+              <a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">
+              {% if edit.change != 0 %}{% if edit.change > 0 -%}
+              {% trans chars=edit.change %}Added one character to post.{% pluralize %}Added {{ chars }} characters to post.{% endtrans %}
+              {%- elif edit.change < 0 -%}
+              {% trans chars=edit.change|abs %}Removed one character from post.{% pluralize %}Removed {{ chars }} characters from post.{% endtrans %}
+              {%- else -%}
+              {% trans %}No change in message's length.{% endtrans %}
+              {%- endif %}{% endif %}{% if edit.thread_name_old %} {% trans old=edit.thread_name_old, new=edit.thread_name_new %}Changed thread name from "{{ old }}" to "{{ new }}".{% endtrans %}{% endif %}</a>
+              <span class="change-details">
+                {% trans user=edit_user(edit), date=edit.date|reldate|low %}By {{ user }} {{ date }}{% endtrans %}
+              </span>
+            </div>
+          </td>
+        </tr>
+        {% endfor %}
+      </tbody>
+    </table>
+    {% else %}
+    <p class="lead">{% trans %}This post was never edited.{% endtrans %}</p>
+    {% endif %}
+  </div>
+</div>
+{% endblock %}
+
+
+{% macro edit_user(edit) -%}
+{% if edit.user_id %}<a href="{{ url('user', user=edit.user_id, username=edit.user_slug) }}">{{ edit.user_name }}</a>{% else %}{{ edit.user_name }}{% endif %}
 {%- endmacro %}
 {%- endmacro %}

+ 95 - 95
templates/cranefly/reports/changelog_diff.html

@@ -1,95 +1,95 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('report_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %} <small>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</small></h1>
-    <ul class="unstyled header-stats pull-left">
-      <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if change.user_id %}<a href="{{ url('user', user=change.user_id, username=change.user_slug) }}">{{ change.user_name }}</a>{% else %}{{ change.user_name }}{% endif %}</li>
-      {% if acl.users.can_see_users_trails() %}
-      <li><i class="icon-globe"></i> {{ change.ip }}</li>
-      <li><i class="icon-qrcode"></i> {{ change.agent }}</li>
-      {% endif %}
-      {% if change.change != 0 %}<li><i class="icon-{% if change.change > 0 %}plus{% elif change.change < 0 %}minus{% endif %}"></i> {% if change.change > 0 -%}
-      {% trans chars=change.change %}Added one character{% pluralize %}Added {{ chars }} characters{% endtrans %}
-      {%- elif change.change < 0 -%}
-      {% trans chars=change.change|abs %}Removed one character{% pluralize %}Removed {{ chars }} characters{% endtrans %}
-      {%- endif %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="post-diff">
-    {% if message %}
-    <div class="messages-list">
-      {{ macros.draw_message(message) }}
-    </div>
-    {% endif %}
-
-    {% if change.reason %}
-    <p class="lead">{{ change.reason }}</p>
-    {% endif %}
-
-    {% if acl.threads.can_edit_reply(user, forum, thread, post) or prev or next %}
-    <div class="diff-extra">
-      {{ pager() }}
-      {% if user.is_authenticated() and acl.threads.can_make_revert(forum, thread) %}
-      <form class="form-inline pull-right" action="{{ url('report_changelog_revert', thread=thread.pk, slug=thread.slug, post=post.pk, change=change.pk) }}" method="post">
-        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-        <button type="submit" class="btn btn-danger">{% trans %}Revert this edit{% endtrans %}</button></li>
-      </form>
-      {%- endif %}
-    </div>
-    {% endif %}
-
-    <div class="post-diff-details">
-      <table>
-        <tbody>
-          {% for line in diff %}{% if line[0] != "?" %}
-          <tr>
-            <td class="line"><a href="#{{ l }}">{{ l }}.</a></td>
-            <td class="{% if line[0] == '+' %}added{% elif line[0] == '-' %}removed{% else %}stag{% endif %}{% if l is even %} even{% endif %}">{% if line[2:] %}{{ line[2:] }}{% else %}&nbsp;{% endif %}</td>
-          </tr>
-          {% set l = l + 1 %}
-          {% endif %}{% endfor %}
-        </tbody>
-      </table>
-    </div>
-
-    {% if prev or next %}
-    <div class="diff-extra">
-      {{ pager() }}
-    </div>
-    {% endif %}
-
-  </div>
-</div>
-{% endblock %}
-
-
-{% macro pager() %}
-{% if prev or prev %}
-<div class="pagination pull-left">
-  <ul>
-    {% if prev %}<li><a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=prev.pk) }}"><i class="icon-chevron-left"></i> {{ prev.date|reldate }}</a></li>{% endif %}
-    {% if next %}<li><a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=next.pk) }}">{{ next.date|reldate }} <i class="icon-chevron-right"></i></a></li>{% endif %}
-  </ul>
-</div>
-{% endif %}
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('report_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %} <small>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</small></h1>
+    <ul class="unstyled header-stats pull-left">
+      <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if change.user_id %}<a href="{{ url('user', user=change.user_id, username=change.user_slug) }}">{{ change.user_name }}</a>{% else %}{{ change.user_name }}{% endif %}</li>
+      {% if acl.users.can_see_users_trails() %}
+      <li><i class="icon-globe"></i> {{ change.ip }}</li>
+      <li><i class="icon-qrcode"></i> {{ change.agent }}</li>
+      {% endif %}
+      {% if change.change != 0 %}<li><i class="icon-{% if change.change > 0 %}plus{% elif change.change < 0 %}minus{% endif %}"></i> {% if change.change > 0 -%}
+      {% trans chars=change.change %}Added one character{% pluralize %}Added {{ chars }} characters{% endtrans %}
+      {%- elif change.change < 0 -%}
+      {% trans chars=change.change|abs %}Removed one character{% pluralize %}Removed {{ chars }} characters{% endtrans %}
+      {%- endif %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="post-diff">
+    {% if message %}
+    <div class="messages-list">
+      {{ macros.draw_message(message) }}
+    </div>
+    {% endif %}
+
+    {% if change.reason %}
+    <p class="lead">{{ change.reason }}</p>
+    {% endif %}
+
+    {% if acl.threads.can_edit_reply(user, forum, thread, post) or prev or next %}
+    <div class="diff-extra">
+      {{ pager() }}
+      {% if user.is_authenticated() and acl.threads.can_make_revert(forum, thread) %}
+      <form class="form-inline pull-right" action="{{ url('report_changelog_revert', thread=thread.pk, slug=thread.slug, post=post.pk, change=change.pk) }}" method="post">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        <button type="submit" class="btn btn-danger">{% trans %}Revert this edit{% endtrans %}</button></li>
+      </form>
+      {%- endif %}
+    </div>
+    {% endif %}
+
+    <div class="post-diff-details">
+      <table>
+        <tbody>
+          {% for line in diff %}{% if line[0] != "?" %}
+          <tr>
+            <td class="line"><a href="#{{ l }}">{{ l }}.</a></td>
+            <td class="{% if line[0] == '+' %}added{% elif line[0] == '-' %}removed{% else %}stag{% endif %}{% if l is even %} even{% endif %}">{% if line[2:] %}{{ line[2:] }}{% else %}&nbsp;{% endif %}</td>
+          </tr>
+          {% set l = l + 1 %}
+          {% endif %}{% endfor %}
+        </tbody>
+      </table>
+    </div>
+
+    {% if prev or next %}
+    <div class="diff-extra">
+      {{ pager() }}
+    </div>
+    {% endif %}
+
+  </div>
+</div>
+{% endblock %}
+
+
+{% macro pager() %}
+{% if prev or prev %}
+<div class="pagination pull-left">
+  <ul>
+    {% if prev %}<li><a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=prev.pk) }}"><i class="icon-chevron-left"></i> {{ prev.date|reldate }}</a></li>{% endif %}
+    {% if next %}<li><a href="{{ url('report_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=next.pk) }}">{{ next.date|reldate }} <i class="icon-chevron-right"></i></a></li>{% endif %}
+  </ul>
+</div>
+{% endif %}
+{% endmacro %}

+ 34 - 34
templates/cranefly/reports/details.html

@@ -1,35 +1,35 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Info") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %} <small>{{ thread.name }}</small></h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
-      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <h2>{% trans %}IP Address{% endtrans %}</h2>
-  <p class="lead">{{ post.ip }}</p>
-  <h2>{% trans %}UserAgent{% endtrans %}</h2>
-  <p class="lead">{{ post.agent }}</p>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Info") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %} <small>{{ thread.name }}</small></h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
+      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <h2>{% trans %}IP Address{% endtrans %}</h2>
+  <p class="lead">{{ post.ip }}</p>
+  <h2>{% trans %}UserAgent{% endtrans %}</h2>
+  <p class="lead">{{ post.agent }}</p>
+</div>
 {% endblock %}
 {% endblock %}

+ 183 - 183
templates/cranefly/reports/list.html

@@ -1,183 +1,183 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Reported Posts'),page=pagination['page']) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans %}Reported Posts{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans %}Reported Posts{% endtrans %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <div class="forum-threads-extra extra-top">
-    {{ pager() }}
-  </div>
-
-  <div class="forum-threads-list reports-list">
-    <div class="header">
-      <div class="row-fluid">
-        <div class="span6 offset2">{% trans %}Report{% endtrans %}</div>
-        <div class="span4 thread-activity">
-          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
-          {% if list_form %}
-          <div class="pull-right check-cell">
-            <label class="checkbox"><input type="checkbox" class="checkbox-master"></label>
-          </div>
-          {% endif %}
-        </div>
-      </div>
-    </div>
-    {% for thread in threads %}
-    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
-      <div class="row-fluid">
-        <div class="span2 thread-label">
-          {% if thread.weight == 2 %}
-          <span class="report-label report-open"><i class="icon-time"></i> {% trans %}Open{% endtrans %}</span>
-          {% elif thread.weight == 1 %}
-          <span class="report-label report-resolved"><i class="icon-ok"></i> {% trans %}Resolved{% endtrans %}</span>
-          {% else %}
-          <span class="report-label report-bogus"><i class="icon-remove"></i> {% trans %}Bogus{% endtrans %}</span>
-          {% endif %}
-        </div>
-        <div class="span6">
-
-          {% if thread.deleted %}
-          <ul class="unstyled thread-flags">
-            <li class="flag-deleted"><i class="icon-trash tooltip-top" title="{% trans %}This thread is deleted{% endtrans %}"></i></li>
-          </ul>
-          {% endif %}
-          
-          <a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}" class="thread-name"><span class="report-id">#{{ thread.pk }}</span> {{ thread.name }}</a>
-          
-          {% if thread.is_read %}
-          <a href="{{ url('report_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last comment{% endtrans %}"><i class="icon-asterisk"></i></a>
-          {% else %}
-          <a href="{{ url('report_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread comment{% endtrans %}"><i class="icon-fire"></i></a>
-          {% endif %}
-
-          <div class="thread-details">
-            {% if thread.report_forum %}
-            {% trans user=thread_starter(thread), start=thread.start|reldate|low, forum=report_forum(thread) %}Post reported by {{ user }} {{ start }} in forum {{ forum }}{% endtrans %}
-            {% else %}
-            {% trans user=thread_starter(thread), start=thread.start|reldate|low %}Deleted post reported by {{ user }} {{ start }}{% endtrans %}
-            {% endif %}
-          </div>
-
-        </div>
-        <div class="span4 thread-activity">
-
-          <div class="thread-replies">
-            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
-            {{ replies(thread.replies) }}
-          </div>
-
-          {% if list_form %}
-          <label class="thread-select checkbox"><input form="threads_form" name="{{ list_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ thread.pk }}"{% if list_form['list_items']['has_value'] and ('' ~ thread.pk) in list_form['list_items']['value'] %} checked="checked"{% endif %}></label>
-          {% endif %}
-        </div>
-      </div>
-    </div>
-    {% else %}
-    <div class="thread-row threads-list-empty">
-      {% trans %}There are no reports currently. Whef!{% endtrans %}
-    </div>
-    {% endfor %}
-
-    {% if list_form %}
-    <div class="threads-actions">
-      <form id="threads_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
-        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-        {{ form_theme.input_select(list_form['list_action'],width=3) }}
-        <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-      </form>
-    </div>
-    {% endif %}
-  </div>
-
-  <div class="forum-threads-extra">
-    {{ pager() }}
-  </div>
-
-</div>
-{% endblock %}
-
-{% macro replies(thread_replies) -%}
-{% trans count=thread_replies, replies=thread_replies|intcomma -%}
-{{ replies }} reply
-{%- pluralize -%}
-{{ replies }} replies
-{%- endtrans %}
-{%- endmacro %}
-
-{% macro thread_starter(thread) -%}
-{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro thread_reply(thread) -%}
-{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro report_forum(thread) -%}
-<a href="{{ thread.report_forum.url }}" class="forum-link">{{ thread.report_forum }}</a>
-{%- endmacro %}
-
-{% macro pager() %}
-{% if pagination['total'] > 0 %}
-<div class="pagination pull-left">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('forum', slug=forum.slug, forum=forum.id) }}" class="tooltip-top" title="{% trans %}First Page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('forum', slug=forum.slug, forum=forum.id, page=pagination['prev']) }}{% else %}{{ url('forum', slug=forum.slug, forum=forum.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Newest Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('forum', slug=forum.slug, forum=forum.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
-{% endmacro %}
-
-{% block javascripts -%}{{ super() }}
-  <script type="text/javascript">
-    $(function () {
-      function populateForumTooltip(target) {
-        return $('#forum-' + target + ' .forum-meta').html();
-      };
-      {% for subforum in forum.subforums %}
-        $('#forum-{{ subforum.id }} .forum-title').tooltip({
-          template: '<div class="tooltip forum-meta-tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
-          placement: 'right',
-          html: true,
-          title: populateForumTooltip({{ subforum.id }})
-        });
-      {% endfor %}
-      {%- if list_form %}
-      $('#threads_form').submit(function() {
-        if ($('.thread-select[]:checked').length == 0) {
-          alert("{% trans %}You have to select at least one thread.{% endtrans %}");
-          return false;
-        }
-        if ($('#id_list_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete selected reports? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });{% endif %}
-    });
-  </script>
-{%- endblock %}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Reported Posts'),page=pagination['page']) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans %}Reported Posts{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans %}Reported Posts{% endtrans %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="forum-threads-extra extra-top">
+    {{ pager() }}
+  </div>
+
+  <div class="forum-threads-list reports-list">
+    <div class="header">
+      <div class="row-fluid">
+        <div class="span6 offset2">{% trans %}Report{% endtrans %}</div>
+        <div class="span4 thread-activity">
+          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
+          {% if list_form %}
+          <div class="pull-right check-cell">
+            <label class="checkbox"><input type="checkbox" class="checkbox-master"></label>
+          </div>
+          {% endif %}
+        </div>
+      </div>
+    </div>
+    {% for thread in threads %}
+    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
+      <div class="row-fluid">
+        <div class="span2 thread-label">
+          {% if thread.weight == 2 %}
+          <span class="report-label report-open"><i class="icon-time"></i> {% trans %}Open{% endtrans %}</span>
+          {% elif thread.weight == 1 %}
+          <span class="report-label report-resolved"><i class="icon-ok"></i> {% trans %}Resolved{% endtrans %}</span>
+          {% else %}
+          <span class="report-label report-bogus"><i class="icon-remove"></i> {% trans %}Bogus{% endtrans %}</span>
+          {% endif %}
+        </div>
+        <div class="span6">
+
+          {% if thread.deleted %}
+          <ul class="unstyled thread-flags">
+            <li class="flag-deleted"><i class="icon-trash tooltip-top" title="{% trans %}This thread is deleted{% endtrans %}"></i></li>
+          </ul>
+          {% endif %}
+          
+          <a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}" class="thread-name"><span class="report-id">#{{ thread.pk }}</span> {{ thread.name }}</a>
+          
+          {% if thread.is_read %}
+          <a href="{{ url('report_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last comment{% endtrans %}"><i class="icon-asterisk"></i></a>
+          {% else %}
+          <a href="{{ url('report_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread comment{% endtrans %}"><i class="icon-fire"></i></a>
+          {% endif %}
+
+          <div class="thread-details">
+            {% if thread.report_forum %}
+            {% trans user=thread_starter(thread), start=thread.start|reldate|low, forum=report_forum(thread) %}Post reported by {{ user }} {{ start }} in forum {{ forum }}{% endtrans %}
+            {% else %}
+            {% trans user=thread_starter(thread), start=thread.start|reldate|low %}Deleted post reported by {{ user }} {{ start }}{% endtrans %}
+            {% endif %}
+          </div>
+
+        </div>
+        <div class="span4 thread-activity">
+
+          <div class="thread-replies">
+            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
+            {{ replies(thread.replies) }}
+          </div>
+
+          {% if list_form %}
+          <label class="thread-select checkbox"><input form="threads_form" name="{{ list_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ thread.pk }}"{% if list_form['list_items']['has_value'] and ('' ~ thread.pk) in list_form['list_items']['value'] %} checked="checked"{% endif %}></label>
+          {% endif %}
+        </div>
+      </div>
+    </div>
+    {% else %}
+    <div class="thread-row threads-list-empty">
+      {% trans %}There are no reports currently. Whef!{% endtrans %}
+    </div>
+    {% endfor %}
+
+    {% if list_form %}
+    <div class="threads-actions">
+      <form id="threads_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        {{ form_theme.input_select(list_form['list_action'],width=3) }}
+        <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+      </form>
+    </div>
+    {% endif %}
+  </div>
+
+  <div class="forum-threads-extra">
+    {{ pager() }}
+  </div>
+
+</div>
+{% endblock %}
+
+{% macro replies(thread_replies) -%}
+{% trans count=thread_replies, replies=thread_replies|intcomma -%}
+{{ replies }} reply
+{%- pluralize -%}
+{{ replies }} replies
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro thread_starter(thread) -%}
+{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro thread_reply(thread) -%}
+{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro report_forum(thread) -%}
+<a href="{{ thread.report_forum.url }}" class="forum-link">{{ thread.report_forum }}</a>
+{%- endmacro %}
+
+{% macro pager() %}
+{% if pagination['total'] > 0 %}
+<div class="pagination pull-left">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('forum', slug=forum.slug, forum=forum.id) }}" class="tooltip-top" title="{% trans %}First Page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('forum', slug=forum.slug, forum=forum.id, page=pagination['prev']) }}{% else %}{{ url('forum', slug=forum.slug, forum=forum.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Newest Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('forum', slug=forum.slug, forum=forum.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{% endmacro %}
+
+{% block javascripts -%}{{ super() }}
+  <script type="text/javascript">
+    $(function () {
+      function populateForumTooltip(target) {
+        return $('#forum-' + target + ' .forum-meta').html();
+      };
+      {% for subforum in forum.subforums %}
+        $('#forum-{{ subforum.id }} .forum-title').tooltip({
+          template: '<div class="tooltip forum-meta-tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+          placement: 'right',
+          html: true,
+          title: populateForumTooltip({{ subforum.id }})
+        });
+      {% endfor %}
+      {%- if list_form %}
+      $('#threads_form').submit(function() {
+        if ($('.thread-select[]:checked').length == 0) {
+          alert("{% trans %}You have to select at least one thread.{% endtrans %}");
+          return false;
+        }
+        if ($('#id_list_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete selected reports? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });{% endif %}
+    });
+  </script>
+{%- endblock %}

+ 177 - 177
templates/cranefly/reports/posting.html

@@ -1,177 +1,177 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/editor.html" as editor with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_(get_title()), parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{{ get_title() }}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{{ get_title() }} <small>{{ thread.name }}</small></h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-tag"></i> {% if thread.weight == 2 -%}
-        {% trans %}Open{% endtrans %}
-        {%- elif thread.weight == 1 -%}
-        Resolved
-        {%- else -%}
-        Bogus
-        {%- endif %}</li>
-      {{ get_info() }}
-    </ul>
-  </div>
-</div>
-<div class="container container-primary">
-  <div class="row">
-    <div class="span8 offset2">
-      <div class="posting">
-        <div class="form-container">
-
-          <div class="form-header">
-            <h1>{{ get_title() }}</h1>
-          </div>
-
-          {% if message %}
-          <div class="messages-list">
-            {{ macros.draw_message(message) }}
-          </div>
-          {% endif %}
-
-          {% if preview %}
-          <div class="form-preview">
-            <div class="markdown js-extra">
-              <article>
-                {{ preview|markdown_final|safe }}
-              </article>
-            </div>
-          </div>
-          {% endif %}
-
-          <form action="{{ get_action() }}" method="post">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            {% if 'thread_name' in form.fields %}
-            {{ form_theme.row_widget(form.fields.thread_name, width=8) }}
-            <hr>
-            <h4>{% trans %}Message Body{% endtrans %}</h4>
-            {% endif %}
-            {{ editor.editor(form.fields.post, get_button(), rows=8, extra=get_extra()) }}
-            {% if intersect(form.fields, ('edit_reason', 'thread_weight', 'close_thread')) %}
-            <hr>
-            {% if 'edit_reason' in form.fields %}
-            {{ form_theme.row_widget(form.fields.edit_reason, width=8) }}
-            {% endif %}
-
-            {% if 'thread_weight' in form.fields %}
-            <div class="control-group">
-              <label class="control-label">{% trans %}Set Report Status{% endtrans %}:</label>
-              <div class="controls">
-                {{ form_theme.input_radio_select(form.fields.thread_weight, width=8) }}
-              </div>
-            </div>
-            {% endif %}
-
-            <div class="form-actions">
-              <button type="submit" class="btn btn-primary">{{ get_button() }}</button>
-              <button id="editor-preview" name="preview" type="submit" class="btn">{% trans %}Preview{% endtrans %}</button>
-            </div>
-            {% endif %}
-          </form>
-
-        </div>
-      </div>
-    </div>
-  </div>
-</div>
-{% endblock %}
-
-{% block stylesheets %}{{ super() }}
-<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
-{% endblock %}
-
-{% block javascripts %}{{ super() }}
-  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
-  <script type="text/javascript">
-    hljs.tabReplace = '    ';
-    hljs.initHighlightingOnLoad();
-    EnhancePostsMD();
-  </script>
-  {{ editor.js() }}
-{% endblock %}
-
-
-{% macro get_action() -%}
-{% if action == 'new_thread' -%}
-{{ url('report_start', forum=forum.pk, slug=forum.slug) }}
-{%- elif action == 'edit_thread' -%}
-{{ url('report_edit', thread=thread.pk, slug=thread.slug) }}
-{%- elif action in 'new_reply' -%}
-{%- if quote -%}
-{{ url('report_reply', thread=thread.pk, slug=thread.slug, quote=quote.pk) }}
-{%- else -%}
-{{ url('report_reply', thread=thread.pk, slug=thread.slug) }}
-{%- endif -%}
-{%- elif action == 'edit_reply' -%}
-{{ url('post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_title() -%}
-{%- if action == 'edit_thread' -%}
-{% trans %}Edit Report{% endtrans %}
-{%- elif action == 'new_reply' -%}
-{% trans %}Post New Comment{% endtrans %}
-{%- elif action == 'edit_reply' -%}
-{% trans %}Edit Comment{% endtrans %}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_info() -%}
-{% if action == 'edit_reply' -%}
-    <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-    <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-    <li><i class="icon-pencil"></i> {% if post.edits > 0 -%}
-      {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}
-    {%- else -%}
-      {% trans %}First edit{% endtrans %}
-    {%- endif %}</li>
-{%- else -%}
-    {% if action == 'edit_thread' %}
-    <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=thread.start_post_id) }}">{{ thread.start|reltimesince }}</a></li>
-    {% else %}
-    <li><i class="icon-time"></i> <a href="{{ url('report_new', thread=thread.pk, slug=thread.slug) }}">{{ thread.last|reltimesince }}</a></li>
-    {% endif %}
-    <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
-    <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
-      {% trans count=thread.replies, replies=thread.replies|intcomma %}One comment{% pluralize %}{{ replies }} comments{% endtrans %}
-    {%- else -%}
-      {% trans %}No comments{% endtrans %}
-    {%- endif %}</li>
-{%- endif %}
-    {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
-{%- endmacro %}
-
-
-{% macro get_button() -%}
-{% if action == 'new_reply' -%}
-{% trans %}Post Comment{% endtrans %}
-{%- else -%}
-{% trans %}Save Changes{% endtrans %}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_extra() %}
-  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Preview{% endtrans %}</button>
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/editor.html" as editor with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_(get_title()), parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('report', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{{ get_title() }}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{{ get_title() }} <small>{{ thread.name }}</small></h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-tag"></i> {% if thread.weight == 2 -%}
+        {% trans %}Open{% endtrans %}
+        {%- elif thread.weight == 1 -%}
+        Resolved
+        {%- else -%}
+        Bogus
+        {%- endif %}</li>
+      {{ get_info() }}
+    </ul>
+  </div>
+</div>
+<div class="container container-primary">
+  <div class="row">
+    <div class="span8 offset2">
+      <div class="posting">
+        <div class="form-container">
+
+          <div class="form-header">
+            <h1>{{ get_title() }}</h1>
+          </div>
+
+          {% if message %}
+          <div class="messages-list">
+            {{ macros.draw_message(message) }}
+          </div>
+          {% endif %}
+
+          {% if preview %}
+          <div class="form-preview">
+            <div class="markdown js-extra">
+              <article>
+                {{ preview|markdown_final|safe }}
+              </article>
+            </div>
+          </div>
+          {% endif %}
+
+          <form action="{{ get_action() }}" method="post">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            {% if 'thread_name' in form.fields %}
+            {{ form_theme.row_widget(form.fields.thread_name, width=8) }}
+            <hr>
+            <h4>{% trans %}Message Body{% endtrans %}</h4>
+            {% endif %}
+            {{ editor.editor(form.fields.post, get_button(), rows=8, extra=get_extra()) }}
+            {% if intersect(form.fields, ('edit_reason', 'thread_weight', 'close_thread')) %}
+            <hr>
+            {% if 'edit_reason' in form.fields %}
+            {{ form_theme.row_widget(form.fields.edit_reason, width=8) }}
+            {% endif %}
+
+            {% if 'thread_weight' in form.fields %}
+            <div class="control-group">
+              <label class="control-label">{% trans %}Set Report Status{% endtrans %}:</label>
+              <div class="controls">
+                {{ form_theme.input_radio_select(form.fields.thread_weight, width=8) }}
+              </div>
+            </div>
+            {% endif %}
+
+            <div class="form-actions">
+              <button type="submit" class="btn btn-primary">{{ get_button() }}</button>
+              <button id="editor-preview" name="preview" type="submit" class="btn">{% trans %}Preview{% endtrans %}</button>
+            </div>
+            {% endif %}
+          </form>
+
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock %}
+
+{% block stylesheets %}{{ super() }}
+<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
+{% endblock %}
+
+{% block javascripts %}{{ super() }}
+  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
+  <script type="text/javascript">
+    hljs.tabReplace = '    ';
+    hljs.initHighlightingOnLoad();
+    EnhancePostsMD();
+  </script>
+  {{ editor.js() }}
+{% endblock %}
+
+
+{% macro get_action() -%}
+{% if action == 'new_thread' -%}
+{{ url('report_start', forum=forum.pk, slug=forum.slug) }}
+{%- elif action == 'edit_thread' -%}
+{{ url('report_edit', thread=thread.pk, slug=thread.slug) }}
+{%- elif action in 'new_reply' -%}
+{%- if quote -%}
+{{ url('report_reply', thread=thread.pk, slug=thread.slug, quote=quote.pk) }}
+{%- else -%}
+{{ url('report_reply', thread=thread.pk, slug=thread.slug) }}
+{%- endif -%}
+{%- elif action == 'edit_reply' -%}
+{{ url('post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_title() -%}
+{%- if action == 'edit_thread' -%}
+{% trans %}Edit Report{% endtrans %}
+{%- elif action == 'new_reply' -%}
+{% trans %}Post New Comment{% endtrans %}
+{%- elif action == 'edit_reply' -%}
+{% trans %}Edit Comment{% endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_info() -%}
+{% if action == 'edit_reply' -%}
+    <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+    <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+    <li><i class="icon-pencil"></i> {% if post.edits > 0 -%}
+      {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}
+    {%- else -%}
+      {% trans %}First edit{% endtrans %}
+    {%- endif %}</li>
+{%- else -%}
+    {% if action == 'edit_thread' %}
+    <li><i class="icon-time"></i> <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=thread.start_post_id) }}">{{ thread.start|reltimesince }}</a></li>
+    {% else %}
+    <li><i class="icon-time"></i> <a href="{{ url('report_new', thread=thread.pk, slug=thread.slug) }}">{{ thread.last|reltimesince }}</a></li>
+    {% endif %}
+    <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
+    <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
+      {% trans count=thread.replies, replies=thread.replies|intcomma %}One comment{% pluralize %}{{ replies }} comments{% endtrans %}
+    {%- else -%}
+      {% trans %}No comments{% endtrans %}
+    {%- endif %}</li>
+{%- endif %}
+    {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
+{%- endmacro %}
+
+
+{% macro get_button() -%}
+{% if action == 'new_reply' -%}
+{% trans %}Post Comment{% endtrans %}
+{%- else -%}
+{% trans %}Save Changes{% endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_extra() %}
+  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Preview{% endtrans %}</button>
+{% endmacro %}

+ 460 - 460
templates/cranefly/reports/thread.html

@@ -1,460 +1,460 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/editor.html" as editor with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=thread.name,parent=forum.name,page=pagination['page']) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{{ thread.name }}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{{ thread.name }}</h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-tag"></i> {% if thread.weight == 2 -%}
-        {% trans %}Open{% endtrans %}
-        {%- elif thread.weight == 1 -%}
-        Resolved
-        {%- else -%}
-        Bogus
-        {%- endif %}</li>
-      <li><i class="icon-time"></i> {{ thread.last|reltimesince }}</li>
-      <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
-      <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
-        {% trans count=thread.replies, replies=thread.replies|intcomma %}One comment{% pluralize %}{{ replies }} comments{% endtrans %}
-      {%- else -%}
-        {% trans %}No comments{% endtrans %}
-      {%- endif %}</li>
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary report-view">
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <div class="thread-buttons">
-    {{ pager() }}
-    <a href="{{ url('report_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Comment{% endtrans %}</a>
-    {% if watcher %}
-    <form action="{{ url('report_unwatch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Remove thread from watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
-    {% if watcher.email %}
-    <form action="{{ url('report_unwatch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Don't e-mail me anymore if anyone replies to this thread{% endtrans %}"><i class="icon-envelope"></i></button></form>
-    {% else %}
-    <form action="{{ url('report_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}E-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
-    {% endif %}
-    {% else %}
-    <form action="{{ url('report_watch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
-    <form action="{{ url('report_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list and e-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
-    {% endif %}
-  </div>
-
-  <div class="thread-body">
-    {% if thread.start_post_id != posts[0].pk %}
-    {% set posts = (posts|list) %}
-    {% do posts.insert(0, thread.start_post) %}
-    {% endif %}
-    {% for post in posts %}
-    <div id="post-{{ post.pk }}" class="post-wrapper{% if post.pk == thread.start_post_id %} report-wrapper{% endif %}">
-      {% if post.message %}
-      <div class="messages-list">
-        {{ macros.draw_message(post.message) }}
-      </div>
-      {% endif %}
-      {% if post.deleted and not acl.threads.can_see_deleted_posts(forum) %}
-      <div class="post-body post-muted">
-        {% if post.user_id %}
-        <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(50) }}" alt="" class="user-avatar"></a>
-        {% else %}
-        <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
-        {% endif %}
-        <div class="post-content">
-          <div class="post-header">
-            <div class="post-header-compact">
-              {% if post.user_id %}
-              <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
-              {% else %}
-              <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
-              {% endif %}
-              <span class="separator">&ndash;</span>
-              <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
-              {% if post.edits %}
-              <span class="separator">&ndash;</span>
-              {% if acl.threads.can_see_changelog(user, forum, post) %}
-              <a href="{{ url('report_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
-              {% else %}
-              <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
-              {% endif %}
-              {% endif %}
-            </div>
-
-            <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
-
-            {% if not post.is_read %}
-            <div class="post-extra">
-              <span class="label label-warning">
-                {% trans %}New{% endtrans %}
-              </span>
-            </div>
-            {% endif %}
-
-          </div>
-          <div class="post-message">
-            {% trans user=edit_user(post), date=post.delete_date|reltimesince|low %}{{ user }} has deleted this reply {{ date }}{% endtrans %}
-          </div>
-        </dv>
-      </div>
-      {% else %}
-      <div class="post-body">
-        {% if post.user_id %}
-        <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(100) }}" alt="" class="user-avatar"></a>
-        {% else %}
-        <img src="{{ macros.avatar_guest(100) }}" alt="" class="user-avatar">
-        {% endif %}
-        <div class="post-arrow"></div>
-        <div class="post-content">
-          <div class="post-header">
-            {% if post.user_id %}
-            <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
-            {% else %}
-            <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
-            {% endif %}
-            <span class="separator">&ndash;</span>
-            <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
-            {% if post.edits %}
-            <span class="separator">&ndash;</span>
-            {% if acl.threads.can_see_changelog(user, forum, post) %}
-            <a href="{{ url('report_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
-            {% else %}
-            <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
-            {% endif %}
-            {% endif %}
-
-            {% if posts_form and thread.start_post_id != post.pk %}
-            <label class="checkbox post-checkbox"><input form="posts_form" name="{{ posts_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ post.pk }}"{% if posts_form['list_items']['has_value'] and ('' ~ post.pk) in posts_form['list_items']['value'] %} checked="checked"{% endif %}></label>
-            {% endif %}
-
-            {% if posts_form and thread.start_post_id == post.pk %}
-            <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#1</i></a>
-            {% else %}
-            <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index0 }}</a>
-            {% endif %}
-
-            <div class="post-extra">
-              {% if post.protected and acl.threads.can_protect(forum) %}
-              <span class="label label-info">
-                {% trans %}Protected{% endtrans %}
-              </span>
-              {% endif %}
-
-              {% if post.deleted %}
-              <span class="label label-inverse">
-                {% trans %}Deleted{% endtrans %}
-              </span>
-              {% endif %}
-
-              {% if not post.is_read %}
-              <span class="label label-warning">
-                {% trans %}New{% endtrans %}
-              </span>
-              {% endif %}
-            </div>
-          </div>
-          <div class="post-message">
-            <div class="markdown js-extra">
-              <article>
-                {{ post.post_preparsed|markdown_final|safe }}
-              </article>
-            </div>
-          </div>
-          <div class="post-footer">{% filter trim %}
-            {% if post.pk == thread.start_post_id %}
-            <div class="post-actions report-actions">
-              {% if thread.weight == 2 %}
-              <form action="{{ request_path }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <input type="hidden" name="origin" value="thread_form">
-                <input type="hidden" name="thread_action" value="sticky">
-                <button type="submit" class="btn btn-link btn-resolve tooltip-top" title="{% trans %}Set this report as resolved{% endtrans %}"><i class="icon-ok"></i> {% trans %}Resolved{% endtrans %}</button>
-              </form>
-              <form action="{{ request_path }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <input type="hidden" name="origin" value="thread_form">
-                <input type="hidden" name="thread_action" value="normal">
-                <button type="submit" class="btn btn-link btn-bogus tooltip-top" title="{% trans %}Set this report as bogus{% endtrans %}"><i class="icon-remove"></i> {% trans %}Bogus{% endtrans %}</button>
-              </form>
-              {% elif thread.weight != 1 %}
-              <form action="{{ request_path }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <input type="hidden" name="origin" value="thread_form">
-                <input type="hidden" name="thread_action" value="sticky">
-                <button type="submit" class="btn btn-link btn-bogus tooltip-top" title="{% trans %}Set this report as resolved{% endtrans %}"><i class="icon-remove"></i> {% trans %}Bogus{% endtrans %}</button>
-              </form>
-              {% elif thread.weight != 0 %}
-              <form action="{{ request_path }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <input type="hidden" name="origin" value="thread_form">
-                <input type="hidden" name="thread_action" value="normal">
-                <button type="submit" class="btn btn-link btn-resolve tooltip-top" title="{% trans %}Set this report as bogus{% endtrans %}"><i class="icon-ok"></i> {% trans %}Resolved{% endtrans %}</button>
-              </form>
-              {% endif %}
-            </div>
-            {% endif %}
-            <div class="post-actions">
-              {% if acl.users.can_see_users_trails() -%}
-              <a href="{{ url('report_post_info', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-trail">{% trans %}Info{% endtrans %}</a>
-              {% endif %}
-              {% if acl.threads.can_edit_thread(user, forum, thread, post) and thread.start_post_id == post.pk %}
-              <a href="{{ url('report_edit', thread=thread.pk, slug=thread.slug) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
-              {% elif acl.threads.can_edit_reply(user, forum, thread, post) %}
-              <a href="{{ url('report_post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
-              {%- endif %}
-              <a href="{{ url('report_reply', thread=thread.pk, slug=thread.slug, quote=post.pk) }}" class="post-reply">{% trans %}Reply{% endtrans %}</a>
-            </div>
-            {% if post.pk == thread.start_post_id %}
-            <div class="post-actions">
-              {% if acl.threads.can_delete_thread(user, forum, thread, post) %}
-              {% if post.deleted %}
-              <form action="{{ url('report_show', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this thread visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
-              </form>
-              {% else %}
-              <form action="{{ url('report_hide', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this thread from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
-              </form>
-              {% endif %}
-              {% endif %}
-              {% if acl.threads.can_delete_thread(user, forum, thread, post) == 2 %}
-              <form action="{{ url('report_delete', thread=thread.pk, slug=thread.slug) }}" class="form-inline prompt-delete-thread" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this thread for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
-              </form>
-              {% endif %}
-            </div>
-            {% elif post.pk != thread.start_post_id and acl.threads.can_delete_post(user, forum, thread, post) %}
-            <div class="post-actions">
-              {% if acl.threads.can_delete_post(user, forum, thread, post) %}
-              {% if post.deleted %}
-              <form action="{{ url('report_post_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this reply visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
-              </form>
-              {% else %}
-              <form action="{{ url('report_post_hide', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this reply from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
-              </form>
-              {% endif %}
-              {% endif %}
-              {% if acl.threads.can_delete_post(user, forum, thread, post) == 2 -%}
-              <form action="{{ url('report_post_delete', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline prompt-delete-post" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this reply for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
-              </form>
-              {% endif %}
-            </div>
-            {% endif %}
-          {% endfilter %}</div>
-        </div>
-      </div>
-      {% endif %}
-    </div>
-
-    {% if post.checkpoints_visible %}
-    <div class="post-checkpoints">
-      {% for checkpoint in post.checkpoints_visible %}
-      <div class="post-checkpoint{% if checkpoint.deleted %} checkpoint-deleted{% endif %}">
-        <hr>
-        <span>
-          {%- if checkpoint.action == 'limit' -%}
-          <i class="icon-lock"></i> {% trans  %}This thread has reached its post limit and has been closed.{% endtrans %}
-          {%- elif checkpoint.action == 'resolved' -%}
-          <i class="icon-ok"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} has set this report as resolved {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'bogus' -%}
-          <i class="icon-remove"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} has set this report as bogus {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'reported' -%}
-          <i class="icon-fire"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} has also reported this post {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'deleted' -%}
-          <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} deleted this thread {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'undeleted' -%}
-          <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} restored this thread {{ date }}{% endtrans %}
-          {%- endif -%}
-          {% if acl.threads.can_delete_checkpoint(forum) %}
-          {% if checkpoint.deleted %}
-          <form action="{{ url('report_post_checkpoint_show', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-            <button type="submit" class="btn btn-link btn-show">{% trans %}Restore{% endtrans %}</button>
-          </form>
-          {% else %}
-          <form action="{{ url('report_post_checkpoint_hide', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-            <button type="submit" class="btn btn-link btn-hide">{% trans %}Hide{% endtrans %}</button>
-          </form>
-          {% endif %}
-          {% endif %}
-          {% if acl.threads.can_delete_checkpoint(forum) == 2 %}
-          <form action="{{ url('report_post_checkpoint_delete', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline prompt-delete-checkpoint">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-            <button type="submit" class="btn btn-link btn-delete">{% trans %}Delete{% endtrans %}</button>
-          </form>
-          {% endif %}
-        </span>
-      </div>
-      {% endfor %}
-    </div>
-    {% endif %}
-    {% endfor %}
-  </div>
-
-  {% if thread_form or posts_form %}
-  <div class="thread-moderation">
-    <form id="thread_form" class="form-inline pull-left" action="{{ request_path }}" method="POST">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <input type="hidden" name="origin" value="thread_form">
-      {{ form_theme.input_select(thread_form['thread_action'],width=3) }}
-      <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-    </form>
-    {% if posts_form%}
-    <form id="posts_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <input type="hidden" name="origin" value="posts_form">
-      {{ form_theme.input_select(posts_form['list_action'],width=3) }}
-      <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-    </form>
-    {% endif %}
-  </div>
-  {% endif %}
-
-  <div class="thread-buttons">
-    {{ pager(false) }}
-    <a href="{{ url('report_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Comment{% endtrans %}</a>
-  </div>
-
-  <div class="thread-quick-reply">
-    <form action="{{ url('report_reply', thread=thread.pk, slug=thread.slug) }}" method="post">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <input type="hidden" name="quick_reply" value="1">
-      <img src="{{ user.get_avatar(100) }}" alt="{% trans %}Your Avatar{% endtrans %}" class="user-avatar">
-      {{ editor.editor(quick_reply.post, _('Post Comment'), extra=editor_extra()) }}
-    </form>
-  </div>
-
-</div>
-{% endblock %}
-
-{% block stylesheets %}{{ super() }}
-<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
-{% endblock %}
-
-{% block javascripts -%}{{ super() }}
-  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
-  <script type="text/javascript">
-    hljs.tabReplace = '    ';
-    hljs.initHighlightingOnLoad();
-    EnhancePostsMD();
-    $(function () {
-      $('#thread_form').submit(function() {
-        if ($('#id_thread_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete this report? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });
-      $('#posts_form').submit(function() {
-        if ($('.post-checkbox[]:checked').length == 0) {
-          alert("{% trans %}You have to select at least one comment.{% endtrans %}");
-          return false;
-        }
-        if ($('#id_list_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete selected comments? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });
-      $('.prompt-delete-thread').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this report?{% endtrans %}");
-          return decision;
-      });
-      $('.prompt-delete-post').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this comment?{% endtrans %}");
-          return decision;
-      });
-      $('.prompt-delete-checkpoint').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this checkpoint?{% endtrans %}");
-          return decision;
-      });
-    });
-  </script>
-  {{ editor.js() }}
-{%- endblock %}
-
-
-{% macro user_label(user) -%}
-<{% if user.rank and user.rank.as_tab %}a href="{{ url('users', slug=user.rank.slug) }}"{% else %}span{% endif %} class="label post-author-label{% if user.rank and user.rank.style %} post-label-{{ user.rank.style }}{% endif %}">{{ user.get_title() }}</{% if user.rank and user.rank.as_tab%}a{% else %}span{% endif %}>
-{%- endmacro %}
-
-
-{% macro pager(extra=true) %}
-<div class="pagination pull-left">
-  <ul>
-    {% if pagination['total'] > 0 %}
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('report', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('report', slug=thread.slug, thread=thread.id, page=pagination['prev']) }}{% else %}{{ url('report', slug=thread.slug, thread=thread.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('report', slug=thread.slug, thread=thread.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Newest Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 and pagination['next'] < pagination['total'] %}<li><a href="{{ url('report', slug=thread.slug, thread=thread.id, page=pagination['total']) }}" class="tooltip-top" title="{% trans %}Go to last page{% endtrans %}">{% trans %}Last{% endtrans %} <i class="icon-chevron-right"></i></a></li>{% endif -%}
-    {% endif %}
-    {% if extra %}
-    {% if not is_read %}<li><a href="{{ url('report_new', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first unread{% endtrans %}"><i class="icon-star"></i> {% trans %}First Unread{% endtrans %}</a></li>{% endif %}
-    {% endif %}
-  </ul>
-</div>
-{% endmacro %}
-
-
-{% macro checkpoint_user(checkpoint) -%}
-{%- if checkpoint.user_id -%}
-<a href="{{ url('user', user=checkpoint.user_id, username=checkpoint.user_slug) }}">{{ checkpoint.user_name }}</a>
-{%- else -%}
-<strong>{{ checkpoint.user_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro checkpoint_forum(checkpoint) -%}
-{%- if checkpoint.old_forum_id -%}
-<a href="{{ url('forum', forum=checkpoint.old_forum_id, slug=checkpoint.old_forum_slug) }}">{{ checkpoint.old_forum_name }}</a>
-{%- else -%}
-<strong>{{ checkpoint.old_forum_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro edit_user(post) -%}
-{%- if post.edit_user_id -%}
-<a href="{{ url('user', user=post.edit_user_id, username=post.edit_user_slug) }}">{{ post.edit_user_name }}</a>
-{%- else -%}
-<strong>{{ post.edit_user_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro editor_extra() %}
-  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Full Editor{% endtrans %}</button>
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/editor.html" as editor with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=thread.name,parent=forum.name,page=pagination['page']) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('reports') }}">{% trans %}Reported Posts{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{{ thread.name }}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{{ thread.name }}</h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-tag"></i> {% if thread.weight == 2 -%}
+        {% trans %}Open{% endtrans %}
+        {%- elif thread.weight == 1 -%}
+        Resolved
+        {%- else -%}
+        Bogus
+        {%- endif %}</li>
+      <li><i class="icon-time"></i> {{ thread.last|reltimesince }}</li>
+      <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
+      <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
+        {% trans count=thread.replies, replies=thread.replies|intcomma %}One comment{% pluralize %}{{ replies }} comments{% endtrans %}
+      {%- else -%}
+        {% trans %}No comments{% endtrans %}
+      {%- endif %}</li>
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary report-view">
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="thread-buttons">
+    {{ pager() }}
+    <a href="{{ url('report_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Comment{% endtrans %}</a>
+    {% if watcher %}
+    <form action="{{ url('report_unwatch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Remove thread from watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
+    {% if watcher.email %}
+    <form action="{{ url('report_unwatch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Don't e-mail me anymore if anyone replies to this thread{% endtrans %}"><i class="icon-envelope"></i></button></form>
+    {% else %}
+    <form action="{{ url('report_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}E-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
+    {% endif %}
+    {% else %}
+    <form action="{{ url('report_watch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
+    <form action="{{ url('report_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list and e-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
+    {% endif %}
+  </div>
+
+  <div class="thread-body">
+    {% if thread.start_post_id != posts[0].pk %}
+    {% set posts = (posts|list) %}
+    {% do posts.insert(0, thread.start_post) %}
+    {% endif %}
+    {% for post in posts %}
+    <div id="post-{{ post.pk }}" class="post-wrapper{% if post.pk == thread.start_post_id %} report-wrapper{% endif %}">
+      {% if post.message %}
+      <div class="messages-list">
+        {{ macros.draw_message(post.message) }}
+      </div>
+      {% endif %}
+      {% if post.deleted and not acl.threads.can_see_deleted_posts(forum) %}
+      <div class="post-body post-muted">
+        {% if post.user_id %}
+        <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(50) }}" alt="" class="user-avatar"></a>
+        {% else %}
+        <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
+        {% endif %}
+        <div class="post-content">
+          <div class="post-header">
+            <div class="post-header-compact">
+              {% if post.user_id %}
+              <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
+              {% else %}
+              <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
+              {% endif %}
+              <span class="separator">&ndash;</span>
+              <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
+              {% if post.edits %}
+              <span class="separator">&ndash;</span>
+              {% if acl.threads.can_see_changelog(user, forum, post) %}
+              <a href="{{ url('report_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
+              {% else %}
+              <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
+              {% endif %}
+              {% endif %}
+            </div>
+
+            <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
+
+            {% if not post.is_read %}
+            <div class="post-extra">
+              <span class="label label-warning">
+                {% trans %}New{% endtrans %}
+              </span>
+            </div>
+            {% endif %}
+
+          </div>
+          <div class="post-message">
+            {% trans user=edit_user(post), date=post.delete_date|reltimesince|low %}{{ user }} has deleted this reply {{ date }}{% endtrans %}
+          </div>
+        </dv>
+      </div>
+      {% else %}
+      <div class="post-body">
+        {% if post.user_id %}
+        <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(100) }}" alt="" class="user-avatar"></a>
+        {% else %}
+        <img src="{{ macros.avatar_guest(100) }}" alt="" class="user-avatar">
+        {% endif %}
+        <div class="post-arrow"></div>
+        <div class="post-content">
+          <div class="post-header">
+            {% if post.user_id %}
+            <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
+            {% else %}
+            <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
+            {% endif %}
+            <span class="separator">&ndash;</span>
+            <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
+            {% if post.edits %}
+            <span class="separator">&ndash;</span>
+            {% if acl.threads.can_see_changelog(user, forum, post) %}
+            <a href="{{ url('report_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
+            {% else %}
+            <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
+            {% endif %}
+            {% endif %}
+
+            {% if posts_form and thread.start_post_id != post.pk %}
+            <label class="checkbox post-checkbox"><input form="posts_form" name="{{ posts_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ post.pk }}"{% if posts_form['list_items']['has_value'] and ('' ~ post.pk) in posts_form['list_items']['value'] %} checked="checked"{% endif %}></label>
+            {% endif %}
+
+            {% if posts_form and thread.start_post_id == post.pk %}
+            <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#1</i></a>
+            {% else %}
+            <a href="{{ url('report_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index0 }}</a>
+            {% endif %}
+
+            <div class="post-extra">
+              {% if post.protected and acl.threads.can_protect(forum) %}
+              <span class="label label-info">
+                {% trans %}Protected{% endtrans %}
+              </span>
+              {% endif %}
+
+              {% if post.deleted %}
+              <span class="label label-inverse">
+                {% trans %}Deleted{% endtrans %}
+              </span>
+              {% endif %}
+
+              {% if not post.is_read %}
+              <span class="label label-warning">
+                {% trans %}New{% endtrans %}
+              </span>
+              {% endif %}
+            </div>
+          </div>
+          <div class="post-message">
+            <div class="markdown js-extra">
+              <article>
+                {{ post.post_preparsed|markdown_final|safe }}
+              </article>
+            </div>
+          </div>
+          <div class="post-footer">{% filter trim %}
+            {% if post.pk == thread.start_post_id %}
+            <div class="post-actions report-actions">
+              {% if thread.weight == 2 %}
+              <form action="{{ request_path }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <input type="hidden" name="origin" value="thread_form">
+                <input type="hidden" name="thread_action" value="sticky">
+                <button type="submit" class="btn btn-link btn-resolve tooltip-top" title="{% trans %}Set this report as resolved{% endtrans %}"><i class="icon-ok"></i> {% trans %}Resolved{% endtrans %}</button>
+              </form>
+              <form action="{{ request_path }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <input type="hidden" name="origin" value="thread_form">
+                <input type="hidden" name="thread_action" value="normal">
+                <button type="submit" class="btn btn-link btn-bogus tooltip-top" title="{% trans %}Set this report as bogus{% endtrans %}"><i class="icon-remove"></i> {% trans %}Bogus{% endtrans %}</button>
+              </form>
+              {% elif thread.weight != 1 %}
+              <form action="{{ request_path }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <input type="hidden" name="origin" value="thread_form">
+                <input type="hidden" name="thread_action" value="sticky">
+                <button type="submit" class="btn btn-link btn-bogus tooltip-top" title="{% trans %}Set this report as resolved{% endtrans %}"><i class="icon-remove"></i> {% trans %}Bogus{% endtrans %}</button>
+              </form>
+              {% elif thread.weight != 0 %}
+              <form action="{{ request_path }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <input type="hidden" name="origin" value="thread_form">
+                <input type="hidden" name="thread_action" value="normal">
+                <button type="submit" class="btn btn-link btn-resolve tooltip-top" title="{% trans %}Set this report as bogus{% endtrans %}"><i class="icon-ok"></i> {% trans %}Resolved{% endtrans %}</button>
+              </form>
+              {% endif %}
+            </div>
+            {% endif %}
+            <div class="post-actions">
+              {% if acl.users.can_see_users_trails() -%}
+              <a href="{{ url('report_post_info', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-trail">{% trans %}Info{% endtrans %}</a>
+              {% endif %}
+              {% if acl.threads.can_edit_thread(user, forum, thread, post) and thread.start_post_id == post.pk %}
+              <a href="{{ url('report_edit', thread=thread.pk, slug=thread.slug) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
+              {% elif acl.threads.can_edit_reply(user, forum, thread, post) %}
+              <a href="{{ url('report_post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
+              {%- endif %}
+              <a href="{{ url('report_reply', thread=thread.pk, slug=thread.slug, quote=post.pk) }}" class="post-reply">{% trans %}Reply{% endtrans %}</a>
+            </div>
+            {% if post.pk == thread.start_post_id %}
+            <div class="post-actions">
+              {% if acl.threads.can_delete_thread(user, forum, thread, post) %}
+              {% if post.deleted %}
+              <form action="{{ url('report_show', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this thread visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
+              </form>
+              {% else %}
+              <form action="{{ url('report_hide', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this thread from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
+              </form>
+              {% endif %}
+              {% endif %}
+              {% if acl.threads.can_delete_thread(user, forum, thread, post) == 2 %}
+              <form action="{{ url('report_delete', thread=thread.pk, slug=thread.slug) }}" class="form-inline prompt-delete-thread" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this thread for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
+              </form>
+              {% endif %}
+            </div>
+            {% elif post.pk != thread.start_post_id and acl.threads.can_delete_post(user, forum, thread, post) %}
+            <div class="post-actions">
+              {% if acl.threads.can_delete_post(user, forum, thread, post) %}
+              {% if post.deleted %}
+              <form action="{{ url('report_post_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this reply visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
+              </form>
+              {% else %}
+              <form action="{{ url('report_post_hide', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this reply from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
+              </form>
+              {% endif %}
+              {% endif %}
+              {% if acl.threads.can_delete_post(user, forum, thread, post) == 2 -%}
+              <form action="{{ url('report_post_delete', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline prompt-delete-post" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this reply for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
+              </form>
+              {% endif %}
+            </div>
+            {% endif %}
+          {% endfilter %}</div>
+        </div>
+      </div>
+      {% endif %}
+    </div>
+
+    {% if post.checkpoints_visible %}
+    <div class="post-checkpoints">
+      {% for checkpoint in post.checkpoints_visible %}
+      <div class="post-checkpoint{% if checkpoint.deleted %} checkpoint-deleted{% endif %}">
+        <hr>
+        <span>
+          {%- if checkpoint.action == 'limit' -%}
+          <i class="icon-lock"></i> {% trans  %}This thread has reached its post limit and has been closed.{% endtrans %}
+          {%- elif checkpoint.action == 'resolved' -%}
+          <i class="icon-ok"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} has set this report as resolved {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'bogus' -%}
+          <i class="icon-remove"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} has set this report as bogus {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'reported' -%}
+          <i class="icon-fire"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} has also reported this post {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'deleted' -%}
+          <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} deleted this thread {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'undeleted' -%}
+          <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} restored this thread {{ date }}{% endtrans %}
+          {%- endif -%}
+          {% if acl.threads.can_delete_checkpoint(forum) %}
+          {% if checkpoint.deleted %}
+          <form action="{{ url('report_post_checkpoint_show', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+            <button type="submit" class="btn btn-link btn-show">{% trans %}Restore{% endtrans %}</button>
+          </form>
+          {% else %}
+          <form action="{{ url('report_post_checkpoint_hide', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+            <button type="submit" class="btn btn-link btn-hide">{% trans %}Hide{% endtrans %}</button>
+          </form>
+          {% endif %}
+          {% endif %}
+          {% if acl.threads.can_delete_checkpoint(forum) == 2 %}
+          <form action="{{ url('report_post_checkpoint_delete', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline prompt-delete-checkpoint">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+            <button type="submit" class="btn btn-link btn-delete">{% trans %}Delete{% endtrans %}</button>
+          </form>
+          {% endif %}
+        </span>
+      </div>
+      {% endfor %}
+    </div>
+    {% endif %}
+    {% endfor %}
+  </div>
+
+  {% if thread_form or posts_form %}
+  <div class="thread-moderation">
+    <form id="thread_form" class="form-inline pull-left" action="{{ request_path }}" method="POST">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <input type="hidden" name="origin" value="thread_form">
+      {{ form_theme.input_select(thread_form['thread_action'],width=3) }}
+      <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+    </form>
+    {% if posts_form%}
+    <form id="posts_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <input type="hidden" name="origin" value="posts_form">
+      {{ form_theme.input_select(posts_form['list_action'],width=3) }}
+      <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+    </form>
+    {% endif %}
+  </div>
+  {% endif %}
+
+  <div class="thread-buttons">
+    {{ pager(false) }}
+    <a href="{{ url('report_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Comment{% endtrans %}</a>
+  </div>
+
+  <div class="thread-quick-reply">
+    <form action="{{ url('report_reply', thread=thread.pk, slug=thread.slug) }}" method="post">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <input type="hidden" name="quick_reply" value="1">
+      <img src="{{ user.get_avatar(100) }}" alt="{% trans %}Your Avatar{% endtrans %}" class="user-avatar">
+      {{ editor.editor(quick_reply.post, _('Post Comment'), extra=editor_extra()) }}
+    </form>
+  </div>
+
+</div>
+{% endblock %}
+
+{% block stylesheets %}{{ super() }}
+<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
+{% endblock %}
+
+{% block javascripts -%}{{ super() }}
+  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
+  <script type="text/javascript">
+    hljs.tabReplace = '    ';
+    hljs.initHighlightingOnLoad();
+    EnhancePostsMD();
+    $(function () {
+      $('#thread_form').submit(function() {
+        if ($('#id_thread_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete this report? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });
+      $('#posts_form').submit(function() {
+        if ($('.post-checkbox[]:checked').length == 0) {
+          alert("{% trans %}You have to select at least one comment.{% endtrans %}");
+          return false;
+        }
+        if ($('#id_list_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete selected comments? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });
+      $('.prompt-delete-thread').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this report?{% endtrans %}");
+          return decision;
+      });
+      $('.prompt-delete-post').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this comment?{% endtrans %}");
+          return decision;
+      });
+      $('.prompt-delete-checkpoint').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this checkpoint?{% endtrans %}");
+          return decision;
+      });
+    });
+  </script>
+  {{ editor.js() }}
+{%- endblock %}
+
+
+{% macro user_label(user) -%}
+<{% if user.rank and user.rank.as_tab %}a href="{{ url('users', slug=user.rank.slug) }}"{% else %}span{% endif %} class="label post-author-label{% if user.rank and user.rank.style %} post-label-{{ user.rank.style }}{% endif %}">{{ user.get_title() }}</{% if user.rank and user.rank.as_tab%}a{% else %}span{% endif %}>
+{%- endmacro %}
+
+
+{% macro pager(extra=true) %}
+<div class="pagination pull-left">
+  <ul>
+    {% if pagination['total'] > 0 %}
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('report', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('report', slug=thread.slug, thread=thread.id, page=pagination['prev']) }}{% else %}{{ url('report', slug=thread.slug, thread=thread.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('report', slug=thread.slug, thread=thread.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Newest Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 and pagination['next'] < pagination['total'] %}<li><a href="{{ url('report', slug=thread.slug, thread=thread.id, page=pagination['total']) }}" class="tooltip-top" title="{% trans %}Go to last page{% endtrans %}">{% trans %}Last{% endtrans %} <i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {% endif %}
+    {% if extra %}
+    {% if not is_read %}<li><a href="{{ url('report_new', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first unread{% endtrans %}"><i class="icon-star"></i> {% trans %}First Unread{% endtrans %}</a></li>{% endif %}
+    {% endif %}
+  </ul>
+</div>
+{% endmacro %}
+
+
+{% macro checkpoint_user(checkpoint) -%}
+{%- if checkpoint.user_id -%}
+<a href="{{ url('user', user=checkpoint.user_id, username=checkpoint.user_slug) }}">{{ checkpoint.user_name }}</a>
+{%- else -%}
+<strong>{{ checkpoint.user_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro checkpoint_forum(checkpoint) -%}
+{%- if checkpoint.old_forum_id -%}
+<a href="{{ url('forum', forum=checkpoint.old_forum_id, slug=checkpoint.old_forum_slug) }}">{{ checkpoint.old_forum_name }}</a>
+{%- else -%}
+<strong>{{ checkpoint.old_forum_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro edit_user(post) -%}
+{%- if post.edit_user_id -%}
+<a href="{{ url('user', user=post.edit_user_id, username=post.edit_user_slug) }}">{{ post.edit_user_name }}</a>
+{%- else -%}
+<strong>{{ post.edit_user_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro editor_extra() %}
+  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Full Editor{% endtrans %}</button>
+{% endmacro %}

+ 35 - 35
templates/cranefly/resend_activation.html

@@ -1,36 +1,36 @@
-{% extends "cranefly/layout.html" %}
-{% import "forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Request new Activation E-mail')) }}{% endblock %}
-
-{% block content %}
-<div class="row">
-  <div class="span6 offset3">
-    <div class="form-container">
-
-      <div class="form-header">
-        <h1>{% trans %}Request new Activation E-mail{% endtrans %}</h1>
-      </div>
-
-      {% if message %}
-      <div class="messages-list">
-        {{ macros.draw_message(message) }}
-      </div>
-      {% endif %}
-
-      <form action="{{ url('send_activation') }}" method="post">
-        {{ form_theme.hidden_fields(form) }}
-        <div class="form-fields">
-          {{ form_theme.row(form.email, attrs={'class': 'span6', 'placeholder': _("Enter your e-mail address.")}) }}
-          {{ form_theme.captcha(form, width=6) }}
-        </div>
-        <div class="form-actions">
-          <button type="submit" class="btn btn-primary">{% trans %}Request new E-mail{% endtrans %}</button>
-        </div>
-      </form>
-
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Request new Activation E-mail')) }}{% endblock %}
+
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="form-container">
+
+      <div class="form-header">
+        <h1>{% trans %}Request new Activation E-mail{% endtrans %}</h1>
+      </div>
+
+      {% if message %}
+      <div class="messages-list">
+        {{ macros.draw_message(message) }}
+      </div>
+      {% endif %}
+
+      <form action="{{ url('send_activation') }}" method="post">
+        {{ form_theme.hidden_fields(form) }}
+        <div class="form-fields">
+          {{ form_theme.row(form.email, attrs={'class': 'span6', 'placeholder': _("Enter your e-mail address.")}) }}
+          {{ form_theme.captcha(form, width=6) }}
+        </div>
+        <div class="form-actions">
+          <button type="submit" class="btn btn-primary">{% trans %}Request new E-mail{% endtrans %}</button>
+        </div>
+      </form>
+
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 35 - 35
templates/cranefly/reset_password.html

@@ -1,36 +1,36 @@
-{% extends "cranefly/layout.html" %}
-{% import "forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Request New Password')) }}{% endblock %}
-
-{% block content %}
-<div class="row">
-  <div class="span6 offset3">
-    <div class="form-container">
-
-      <div class="form-header">
-        <h1>{% trans %}Request New Password{% endtrans %}</h1>
-      </div>
-
-      {% if message %}
-      <div class="messages-list">
-        {{ macros.draw_message(message) }}
-      </div>
-      {% endif %}
-
-      <form action="{{ url('forgot_password') }}" method="post">
-        {{ form_theme.hidden_fields(form) }}
-        <div class="form-fields">
-          {{ form_theme.row(form.email, attrs={'class': 'span6', 'placeholder': _("Enter your e-mail address.")}) }}
-          {{ form_theme.captcha(form, width=6) }}
-        </div>
-        <div class="form-actions">
-          <button type="submit" class="btn btn-primary">{% trans %}Reset Password{% endtrans %}</button>
-        </div>
-      </form>
-
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Request New Password')) }}{% endblock %}
+
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="form-container">
+
+      <div class="form-header">
+        <h1>{% trans %}Request New Password{% endtrans %}</h1>
+      </div>
+
+      {% if message %}
+      <div class="messages-list">
+        {{ macros.draw_message(message) }}
+      </div>
+      {% endif %}
+
+      <form action="{{ url('forgot_password') }}" method="post">
+        {{ form_theme.hidden_fields(form) }}
+        <div class="form-fields">
+          {{ form_theme.row(form.email, attrs={'class': 'span6', 'placeholder': _("Enter your e-mail address.")}) }}
+          {{ form_theme.captcha(form, width=6) }}
+        </div>
+        <div class="form-actions">
+          <button type="submit" class="btn btn-primary">{% trans %}Reset Password{% endtrans %}</button>
+        </div>
+      </form>
+
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 5 - 5
templates/cranefly/search/error.html

@@ -1,6 +1,6 @@
-{% extends "cranefly/search/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block action %}
-<p class="lead">{{ message }}</p>
+{% extends "cranefly/search/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block action %}
+<p class="lead">{{ message }}</p>
 {% endblock %}
 {% endblock %}

+ 16 - 16
templates/cranefly/search/home.html

@@ -1,17 +1,17 @@
-{% extends "cranefly/search/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block action %}
-{% if search_result %}
-<div class="search-resume">
-  <p class="lead muted">{% trans search_query=style_query(search_result.search_query) %}Your search results for query "{{ search_query }}" are still available.{% endtrans %}</p>
-  <p class="lead muted">{% trans %}To discard it and start new search, simply perform new search using form above.{% endtrans %}</p>
-</div>
-{% else %}
-<p class="lead">{% trans %}To search forums, enter phrases you want to find in text field above and press search button.{% endtrans %}</p>
-{% endif %}
-{% endblock %}
-
-{% macro style_query(query) -%}
-<a href="{{ url('search_results') }}">{{ query }}</a>
+{% extends "cranefly/search/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block action %}
+{% if search_result %}
+<div class="search-resume">
+  <p class="lead muted">{% trans search_query=style_query(search_result.search_query) %}Your search results for query "{{ search_query }}" are still available.{% endtrans %}</p>
+  <p class="lead muted">{% trans %}To discard it and start new search, simply perform new search using form above.{% endtrans %}</p>
+</div>
+{% else %}
+<p class="lead">{% trans %}To search forums, enter phrases you want to find in text field above and press search button.{% endtrans %}</p>
+{% endif %}
+{% endblock %}
+
+{% macro style_query(query) -%}
+<a href="{{ url('search_results') }}">{{ query }}</a>
 {%- endmacro %}
 {%- endmacro %}

+ 16 - 16
templates/cranefly/search/layout.html

@@ -1,17 +1,17 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-{% import "_forms.html" as forms with context %}
-
-{% block title %}{{ macros.page_title(title=_('Search Community')) }}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary header-search">
-  <div class="container">
-    <h1>{% trans %}Search Community{% endtrans %}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% block action %}{% endblock %}
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+{% import "_forms.html" as forms with context %}
+
+{% block title %}{{ macros.page_title(title=_('Search Community')) }}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary header-search">
+  <div class="container">
+    <h1>{% trans %}Search Community{% endtrans %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% block action %}{% endblock %}
+</div>
 {% endblock %}
 {% endblock %}

+ 56 - 56
templates/cranefly/search/results.html

@@ -1,57 +1,57 @@
-{% extends "cranefly/search/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_("Results"),parent=_('Search Community')) }}{% endblock %}
-
-{% block action %}
-  <div class="search-results">
-    {% if results %}
-    <h2>{% trans count=results|length, results=results|length|intcomma %}Search has returned one result:{% pluralize %}Search has returned {{ results }} results:{% endtrans %}</h2>
-    <div class="results-list">
-      {% for result in results %}
-      <div class="result">
-        <div class="result-avatar">
-          {% if result.user_id %}
-          <a href="{{ url('user', user=result.user.pk, username=result.user.username_slug) }}"><img src="{{ result.user.get_avatar(70) }}" alt=""></a>
-          {% else %}
-          <img src="{{ macros.avatar_guest(80) }}" alt="{{ result.user_name }}">
-          {% endif %}
-        </div>
-        <div class="result-content">
-          <h3><a href="{{ url(result.forum.thread_link('find'), thread=result.thread_id, slug=result.thread.slug, post=result.pk) }}">{{ result.thread.name }}</a></h3>
-          <p class="post-preview">{{ result.post_clean|highlight(search_query, 320)|safe }}</p>
-          <p class="post-extra">{% trans forum=forum(result.forum), user=username(result), date=result.date|reltimesince|low %}In {{ forum }} by {{ user }} {{ date }}{% endtrans %}</p>
-        </div>
-      </div>
-      {% endfor %}
-    </div>
-    {{ pager() }}
-    {% else %}
-    <p class="lead">{% trans %}Looks like your search has expired. Please try searching again.{% endtrans %}</p>
-    {% endif %}
-  </div>
-{% endblock %}
-
-{% macro forum(forum) -%}
-<a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" class="forum-link">{{ forum.name }}</a>
-{%- endmacro %}
-
-{% macro username(post) -%}
-{% if post.user_id -%}
-<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="user-link">{{ post.user.username }}</a>
-{%- else -%}
-{{ post.user_name }}
-{%- endif %}
-{%- endmacro %}
-
-{% macro pager() -%}
-{% if items_total > 0 and pagination['total'] > 1 %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('search_results', page=pagination['prev']) }}{% else %}{{ url('search_results') }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('search_results', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
+{% extends "cranefly/search/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_("Results"),parent=_('Search Community')) }}{% endblock %}
+
+{% block action %}
+  <div class="search-results">
+    {% if results %}
+    <h2>{% trans count=results|length, results=results|length|intcomma %}Search has returned one result:{% pluralize %}Search has returned {{ results }} results:{% endtrans %}</h2>
+    <div class="results-list">
+      {% for result in results %}
+      <div class="result">
+        <div class="result-avatar">
+          {% if result.user_id %}
+          <a href="{{ url('user', user=result.user.pk, username=result.user.username_slug) }}"><img src="{{ result.user.get_avatar(70) }}" alt=""></a>
+          {% else %}
+          <img src="{{ macros.avatar_guest(80) }}" alt="{{ result.user_name }}">
+          {% endif %}
+        </div>
+        <div class="result-content">
+          <h3><a href="{{ url(result.forum.thread_link('find'), thread=result.thread_id, slug=result.thread.slug, post=result.pk) }}">{{ result.thread.name }}</a></h3>
+          <p class="post-preview">{{ result.post_clean|highlight(search_query, 320)|safe }}</p>
+          <p class="post-extra">{% trans forum=forum(result.forum), user=username(result), date=result.date|reltimesince|low %}In {{ forum }} by {{ user }} {{ date }}{% endtrans %}</p>
+        </div>
+      </div>
+      {% endfor %}
+    </div>
+    {{ pager() }}
+    {% else %}
+    <p class="lead">{% trans %}Looks like your search has expired. Please try searching again.{% endtrans %}</p>
+    {% endif %}
+  </div>
+{% endblock %}
+
+{% macro forum(forum) -%}
+<a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" class="forum-link">{{ forum.name }}</a>
+{%- endmacro %}
+
+{% macro username(post) -%}
+{% if post.user_id -%}
+<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="user-link">{{ post.user.username }}</a>
+{%- else -%}
+{{ post.user_name }}
+{%- endif %}
+{%- endmacro %}
+
+{% macro pager() -%}
+{% if items_total > 0 and pagination['total'] > 1 %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('search_results', page=pagination['prev']) }}{% else %}{{ url('search_results') }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('search_results', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
 {%- endmacro %}
 {%- endmacro %}

+ 65 - 65
templates/cranefly/signin.html

@@ -1,66 +1,66 @@
-{% extends "cranefly/layout.html" %}
-{% import "forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Sign In')) }}{% endblock %}
-     
-{% block content %}
-<div class="row">
-  <div class="span6 offset3">
-    <div class="form-container container-horizontal">
-
-      <div class="form-header">
-        <h1>{% trans %}Sign In to Your Account{% endtrans %}</h1>
-      </div>
-
-      {% if message %}
-      <div class="messages-list">
-        <div class="alert alert-{{ message.type }}">
-          {{ macros.draw_message_icon(message) }}
-          <p><strong>{{ message.message }}</strong></p>
-          {% if bad_password %}
-          <p><a href="{{ url('forgot_password') }}">{% trans %}Click here if you forgot your sign in credentials.{% endtrans %}</a></p>
-          {% endif %}
-          {% if not_active %}
-          <p><a href="{{ url('send_activation') }}">{% trans %}Click here if you didn't receive activation e-mail.{% endtrans %}</a></p>
-          {% endif %}
-          {% if banned_account.reason_user %}
-          {{ banned_account.reason_user|markdown|safe }}
-          {% endif %}
-          {% if banned_account.expires %}
-          <p><strong>{% trans ban_expires=banned_account.expires|date %}Your ban will expire on {{ ban_expires }}{% endtrans %}</p>
-          {% endif %}
-        </div>
-      </div>
-      {% endif %}
-
-      <form class="form-horizontal" action="{{ url('sign_in') }}" method="post">
-        <div class="form-fields">
-          {{ form_theme.hidden_fields(form) }}
-          {{ form_theme.row(form.user_email, attrs={'class': 'span4', 'placeholder': _("Enter your e-mail")}) }}
-          {{ form_theme.row(form.user_password, attrs={'class': 'span4', 'placeholder': _("Enter your password")}) }}
-          {% if 'user_remember_me' in form.fields %}
-          {{ form_theme.row(form.user_remember_me) }}
-          {% endif %}
-        </div>
-        <div class="form-actions">
-          <button type="submit" class="btn btn-primary">{% trans %}Sign In{% endtrans %}</button>
-          <div class="signin-help">
-
-            <h2>{% trans %}Problems Signing In?{% endtrans %}</h2>
-            <ul class="unstyled">
-              <li><a href="{{ url('forgot_password') }}"><i class="icon-refresh"></i> {% trans %}I don't remember my password{% endtrans %}</a></li>
-              <li><a href="{{ url('send_activation') }}"><i class="icon-envelope"></i> {% trans %}I haven't received activation e-mail{% endtrans %}</a></li>
-              {% if settings.account_activation != 'block' %}
-              <li><a href="{{ url('register') }}"><i class="icon-edit"></i> {% trans %}I don't have account{% endtrans %}</a></li>
-              {% endif %}
-            </ul>
-
-          </div>
-        </div>
-      </form>
-
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Sign In')) }}{% endblock %}
+     
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="form-container container-horizontal">
+
+      <div class="form-header">
+        <h1>{% trans %}Sign In to Your Account{% endtrans %}</h1>
+      </div>
+
+      {% if message %}
+      <div class="messages-list">
+        <div class="alert alert-{{ message.type }}">
+          {{ macros.draw_message_icon(message) }}
+          <p><strong>{{ message.message }}</strong></p>
+          {% if bad_password %}
+          <p><a href="{{ url('forgot_password') }}">{% trans %}Click here if you forgot your sign in credentials.{% endtrans %}</a></p>
+          {% endif %}
+          {% if not_active %}
+          <p><a href="{{ url('send_activation') }}">{% trans %}Click here if you didn't receive activation e-mail.{% endtrans %}</a></p>
+          {% endif %}
+          {% if banned_account.reason_user %}
+          {{ banned_account.reason_user|markdown|safe }}
+          {% endif %}
+          {% if banned_account.expires %}
+          <p><strong>{% trans ban_expires=banned_account.expires|date %}Your ban will expire on {{ ban_expires }}{% endtrans %}</p>
+          {% endif %}
+        </div>
+      </div>
+      {% endif %}
+
+      <form class="form-horizontal" action="{{ url('sign_in') }}" method="post">
+        <div class="form-fields">
+          {{ form_theme.hidden_fields(form) }}
+          {{ form_theme.row(form.user_email, attrs={'class': 'span4', 'placeholder': _("Enter your e-mail")}) }}
+          {{ form_theme.row(form.user_password, attrs={'class': 'span4', 'placeholder': _("Enter your password")}) }}
+          {% if 'user_remember_me' in form.fields %}
+          {{ form_theme.row(form.user_remember_me) }}
+          {% endif %}
+        </div>
+        <div class="form-actions">
+          <button type="submit" class="btn btn-primary">{% trans %}Sign In{% endtrans %}</button>
+          <div class="signin-help">
+
+            <h2>{% trans %}Problems Signing In?{% endtrans %}</h2>
+            <ul class="unstyled">
+              <li><a href="{{ url('forgot_password') }}"><i class="icon-refresh"></i> {% trans %}I don't remember my password{% endtrans %}</a></li>
+              <li><a href="{{ url('send_activation') }}"><i class="icon-envelope"></i> {% trans %}I haven't received activation e-mail{% endtrans %}</a></li>
+              {% if settings.account_activation != 'block' %}
+              <li><a href="{{ url('register') }}"><i class="icon-edit"></i> {% trans %}I don't have account{% endtrans %}</a></li>
+              {% endif %}
+            </ul>
+
+          </div>
+        </div>
+      </form>
+
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 80 - 80
templates/cranefly/threads/changelog.html

@@ -1,81 +1,81 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %} <small>{{ thread.name }}</small></h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
-      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="post-changelog">
-    {% if edits %}
-    <table class="table table-striped">
-      <thead>
-        <tr>
-          <th style="width: 1%;">&nbsp;</th>
-          <th>{% trans %}Change Log{% endtrans %}</th>
-        </tr>
-      </thead>
-      <tbody>
-        {% for edit in edits %}
-        <tr>
-          <td>
-            <span class="change-{% if edit.change > 0 %}added{% elif edit.change < 0 %}removed{% else %}none{% endif %}{% if not edit.reason %} change-small{% endif %}">
-              {% if edit.change > 0 %}+{% endif %}{{ edit.change }}
-            </span>
-          </td>
-          <td>
-            <a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}" class="change-no">#{{ loop.revindex }}</a>
-            {% if edit.reason %}
-            <div class="change-reason">
-              <a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">{{ edit.reason }}</a>
-            </div>{% endif %}
-            <div class="change-description">
-              <a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">
-              {% if edit.change != 0 %}{% if edit.change > 0 -%}
-              {% trans chars=edit.change %}Added one character to post.{% pluralize %}Added {{ chars }} characters to post.{% endtrans %}
-              {%- elif edit.change < 0 -%}
-              {% trans chars=edit.change|abs %}Removed one character from post.{% pluralize %}Removed {{ chars }} characters from post.{% endtrans %}
-              {%- else -%}
-              {% trans %}No change in message's length.{% endtrans %}
-              {%- endif %}{% endif %}{% if edit.thread_name_old %} {% trans old=edit.thread_name_old, new=edit.thread_name_new %}Changed thread name from "{{ old }}" to "{{ new }}".{% endtrans %}{% endif %}</a>
-              <span class="change-details">
-                {% trans user=edit_user(edit), date=edit.date|reldate|low %}By {{ user }} {{ date }}{% endtrans %}
-              </span>
-            </div>
-          </td>
-        </tr>
-        {% endfor %}
-      </tbody>
-    </table>
-    {% else %}
-    <p class="lead">{% trans %}This post was never edited.{% endtrans %}</p>
-    {% endif %}
-  </div>
-</div>
-{% endblock %}
-
-
-{% macro edit_user(edit) -%}
-{% if edit.user_id %}<a href="{{ url('user', user=edit.user_id, username=edit.user_slug) }}">{{ edit.user_name }}</a>{% else %}{{ edit.user_name }}{% endif %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %} <small>{{ thread.name }}</small></h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
+      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="post-changelog">
+    {% if edits %}
+    <table class="table table-striped">
+      <thead>
+        <tr>
+          <th style="width: 1%;">&nbsp;</th>
+          <th>{% trans %}Change Log{% endtrans %}</th>
+        </tr>
+      </thead>
+      <tbody>
+        {% for edit in edits %}
+        <tr>
+          <td>
+            <span class="change-{% if edit.change > 0 %}added{% elif edit.change < 0 %}removed{% else %}none{% endif %}{% if not edit.reason %} change-small{% endif %}">
+              {% if edit.change > 0 %}+{% endif %}{{ edit.change }}
+            </span>
+          </td>
+          <td>
+            <a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}" class="change-no">#{{ loop.revindex }}</a>
+            {% if edit.reason %}
+            <div class="change-reason">
+              <a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">{{ edit.reason }}</a>
+            </div>{% endif %}
+            <div class="change-description">
+              <a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=edit.pk) }}">
+              {% if edit.change != 0 %}{% if edit.change > 0 -%}
+              {% trans chars=edit.change %}Added one character to post.{% pluralize %}Added {{ chars }} characters to post.{% endtrans %}
+              {%- elif edit.change < 0 -%}
+              {% trans chars=edit.change|abs %}Removed one character from post.{% pluralize %}Removed {{ chars }} characters from post.{% endtrans %}
+              {%- else -%}
+              {% trans %}No change in message's length.{% endtrans %}
+              {%- endif %}{% endif %}{% if edit.thread_name_old %} {% trans old=edit.thread_name_old, new=edit.thread_name_new %}Changed thread name from "{{ old }}" to "{{ new }}".{% endtrans %}{% endif %}</a>
+              <span class="change-details">
+                {% trans user=edit_user(edit), date=edit.date|reldate|low %}By {{ user }} {{ date }}{% endtrans %}
+              </span>
+            </div>
+          </td>
+        </tr>
+        {% endfor %}
+      </tbody>
+    </table>
+    {% else %}
+    <p class="lead">{% trans %}This post was never edited.{% endtrans %}</p>
+    {% endif %}
+  </div>
+</div>
+{% endblock %}
+
+
+{% macro edit_user(edit) -%}
+{% if edit.user_id %}<a href="{{ url('user', user=edit.user_id, username=edit.user_slug) }}">{{ edit.user_name }}</a>{% else %}{{ edit.user_name }}{% endif %}
 {%- endmacro %}
 {%- endmacro %}

+ 95 - 95
templates/cranefly/threads/changelog_diff.html

@@ -1,95 +1,95 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li><a href="{{ url('thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %} <small>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</small></h1>
-    <ul class="unstyled header-stats pull-left">
-      <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if change.user_id %}<a href="{{ url('user', user=change.user_id, username=change.user_slug) }}">{{ change.user_name }}</a>{% else %}{{ change.user_name }}{% endif %}</li>
-      {% if acl.users.can_see_users_trails() %}
-      <li><i class="icon-globe"></i> {{ change.ip }}</li>
-      <li><i class="icon-qrcode"></i> {{ change.agent }}</li>
-      {% endif %}
-      {% if change.change != 0 %}<li><i class="icon-{% if change.change > 0 %}plus{% elif change.change < 0 %}minus{% endif %}"></i> {% if change.change > 0 -%}
-      {% trans chars=change.change %}Added one character{% pluralize %}Added {{ chars }} characters{% endtrans %}
-      {%- elif change.change < 0 -%}
-      {% trans chars=change.change|abs %}Removed one character{% pluralize %}Removed {{ chars }} characters{% endtrans %}
-      {%- endif %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="post-diff">
-    {% if message %}
-    <div class="messages-list">
-      {{ macros.draw_message(message) }}
-    </div>
-    {% endif %}
-
-    {% if change.reason %}
-    <p class="lead">{{ change.reason }}</p>
-    {% endif %}
-
-    {% if acl.threads.can_edit_reply(user, forum, thread, post) or prev or next %}
-    <div class="diff-extra">
-      {{ pager() }}
-      {% if user.is_authenticated() and acl.threads.can_make_revert(forum, thread) %}
-      <form class="form-inline pull-right" action="{{ url('thread_changelog_revert', thread=thread.pk, slug=thread.slug, post=post.pk, change=change.pk) }}" method="post">
-        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-        <button type="submit" class="btn btn-danger">{% trans %}Revert this edit{% endtrans %}</button></li>
-      </form>
-      {%- endif %}
-    </div>
-    {% endif %}
-
-    <div class="post-diff-details">
-      <table>
-        <tbody>
-          {% for line in diff %}{% if line[0] != "?" %}
-          <tr>
-            <td class="line"><a href="#{{ l }}">{{ l }}.</a></td>
-            <td class="{% if line[0] == '+' %}added{% elif line[0] == '-' %}removed{% else %}stag{% endif %}{% if l is even %} even{% endif %}">{% if line[2:] %}{{ line[2:] }}{% else %}&nbsp;{% endif %}</td>
-          </tr>
-          {% set l = l + 1 %}
-          {% endif %}{% endfor %}
-        </tbody>
-      </table>
-    </div>
-
-    {% if prev or next %}
-    <div class="diff-extra">
-      {{ pager() }}
-    </div>
-    {% endif %}
-
-  </div>
-</div>
-{% endblock %}
-
-
-{% macro pager() %}
-{% if prev or prev %}
-<div class="pagination pull-left">
-  <ul>
-    {% if prev %}<li><a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=prev.pk) }}"><i class="icon-chevron-left"></i> {{ prev.date|reldate }}</a></li>{% endif %}
-    {% if next %}<li><a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=next.pk) }}">{{ next.date|reldate }} <i class="icon-chevron-right"></i></a></li>{% endif %}
-  </ul>
-</div>
-{% endif %}
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Changelog") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li><a href="{{ url('thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans date=change.date|reltimesince|low %}Edit from {{ date }}{% endtrans %} <small>{% trans post=post.pk %}Post #{{ post }} Changelog{% endtrans %}</small></h1>
+    <ul class="unstyled header-stats pull-left">
+      <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if change.user_id %}<a href="{{ url('user', user=change.user_id, username=change.user_slug) }}">{{ change.user_name }}</a>{% else %}{{ change.user_name }}{% endif %}</li>
+      {% if acl.users.can_see_users_trails() %}
+      <li><i class="icon-globe"></i> {{ change.ip }}</li>
+      <li><i class="icon-qrcode"></i> {{ change.agent }}</li>
+      {% endif %}
+      {% if change.change != 0 %}<li><i class="icon-{% if change.change > 0 %}plus{% elif change.change < 0 %}minus{% endif %}"></i> {% if change.change > 0 -%}
+      {% trans chars=change.change %}Added one character{% pluralize %}Added {{ chars }} characters{% endtrans %}
+      {%- elif change.change < 0 -%}
+      {% trans chars=change.change|abs %}Removed one character{% pluralize %}Removed {{ chars }} characters{% endtrans %}
+      {%- endif %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="post-diff">
+    {% if message %}
+    <div class="messages-list">
+      {{ macros.draw_message(message) }}
+    </div>
+    {% endif %}
+
+    {% if change.reason %}
+    <p class="lead">{{ change.reason }}</p>
+    {% endif %}
+
+    {% if acl.threads.can_edit_reply(user, forum, thread, post) or prev or next %}
+    <div class="diff-extra">
+      {{ pager() }}
+      {% if user.is_authenticated() and acl.threads.can_make_revert(forum, thread) %}
+      <form class="form-inline pull-right" action="{{ url('thread_changelog_revert', thread=thread.pk, slug=thread.slug, post=post.pk, change=change.pk) }}" method="post">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        <button type="submit" class="btn btn-danger">{% trans %}Revert this edit{% endtrans %}</button></li>
+      </form>
+      {%- endif %}
+    </div>
+    {% endif %}
+
+    <div class="post-diff-details">
+      <table>
+        <tbody>
+          {% for line in diff %}{% if line[0] != "?" %}
+          <tr>
+            <td class="line"><a href="#{{ l }}">{{ l }}.</a></td>
+            <td class="{% if line[0] == '+' %}added{% elif line[0] == '-' %}removed{% else %}stag{% endif %}{% if l is even %} even{% endif %}">{% if line[2:] %}{{ line[2:] }}{% else %}&nbsp;{% endif %}</td>
+          </tr>
+          {% set l = l + 1 %}
+          {% endif %}{% endfor %}
+        </tbody>
+      </table>
+    </div>
+
+    {% if prev or next %}
+    <div class="diff-extra">
+      {{ pager() }}
+    </div>
+    {% endif %}
+
+  </div>
+</div>
+{% endblock %}
+
+
+{% macro pager() %}
+{% if prev or prev %}
+<div class="pagination pull-left">
+  <ul>
+    {% if prev %}<li><a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=prev.pk) }}"><i class="icon-chevron-left"></i> {{ prev.date|reldate }}</a></li>{% endif %}
+    {% if next %}<li><a href="{{ url('thread_changelog_diff', thread=thread.pk, slug=thread.slug, post=post.pk, change=next.pk) }}">{{ next.date|reldate }} <i class="icon-chevron-right"></i></a></li>{% endif %}
+  </ul>
+</div>
+{% endif %}
+{% endmacro %}

+ 34 - 34
templates/cranefly/threads/details.html

@@ -1,35 +1,35 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Info") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %} <small>{{ thread.name }}</small></h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
-      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <h2>{% trans %}IP Address{% endtrans %}</h2>
-  <p class="lead">{{ post.ip }}</p>
-  <h2>{% trans %}UserAgent{% endtrans %}</h2>
-  <p class="lead">{{ post.agent }}</p>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Info") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans post=post.pk %}Post #{{ post }} Info{% endtrans %} <small>{{ thread.name }}</small></h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+      <li><i class="icon-pencil"></i> {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</li>
+      {% if post.protected %}<li><i class="icon-lock"></i> {% trans %}Protected{% endtrans %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <h2>{% trans %}IP Address{% endtrans %}</h2>
+  <p class="lead">{{ post.ip }}</p>
+  <h2>{% trans %}UserAgent{% endtrans %}</h2>
+  <p class="lead">{{ post.agent }}</p>
+</div>
 {% endblock %}
 {% endblock %}

+ 116 - 116
templates/cranefly/threads/karmas.html

@@ -1,116 +1,116 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Votes") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans post=post.pk %}Post #{{ post }} Votes{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans post=post.pk %}Post #{{ post }} Votes{% endtrans %} <small>{{ thread.name }}</small></h1>
-    <ul class="unstyled header-stats">
-      <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-      <li><i class="icon-thumbs-up"></i> {{ post.upvotes }}</li>
-      <li><i class="icon-thumbs-down"></i> {{ post.downvotes }}</li>
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="post-votes-list">
-    <div class="post-likes">
-      <h2>{% trans count=upvotes|length, votes=upvotes|length|intcomma -%}
-        One like
-        {%- pluralize -%}
-        {{ votes }} likes
-        {%- endtrans %}</h2>
-      {% if upvotes %}
-      <table class="table table-striped">
-        <tbody>
-          {% for row in upvotes|batch(4, '') %}
-          <tr>
-            {% for vote in row %}
-            <td class="span3">
-              {% if vote %}
-              {{ vote_details(vote, 'thumbs-up') }}
-              {% else %}
-              &nbsp;
-              {% endif %}
-            </td>
-            {% endfor %}
-          </tr>
-          {% endfor %}
-        </tbody>
-      </table>
-      {% else %}
-      <p class="lead">{% trans %}Nobody liked this post.{% endtrans %}</p>
-      {% endif %}
-    </div>
-
-    <hr>
-
-    <div class="post-dislikes">
-      <h2>{% trans count=downvotes|length, votes=downvotes|length|intcomma -%}
-        One dislike
-        {%- pluralize -%}
-        {{ votes }} dislikes
-        {%- endtrans %}</h2>
-      {% if downvotes %}
-      <table class="table table-striped">
-        <tbody>
-          {% for row in downvotes|batch(4, '') %}
-          <tr>
-            {% for vote in row %}
-            <td class="span3">
-              {% if vote %}
-              {{ vote_details(vote, 'thumbs-down') }}
-              {% else %}
-              &nbsp;
-              {% endif %}
-            </td>
-            {% endfor %}
-          </tr>
-          {% endfor %}
-        </tbody>
-      </table>
-      {% else %}
-      <p class="lead">{% trans %}Nobody disliked this post.{% endtrans %}</p>
-      {% endif %}
-    </div>
-  </div>
-</div>
-{% endblock %}
-
-{% block javascripts %}{{ super() }}
-  <script type="text/javascript">
-    $(function() {
-      {% for vote in (upvotes|list + downvotes|list) %}
-      $('.vote-{{ vote.id }}').popover({
-        'placement': 'top',
-        'trigger': 'hover',
-        'html': true,
-        {% if acl.users.can_see_users_trails() %}
-        'title': '<strong>{{ vote.date|reldate }}</strong>',
-        'content': '{% trans ip=vote.ip %}From {{ ip }}{% endtrans %}'
-        {% else %}
-        'content': '<strong>{{ vote.ip }}</strong>'
-        {% endif %}
-      });
-      {% endfor %}
-    });
-  </script>
-{% endblock %}
-
-{% macro vote_details(vote, icon) %}
-{% if vote.user_id %}<a href="{{ url('user', user=vote.user_id, username=vote.user_slug) }}" class="vote-user vote-{{ vote.pk }}"><span class="vote-icon"><i class="icon-{{ icon }}"></i></span> {{ vote.user_name }}</a>{% else %}<span class="vote-user vote-{{ vote.pk }}"><span class="vote-icon"><i class="icon-{{ icon }}"></i></span> {{ vote.user_name }}</span>{% endif %}
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=(_("Post #%(post)s Votes") % {'post': post.pk}),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans post=post.pk %}Post #{{ post }} Votes{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans post=post.pk %}Post #{{ post }} Votes{% endtrans %} <small>{{ thread.name }}</small></h1>
+    <ul class="unstyled header-stats">
+      <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+      <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+      <li><i class="icon-thumbs-up"></i> {{ post.upvotes }}</li>
+      <li><i class="icon-thumbs-down"></i> {{ post.downvotes }}</li>
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="post-votes-list">
+    <div class="post-likes">
+      <h2>{% trans count=upvotes|length, votes=upvotes|length|intcomma -%}
+        One like
+        {%- pluralize -%}
+        {{ votes }} likes
+        {%- endtrans %}</h2>
+      {% if upvotes %}
+      <table class="table table-striped">
+        <tbody>
+          {% for row in upvotes|batch(4, '') %}
+          <tr>
+            {% for vote in row %}
+            <td class="span3">
+              {% if vote %}
+              {{ vote_details(vote, 'thumbs-up') }}
+              {% else %}
+              &nbsp;
+              {% endif %}
+            </td>
+            {% endfor %}
+          </tr>
+          {% endfor %}
+        </tbody>
+      </table>
+      {% else %}
+      <p class="lead">{% trans %}Nobody liked this post.{% endtrans %}</p>
+      {% endif %}
+    </div>
+
+    <hr>
+
+    <div class="post-dislikes">
+      <h2>{% trans count=downvotes|length, votes=downvotes|length|intcomma -%}
+        One dislike
+        {%- pluralize -%}
+        {{ votes }} dislikes
+        {%- endtrans %}</h2>
+      {% if downvotes %}
+      <table class="table table-striped">
+        <tbody>
+          {% for row in downvotes|batch(4, '') %}
+          <tr>
+            {% for vote in row %}
+            <td class="span3">
+              {% if vote %}
+              {{ vote_details(vote, 'thumbs-down') }}
+              {% else %}
+              &nbsp;
+              {% endif %}
+            </td>
+            {% endfor %}
+          </tr>
+          {% endfor %}
+        </tbody>
+      </table>
+      {% else %}
+      <p class="lead">{% trans %}Nobody disliked this post.{% endtrans %}</p>
+      {% endif %}
+    </div>
+  </div>
+</div>
+{% endblock %}
+
+{% block javascripts %}{{ super() }}
+  <script type="text/javascript">
+    $(function() {
+      {% for vote in (upvotes|list + downvotes|list) %}
+      $('.vote-{{ vote.id }}').popover({
+        'placement': 'top',
+        'trigger': 'hover',
+        'html': true,
+        {% if acl.users.can_see_users_trails() %}
+        'title': '<strong>{{ vote.date|reldate }}</strong>',
+        'content': '{% trans ip=vote.ip %}From {{ ip }}{% endtrans %}'
+        {% else %}
+        'content': '<strong>{{ vote.ip }}</strong>'
+        {% endif %}
+      });
+      {% endfor %}
+    });
+  </script>
+{% endblock %}
+
+{% macro vote_details(vote, icon) %}
+{% if vote.user_id %}<a href="{{ url('user', user=vote.user_id, username=vote.user_slug) }}" class="vote-user vote-{{ vote.pk }}"><span class="vote-icon"><i class="icon-{{ icon }}"></i></span> {{ vote.user_name }}</a>{% else %}<span class="vote-user vote-{{ vote.pk }}"><span class="vote-icon"><i class="icon-{{ icon }}"></i></span> {{ vote.user_name }}</span>{% endif %}
+{% endmacro %}

+ 289 - 289
templates/cranefly/threads/list.html

@@ -1,289 +1,289 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=forum.name,page=pagination['page']) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li class="active">{{ forum.name }}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{{ forum.name }}</h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-
-  {% if forum.description %}
-  <div class="markdown lead page-description">
-    {{ forum.description_preparsed|markdown_final|safe }}
-  </div>
-  {% endif %}
-
-  {% if forum.subforums %}
-  <div id="subforums" class="forum-subforums-list{% if forum.style %} forum-subforums-{{ forum.style }}{% endif %}">
-    <div class="header">
-      <h2>{% trans %}Child forums{% endtrans %}</h2>
-    </div>
-    {% for subforum in forum.subforums %}
-    <div class="forum{% if loop.last %} last{% endif %}">
-      <div class="forum-icon">
-        <div class="forum-icon-wrap{% if subforum.type == 'redirect' %} forum-icon-redirect{% elif not subforum.is_read %} forum-icon-new{% endif %}"><i class="icon-{% if subforum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %} icon-white"></i></div>
-      </div>
-      <div id="forum-{{ subforum.id }}" class="forum-main">
-        <h3 class="forum-title{% if not subforum.is_read %} forum-title-new{% endif %}"><a href="{{ url(subforum.type, slug=subforum.slug, forum=subforum.id) }}">{{ subforum.name }}</a></h3>
-        {% if subforum.show_details %}
-        <div class="forum-details">
-          {% if subforum.type != 'redirect' %}
-          {% if acl.forums.can_browse(subforum) and (acl.threads.can_read_threads(subforum) == 2 or (acl.threads.can_read_threads(subforum) == 1 and subforum.last_poster_id == user.pk)) %}
-          {% if subforum.last_thread_id -%}
-          <div class="thread-name">
-            <a href="{{ url('thread_new', thread=subforum.last_thread_id, slug=subforum.last_thread_slug) }}"{% if subforum.last_thread_name|length > 34 %} class="tooltip-top" title="{{ subforum.last_thread_name }}"{% endif %}>{{ subforum.last_thread_name|short_string(34) }}</a>
-          </div>
-          <div class="muted">{% if subforum.last_poster_id %}<a href="{{ url('user', user=subforum.last_poster_id, username=subforum.last_poster_slug) }}" class="last-poster">{{ subforum.last_poster_name }}</a>{% else %}<span class="last-poster">{{ subforum.last_poster_name }}</span>{% endif %} - {{ subforum.last_thread_date|reltimesince }}</div>
-          {%- else -%}
-          <em>{% trans %}This forum is empty{% endtrans %}</em>
-          {%- endif %}
-          {%- else -%}
-          <em>{% trans %}This forum is protected{% endtrans %}</em>
-          {%- endif %}
-          {%- else -%}
-          <div class="thread-name">
-            <a href="{{ url('redirect', slug=subforum.slug, forum=subforum.id) }}">{{ subforum.redirect_domain() }}</a>
-          </div>
-          <div class="muted">{% trans count=subforum.redirects, clicks=macros.wrap(subforum.redirects|intcomma, 'span', 'class="last-poster"') %}{{ clicks }} click{% pluralize %}{{ clicks }} clicks{% endtrans %}</div>
-          {%- endif %}
-        </div>
-        {% endif %}
-        {% if subforum.subforums %}
-        <div class="dropdown">
-          {% if subforum.subforums|length > 1 %}
-          <a href="{{ url(subforum.type, slug=subforum.slug, forum=subforum.id) }}#subforums" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-chevron-down"></i> {% trans %}Subforums{% endtrans %}</a>
-          <div class="dropdown-menu" role="menu" aria-labelledby="dLabel">
-            <div class="dropdown-shadow">
-              <ul>
-                {% for subsubforum in subforum.subforums %}
-                <li><a href="{{ url(subsubforum.type, slug=subsubforum.slug, forum=subsubforum.id) }}"><i class="icon-{% if subsubforum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %}"></i> {{ subsubforum.name }}</a></li>
-                {% endfor %}
-              </ul>
-            </div>
-          </div>
-          {% else %}
-          <a href="{{ url(subforum.subforums[0].type, slug=subforum.subforums[0].slug, forum=subforum.subforums[0].id) }}" class="subforum tooltip-top" title="{% trans subforum=subforum.subforums[0].name %}Go to the {{ subforum }} subforum{% endtrans %}">{{ subforum.subforums[0].name|short_string(16) }}</a>
-          {% endif %}
-        </div>
-        {% endif%}
-        <div class="hide forum-meta">
-          {% if subforum.description %}<p class="forum-description">{{ subforum.description }}</p>{% endif %}
-          <div class="forum-stats">
-            {% if subforum.type != 'redirect' %}
-            <span>{% trans %}Posts{% endtrans %}: <strong>{{ subforum.posts|intcomma }}</strong></span>
-            {% trans %}Threads{% endtrans %}: <strong>{{ subforum.threads|intcomma }}</strong>
-            {% else %}
-            {% trans %}Clicks{% endtrans %}: <strong>{{ subforum.redirects|intcomma }}</strong>
-            {% endif %}
-          </div>
-        </div>
-      </div>
-    </div>
-    {% endfor %}
-  </div>
-  {% endif %}
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <div class="forum-threads-extra extra-top">
-    {{ pager() }}
-    {% if user.is_authenticated() and acl.threads.can_start_threads(forum) %}
-    <a href="{{ url('thread_start', forum=forum.pk, slug=forum.slug) }}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
-    {% endif %}
-  </div>
-
-  <div class="forum-threads-list">
-    <div class="header">
-      <div class="row-fluid">
-        <div class="span7">{% trans %}Thread{% endtrans %}</div>
-        <div class="span5 thread-activity">
-          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
-          {% if user.is_authenticated() and list_form %}
-          <div class="pull-right check-cell">
-            <label class="checkbox"><input type="checkbox" class="checkbox-master"></label>
-          </div>
-          {% endif %}
-        </div>
-      </div>
-    </div>
-    {% for thread in threads %}
-    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
-      <div class="row-fluid">
-        <div class="span7">
-          {% if thread.is_read %}
-          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
-          {% else %}
-          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
-          {% endif %}
-
-          {{ macros.thread_flags(thread) }}
-          
-          <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="thread-name">{{ thread.name }}</a>
-          
-          <div class="thread-details">
-            {% trans user=thread_starter(thread), start=thread.start|reldate|low %}by {{ user }}, {{ start }}{% endtrans %}
-          </div>
-
-        </div>
-        <div class="span5 thread-activity">
-
-          {% if settings.avatars_on_threads_list %}
-          <div class="thread-last-avatar">
-            {% if thread.last_poster_id %}
-            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
-            {% else %}
-            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
-            {% endif %}
-          </div>
-          {% endif %}
-
-          <div class="thread-replies">
-            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
-            {{ replies(thread.replies) }}, <span{% if (thread.upvotes-thread.downvotes) > 0 %} class="text-success"{% elif (thread.upvotes-thread.downvotes) < 0 %} class="text-error"{% endif %}><strong>{% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}</strong>{% trans rating=(thread.upvotes-thread.downvotes)|abs|intcomma %}{{ rating }} thread rating{% endtrans %}</span>
-          </div>
-
-          {% if user.is_authenticated() and list_form %}
-          <label class="thread-select checkbox"><input form="threads_form" name="{{ list_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ thread.pk }}"{% if list_form['list_items']['has_value'] and ('' ~ thread.pk) in list_form['list_items']['value'] %} checked="checked"{% endif %}></label>
-          {% endif %}
-        </div>
-      </div>
-    </div>
-    {% else %}
-    <div class="thread-row threads-list-empty">
-      {% trans %}There are no threads in this forum.{% endtrans %}
-    </div>
-    {% endfor %}
-
-    {% if user.is_authenticated() and list_form %}
-    <div class="threads-actions">
-      <form id="threads_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
-        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-        {{ form_theme.input_select(list_form['list_action'],width=3) }}
-        <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-      </form>
-    </div>
-    {% endif %}
-  </div>
-
-  <div class="forum-threads-extra">
-    {{ pager() }}
-    {% if user.is_authenticated() and acl.threads.can_start_threads(forum) %}
-    <a href="{{ url('thread_start', forum=forum.pk, slug=forum.slug) }}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
-    {% elif not user.is_authenticated() and not user.is_crawler() %}
-    <p class="lead threads-signin-message"><a href="{{ url('sign_in') }}">{% trans %}Sign in or register to start threads.{% endtrans %}</a></p>
-    {% endif %}
-  </div>
-
-</div>
-{% endblock %}
-
-
-{% macro forum_stats(forum) -%}
-{% if forum.last_thread_id and not forum.attr('hidethread') -%}
-  {% trans count=forum.posts, posts=fancy_number(forum.posts, forum.posts_delta), thread=forum_thread(forum) -%}
-  {{ posts }} post - last in {{ thread }}
-  {%- pluralize -%}
-  {{ posts }} posts - last in {{ thread }}
-  {%- endtrans %}
-{%- else -%}
-  {% trans count=forum.posts, posts=fancy_number(forum.posts, forum.posts_delta) -%}
-  {{ posts }} post
-  {%- pluralize -%}
-  {{ posts }} posts
-  {%- endtrans %}
-{%- endif %}
-{%- endmacro %}
-
-{% macro forum_thread(forum) -%}
-<a href="{{ url('thread_new', thread=forum.last_thread_id, slug=forum.last_thread_slug) }}">{{ forum.last_thread_name }}</a>
-{%- endmacro %}
-
-{% macro redirect_stats(forum) -%}
-{% trans count=forum.redirects, redirects=fancy_number(forum.redirects, forum.redirects_delta) -%}
-{{ redirects }} click
-{%- pluralize -%}
-{{ redirects }} clicks
-{%- endtrans %}
-{%- endmacro %}
-
-{% macro fancy_number(number, delta) -%}
-<strong{% if delta < number %} class="stat-increment"{% endif %}>{{ number|intcomma }}</strong>
-{%- endmacro %}
-
-{% macro replies(thread_replies) -%}
-{% trans count=thread_replies, replies=thread_replies|intcomma -%}
-{{ replies }} reply
-{%- pluralize -%}
-{{ replies }} replies
-{%- endtrans %}
-{%- endmacro %}
-
-{% macro thread_starter(thread) -%}
-{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro thread_reply(thread) -%}
-{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro pager() %}
-{% if pagination['total'] > 0 %}
-<div class="pagination pull-left">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('forum', slug=forum.slug, forum=forum.id) }}" class="tooltip-top" title="{% trans %}First Page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('forum', slug=forum.slug, forum=forum.id, page=pagination['prev']) }}{% else %}{{ url('forum', slug=forum.slug, forum=forum.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Newest Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('forum', slug=forum.slug, forum=forum.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-  </ul>
-</div>
-{% endif %}
-{% endmacro %}
-
-{% block javascripts -%}{{ super() }}
-  <script type="text/javascript">
-    $(function () {
-      function populateForumTooltip(target) {
-        return $('#forum-' + target + ' .forum-meta').html();
-      };
-      {% for subforum in forum.subforums %}
-        $('#forum-{{ subforum.id }} .forum-title').tooltip({
-          template: '<div class="tooltip forum-meta-tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
-          placement: 'right',
-          html: true,
-          title: populateForumTooltip({{ subforum.id }})
-        });
-      {% endfor %}
-      {%- if user.is_authenticated() and list_form %}
-      $('#threads_form').submit(function() {
-        if ($('.thread-select[]:checked').length == 0) {
-          alert("{% trans %}You have to select at least one thread.{% endtrans %}");
-          return false;
-        }
-        if ($('#id_list_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete selected threads? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });{% endif %}
-    });
-  </script>
-{%- endblock %}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=forum.name,page=pagination['page']) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li class="active">{{ forum.name }}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{{ forum.name }}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+
+  {% if forum.description %}
+  <div class="markdown lead page-description">
+    {{ forum.description_preparsed|markdown_final|safe }}
+  </div>
+  {% endif %}
+
+  {% if forum.subforums %}
+  <div id="subforums" class="forum-subforums-list{% if forum.style %} forum-subforums-{{ forum.style }}{% endif %}">
+    <div class="header">
+      <h2>{% trans %}Child forums{% endtrans %}</h2>
+    </div>
+    {% for subforum in forum.subforums %}
+    <div class="forum{% if loop.last %} last{% endif %}">
+      <div class="forum-icon">
+        <div class="forum-icon-wrap{% if subforum.type == 'redirect' %} forum-icon-redirect{% elif not subforum.is_read %} forum-icon-new{% endif %}"><i class="icon-{% if subforum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %} icon-white"></i></div>
+      </div>
+      <div id="forum-{{ subforum.id }}" class="forum-main">
+        <h3 class="forum-title{% if not subforum.is_read %} forum-title-new{% endif %}"><a href="{{ url(subforum.type, slug=subforum.slug, forum=subforum.id) }}">{{ subforum.name }}</a></h3>
+        {% if subforum.show_details %}
+        <div class="forum-details">
+          {% if subforum.type != 'redirect' %}
+          {% if acl.forums.can_browse(subforum) and (acl.threads.can_read_threads(subforum) == 2 or (acl.threads.can_read_threads(subforum) == 1 and subforum.last_poster_id == user.pk)) %}
+          {% if subforum.last_thread_id -%}
+          <div class="thread-name">
+            <a href="{{ url('thread_new', thread=subforum.last_thread_id, slug=subforum.last_thread_slug) }}"{% if subforum.last_thread_name|length > 34 %} class="tooltip-top" title="{{ subforum.last_thread_name }}"{% endif %}>{{ subforum.last_thread_name|short_string(34) }}</a>
+          </div>
+          <div class="muted">{% if subforum.last_poster_id %}<a href="{{ url('user', user=subforum.last_poster_id, username=subforum.last_poster_slug) }}" class="last-poster">{{ subforum.last_poster_name }}</a>{% else %}<span class="last-poster">{{ subforum.last_poster_name }}</span>{% endif %} - {{ subforum.last_thread_date|reltimesince }}</div>
+          {%- else -%}
+          <em>{% trans %}This forum is empty{% endtrans %}</em>
+          {%- endif %}
+          {%- else -%}
+          <em>{% trans %}This forum is protected{% endtrans %}</em>
+          {%- endif %}
+          {%- else -%}
+          <div class="thread-name">
+            <a href="{{ url('redirect', slug=subforum.slug, forum=subforum.id) }}">{{ subforum.redirect_domain() }}</a>
+          </div>
+          <div class="muted">{% trans count=subforum.redirects, clicks=macros.wrap(subforum.redirects|intcomma, 'span', 'class="last-poster"') %}{{ clicks }} click{% pluralize %}{{ clicks }} clicks{% endtrans %}</div>
+          {%- endif %}
+        </div>
+        {% endif %}
+        {% if subforum.subforums %}
+        <div class="dropdown">
+          {% if subforum.subforums|length > 1 %}
+          <a href="{{ url(subforum.type, slug=subforum.slug, forum=subforum.id) }}#subforums" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-chevron-down"></i> {% trans %}Subforums{% endtrans %}</a>
+          <div class="dropdown-menu" role="menu" aria-labelledby="dLabel">
+            <div class="dropdown-shadow">
+              <ul>
+                {% for subsubforum in subforum.subforums %}
+                <li><a href="{{ url(subsubforum.type, slug=subsubforum.slug, forum=subsubforum.id) }}"><i class="icon-{% if subsubforum.type == 'redirect' %}circle-arrow-right{% else %}comment{% endif %}"></i> {{ subsubforum.name }}</a></li>
+                {% endfor %}
+              </ul>
+            </div>
+          </div>
+          {% else %}
+          <a href="{{ url(subforum.subforums[0].type, slug=subforum.subforums[0].slug, forum=subforum.subforums[0].id) }}" class="subforum tooltip-top" title="{% trans subforum=subforum.subforums[0].name %}Go to the {{ subforum }} subforum{% endtrans %}">{{ subforum.subforums[0].name|short_string(16) }}</a>
+          {% endif %}
+        </div>
+        {% endif%}
+        <div class="hide forum-meta">
+          {% if subforum.description %}<p class="forum-description">{{ subforum.description }}</p>{% endif %}
+          <div class="forum-stats">
+            {% if subforum.type != 'redirect' %}
+            <span>{% trans %}Posts{% endtrans %}: <strong>{{ subforum.posts|intcomma }}</strong></span>
+            {% trans %}Threads{% endtrans %}: <strong>{{ subforum.threads|intcomma }}</strong>
+            {% else %}
+            {% trans %}Clicks{% endtrans %}: <strong>{{ subforum.redirects|intcomma }}</strong>
+            {% endif %}
+          </div>
+        </div>
+      </div>
+    </div>
+    {% endfor %}
+  </div>
+  {% endif %}
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="forum-threads-extra extra-top">
+    {{ pager() }}
+    {% if user.is_authenticated() and acl.threads.can_start_threads(forum) %}
+    <a href="{{ url('thread_start', forum=forum.pk, slug=forum.slug) }}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
+    {% endif %}
+  </div>
+
+  <div class="forum-threads-list">
+    <div class="header">
+      <div class="row-fluid">
+        <div class="span7">{% trans %}Thread{% endtrans %}</div>
+        <div class="span5 thread-activity">
+          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
+          {% if user.is_authenticated() and list_form %}
+          <div class="pull-right check-cell">
+            <label class="checkbox"><input type="checkbox" class="checkbox-master"></label>
+          </div>
+          {% endif %}
+        </div>
+      </div>
+    </div>
+    {% for thread in threads %}
+    <div class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
+      <div class="row-fluid">
+        <div class="span7">
+          {% if thread.is_read %}
+          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
+          {% else %}
+          <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
+          {% endif %}
+
+          {{ macros.thread_flags(thread) }}
+          
+          <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="thread-name">{{ thread.name }}</a>
+          
+          <div class="thread-details">
+            {% trans user=thread_starter(thread), start=thread.start|reldate|low %}by {{ user }}, {{ start }}{% endtrans %}
+          </div>
+
+        </div>
+        <div class="span5 thread-activity">
+
+          {% if settings.avatars_on_threads_list %}
+          <div class="thread-last-avatar">
+            {% if thread.last_poster_id %}
+            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
+            {% else %}
+            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
+            {% endif %}
+          </div>
+          {% endif %}
+
+          <div class="thread-replies">
+            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
+            {{ replies(thread.replies) }}, <span{% if (thread.upvotes-thread.downvotes) > 0 %} class="text-success"{% elif (thread.upvotes-thread.downvotes) < 0 %} class="text-error"{% endif %}><strong>{% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}</strong>{% trans rating=(thread.upvotes-thread.downvotes)|abs|intcomma %}{{ rating }} thread rating{% endtrans %}</span>
+          </div>
+
+          {% if user.is_authenticated() and list_form %}
+          <label class="thread-select checkbox"><input form="threads_form" name="{{ list_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ thread.pk }}"{% if list_form['list_items']['has_value'] and ('' ~ thread.pk) in list_form['list_items']['value'] %} checked="checked"{% endif %}></label>
+          {% endif %}
+        </div>
+      </div>
+    </div>
+    {% else %}
+    <div class="thread-row threads-list-empty">
+      {% trans %}There are no threads in this forum.{% endtrans %}
+    </div>
+    {% endfor %}
+
+    {% if user.is_authenticated() and list_form %}
+    <div class="threads-actions">
+      <form id="threads_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        {{ form_theme.input_select(list_form['list_action'],width=3) }}
+        <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+      </form>
+    </div>
+    {% endif %}
+  </div>
+
+  <div class="forum-threads-extra">
+    {{ pager() }}
+    {% if user.is_authenticated() and acl.threads.can_start_threads(forum) %}
+    <a href="{{ url('thread_start', forum=forum.pk, slug=forum.slug) }}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
+    {% elif not user.is_authenticated() and not user.is_crawler() %}
+    <p class="lead threads-signin-message"><a href="{{ url('sign_in') }}">{% trans %}Sign in or register to start threads.{% endtrans %}</a></p>
+    {% endif %}
+  </div>
+
+</div>
+{% endblock %}
+
+
+{% macro forum_stats(forum) -%}
+{% if forum.last_thread_id and not forum.attr('hidethread') -%}
+  {% trans count=forum.posts, posts=fancy_number(forum.posts, forum.posts_delta), thread=forum_thread(forum) -%}
+  {{ posts }} post - last in {{ thread }}
+  {%- pluralize -%}
+  {{ posts }} posts - last in {{ thread }}
+  {%- endtrans %}
+{%- else -%}
+  {% trans count=forum.posts, posts=fancy_number(forum.posts, forum.posts_delta) -%}
+  {{ posts }} post
+  {%- pluralize -%}
+  {{ posts }} posts
+  {%- endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+{% macro forum_thread(forum) -%}
+<a href="{{ url('thread_new', thread=forum.last_thread_id, slug=forum.last_thread_slug) }}">{{ forum.last_thread_name }}</a>
+{%- endmacro %}
+
+{% macro redirect_stats(forum) -%}
+{% trans count=forum.redirects, redirects=fancy_number(forum.redirects, forum.redirects_delta) -%}
+{{ redirects }} click
+{%- pluralize -%}
+{{ redirects }} clicks
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro fancy_number(number, delta) -%}
+<strong{% if delta < number %} class="stat-increment"{% endif %}>{{ number|intcomma }}</strong>
+{%- endmacro %}
+
+{% macro replies(thread_replies) -%}
+{% trans count=thread_replies, replies=thread_replies|intcomma -%}
+{{ replies }} reply
+{%- pluralize -%}
+{{ replies }} replies
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro thread_starter(thread) -%}
+{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro thread_reply(thread) -%}
+{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro pager() %}
+{% if pagination['total'] > 0 %}
+<div class="pagination pull-left">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('forum', slug=forum.slug, forum=forum.id) }}" class="tooltip-top" title="{% trans %}First Page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('forum', slug=forum.slug, forum=forum.id, page=pagination['prev']) }}{% else %}{{ url('forum', slug=forum.slug, forum=forum.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Newest Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('forum', slug=forum.slug, forum=forum.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{% endmacro %}
+
+{% block javascripts -%}{{ super() }}
+  <script type="text/javascript">
+    $(function () {
+      function populateForumTooltip(target) {
+        return $('#forum-' + target + ' .forum-meta').html();
+      };
+      {% for subforum in forum.subforums %}
+        $('#forum-{{ subforum.id }} .forum-title').tooltip({
+          template: '<div class="tooltip forum-meta-tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+          placement: 'right',
+          html: true,
+          title: populateForumTooltip({{ subforum.id }})
+        });
+      {% endfor %}
+      {%- if user.is_authenticated() and list_form %}
+      $('#threads_form').submit(function() {
+        if ($('.thread-select[]:checked').length == 0) {
+          alert("{% trans %}You have to select at least one thread.{% endtrans %}");
+          return false;
+        }
+        if ($('#id_list_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete selected threads? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });{% endif %}
+    });
+  </script>
+{%- endblock %}

+ 62 - 62
templates/cranefly/threads/merge.html

@@ -1,63 +1,63 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_("Merge Threads"),parent=forum.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url(forum.type, forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans %}Merge Threads{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans %}Merge Threads{% endtrans %} <small>{{ forum.name }}</small></h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="row">
-    <div class="span6 offset3">
-      <div class="form-container">
-
-        <div class="form-header">
-          <h1>{% trans %}Merge Threads{% endtrans %}</h1>
-        </div>
-
-        {% if message or warning %}
-        <div class="messages-list">
-          {% if message %}
-          {{ macros.draw_message(message) }}
-          {% endif %}
-          {% if warning %}
-          {{ macros.draw_message(warning) }}
-          {% endif %}
-        </div>
-        {% endif %}
-
-        <form action="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" method="post">
-          <input type="hidden" name="origin" value="merge_form">
-          <input type="hidden" name="list_action" value="merge">
-          {% for thread in threads -%}
-          <input type="hidden" name="list_items" value="{{ thread.pk }}">
-          {% endfor %}
-          <div class="form-fields">
-            {{ form_theme.form_widget(form, width=6) }}
-          </div>
-          <div class="form-actions">
-            <button type="submit" class="btn btn-primary">{% trans %}Merge Threads{% endtrans %}</button>
-            <a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
-          </div>
-        </form>
-
-      </div>
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_("Merge Threads"),parent=forum.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url(forum.type, forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans %}Merge Threads{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans %}Merge Threads{% endtrans %} <small>{{ forum.name }}</small></h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="row">
+    <div class="span6 offset3">
+      <div class="form-container">
+
+        <div class="form-header">
+          <h1>{% trans %}Merge Threads{% endtrans %}</h1>
+        </div>
+
+        {% if message or warning %}
+        <div class="messages-list">
+          {% if message %}
+          {{ macros.draw_message(message) }}
+          {% endif %}
+          {% if warning %}
+          {{ macros.draw_message(warning) }}
+          {% endif %}
+        </div>
+        {% endif %}
+
+        <form action="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" method="post">
+          <input type="hidden" name="origin" value="merge_form">
+          <input type="hidden" name="list_action" value="merge">
+          {% for thread in threads -%}
+          <input type="hidden" name="list_items" value="{{ thread.pk }}">
+          {% endfor %}
+          <div class="form-fields">
+            {{ form_theme.form_widget(form, width=6) }}
+          </div>
+          <div class="form-actions">
+            <button type="submit" class="btn btn-primary">{% trans %}Merge Threads{% endtrans %}</button>
+            <a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+          </div>
+        </form>
+
+      </div>
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 58 - 58
templates/cranefly/threads/move_posts.html

@@ -1,59 +1,59 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_("Move Posts"),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans %}Move Posts{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans %}Move Posts{% endtrans %} <small>{{ thread.name }}</small></h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="row">
-    <div class="span6 offset3">
-      <div class="form-container">
-
-        <div class="form-header">
-          <h1>{% trans %}Move Posts{% endtrans %}</h1>
-        </div>
-
-        {% if message %}
-        <div class="messages-list">
-          {{ macros.draw_message(message) }}
-        </div>
-        {% endif %}
-
-        <form action="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" method="post">
-          <input type="hidden" name="origin" value="posts_form">
-          <input type="hidden" name="list_action" value="move">
-          <input type="hidden" name="do" value="move">
-          {% for post in posts -%}
-          <input type="hidden" name="list_items" value="{{ post }}">
-          {% endfor %}
-          <div class="form-fields">
-            {{ form_theme.form_widget(form, width=6) }}
-          </div>
-          <div class="form-actions">
-            <button type="submit" class="btn btn-primary">{% trans %}Move Posts{% endtrans %}</button>
-            <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
-          </div>
-        </form>
-
-      </div>
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_("Move Posts"),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans %}Move Posts{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans %}Move Posts{% endtrans %} <small>{{ thread.name }}</small></h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="row">
+    <div class="span6 offset3">
+      <div class="form-container">
+
+        <div class="form-header">
+          <h1>{% trans %}Move Posts{% endtrans %}</h1>
+        </div>
+
+        {% if message %}
+        <div class="messages-list">
+          {{ macros.draw_message(message) }}
+        </div>
+        {% endif %}
+
+        <form action="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" method="post">
+          <input type="hidden" name="origin" value="posts_form">
+          <input type="hidden" name="list_action" value="move">
+          <input type="hidden" name="do" value="move">
+          {% for post in posts -%}
+          <input type="hidden" name="list_items" value="{{ post }}">
+          {% endfor %}
+          <div class="form-fields">
+            {{ form_theme.form_widget(form, width=6) }}
+          </div>
+          <div class="form-actions">
+            <button type="submit" class="btn btn-primary">{% trans %}Move Posts{% endtrans %}</button>
+            <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+          </div>
+        </form>
+
+      </div>
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 59 - 59
templates/cranefly/threads/move_thread.html

@@ -1,60 +1,60 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_("Move Threads"),parent=forum.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans %}Move Thread{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans %}Move Thread{% endtrans %} <small>{{ thread.name }}</small></h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="row">
-    <div class="span6 offset3">
-      <div class="form-container">
-
-        <div class="form-header">
-          <h1>{% trans %}Move Thread{% endtrans %}</h1>
-        </div>
-
-        {% if message %}
-        <div class="messages-list">
-          {{ macros.draw_message(message) }}
-        </div>
-        {% endif %}
-
-        <form action="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" method="post">
-          <input type="hidden" name="origin" value="thread_form">
-          <input type="hidden" name="thread_action" value="move">
-          <input type="hidden" name="do" value="move">
-          {% for thread in threads -%}
-          <input type="hidden" name="list_items" value="{{ thread.pk }}">
-          {% endfor %}
-          <div class="form-fields">
-            {% do form.fieldsets[0]['fields'][0].update({'label': _("Move Thread To"), 'help_text': _("Select forum you want to move this thread to.")}) %}
-            {{ form_theme.form_widget(form, width=6) }}
-          </div>
-          <div class="form-actions">
-            <button type="submit" class="btn btn-primary">{% trans %}Move Thread{% endtrans %}</button>
-            <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
-          </div>
-        </form>
-
-      </div>
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_("Move Threads"),parent=forum.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans %}Move Thread{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans %}Move Thread{% endtrans %} <small>{{ thread.name }}</small></h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="row">
+    <div class="span6 offset3">
+      <div class="form-container">
+
+        <div class="form-header">
+          <h1>{% trans %}Move Thread{% endtrans %}</h1>
+        </div>
+
+        {% if message %}
+        <div class="messages-list">
+          {{ macros.draw_message(message) }}
+        </div>
+        {% endif %}
+
+        <form action="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" method="post">
+          <input type="hidden" name="origin" value="thread_form">
+          <input type="hidden" name="thread_action" value="move">
+          <input type="hidden" name="do" value="move">
+          {% for thread in threads -%}
+          <input type="hidden" name="list_items" value="{{ thread.pk }}">
+          {% endfor %}
+          <div class="form-fields">
+            {% do form.fieldsets[0]['fields'][0].update({'label': _("Move Thread To"), 'help_text': _("Select forum you want to move this thread to.")}) %}
+            {{ form_theme.form_widget(form, width=6) }}
+          </div>
+          <div class="form-actions">
+            <button type="submit" class="btn btn-primary">{% trans %}Move Thread{% endtrans %}</button>
+            <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+          </div>
+        </form>
+
+      </div>
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 57 - 57
templates/cranefly/threads/move_threads.html

@@ -1,58 +1,58 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_("Move Threads"),parent=forum.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url(forum.type, forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans %}Move Threads{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans %}Move Threads{% endtrans %} <small>{{ forum.name }}</small></h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="row">
-    <div class="span6 offset3">
-      <div class="form-container">
-
-        <div class="form-header">
-          <h1>{% trans %}Move Threads{% endtrans %}</h1>
-        </div>
-
-        {% if message %}
-        <div class="messages-list">
-          {{ macros.draw_message(message) }}
-        </div>
-        {% endif %}
-
-        <form action="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" method="post">
-          <input type="hidden" name="origin" value="move_form">
-          <input type="hidden" name="list_action" value="move">
-          {% for thread in threads -%}
-          <input type="hidden" name="list_items" value="{{ thread.pk }}">
-          {% endfor %}
-          <div class="form-fields">
-            {{ form_theme.form_widget(form, width=6) }}
-          </div>
-          <div class="form-actions">
-            <button type="submit" class="btn btn-primary">{% trans %}Move Threads{% endtrans %}</button>
-            <a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
-          </div>
-        </form>
-
-      </div>
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_("Move Threads"),parent=forum.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url(forum.type, forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans %}Move Threads{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans %}Move Threads{% endtrans %} <small>{{ forum.name }}</small></h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="row">
+    <div class="span6 offset3">
+      <div class="form-container">
+
+        <div class="form-header">
+          <h1>{% trans %}Move Threads{% endtrans %}</h1>
+        </div>
+
+        {% if message %}
+        <div class="messages-list">
+          {{ macros.draw_message(message) }}
+        </div>
+        {% endif %}
+
+        <form action="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" method="post">
+          <input type="hidden" name="origin" value="move_form">
+          <input type="hidden" name="list_action" value="move">
+          {% for thread in threads -%}
+          <input type="hidden" name="list_items" value="{{ thread.pk }}">
+          {% endfor %}
+          <div class="form-fields">
+            {{ form_theme.form_widget(form, width=6) }}
+          </div>
+          <div class="form-actions">
+            <button type="submit" class="btn btn-primary">{% trans %}Move Threads{% endtrans %}</button>
+            <a href="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+          </div>
+        </form>
+
+      </div>
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 188 - 188
templates/cranefly/threads/posting.html

@@ -1,188 +1,188 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/editor.html" as editor with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{% if thread -%}
-{{ macros.page_title(title=_(get_title()), parent=thread.name) }}
-{%- else -%}
-{{ macros.page_title(title=_(get_title()), parent=forum.name) }}
-{%- endif %}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url(forum.type, forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{% if thread %}<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>{% endif %}
-<li class="active">{{ get_title() }}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{{ get_title() }} <small>{% if thread %}{{ thread.name }}{% else %}{{ forum.name }}{% endif %}</small></h1>
-    {% if thread %}
-    <ul class="unstyled header-stats">
-      {{ get_info() }}
-    </ul>
-    {% endif %}
-  </div>
-</div>
-<div class="container container-primary">
-  <div class="row">
-    <div class="span8 offset2">
-      <div class="posting">
-        <div class="form-container">
-
-          <div class="form-header">
-            <h1>{{ get_title() }}</h1>
-          </div>
-
-          {% if message %}
-          <div class="messages-list">
-            {{ macros.draw_message(message) }}
-          </div>
-          {% endif %}
-
-          {% if preview %}
-          <div class="form-preview">
-            <div class="markdown js-extra">
-              <article>
-                {{ preview|markdown_final|safe }}
-              </article>
-            </div>
-          </div>
-          {% endif %}
-
-          <form action="{{ get_action() }}" method="post">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            {% if 'thread_name' in form.fields %}
-            {{ form_theme.row_widget(form.fields.thread_name, width=8) }}
-            <hr>
-            <h4>{% trans %}Message Body{% endtrans %}</h4>
-            {% endif %}
-            {{ editor.editor(form.fields.post, get_button(), rows=8, extra=get_extra()) }}
-            {% if intersect(form.fields, ('edit_reason', 'thread_weight', 'close_thread')) %}
-            <hr>
-            {% if 'edit_reason' in form.fields %}
-            {{ form_theme.row_widget(form.fields.edit_reason, width=8) }}
-            {% endif %}
-
-            {% if intersect(form.fields, ('thread_weight', 'close_thread')) %}
-            <div class="control-group">
-              <label class="control-label">{% trans %}Thread Status{% endtrans %}:</label>
-              <div class="controls">
-                {% if 'thread_weight' in form.fields %}
-                {{ form_theme.input_radio_select(form.fields.thread_weight, width=8) }}
-                {% endif %}
-                {% if 'close_thread' in form.fields %}
-                {{ form_theme.input_checkbox(form.fields.close_thread, width=8) }}
-                {% endif %}
-              </div>
-            </div>
-            {% endif %}
-
-            <div class="form-actions">
-              <button type="submit" class="btn btn-primary">{{ get_button() }}</button>
-              <button id="editor-preview" name="preview" type="submit" class="btn">{% trans %}Preview{% endtrans %}</button>
-            </div>
-            {% endif %}
-          </form>
-
-        </div>
-      </div>
-    </div>
-  </div>
-</div>
-{% endblock %}
-
-{% block stylesheets %}{{ super() }}
-<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
-{% endblock %}
-
-{% block javascripts %}{{ super() }}
-  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
-  <script type="text/javascript">
-    hljs.tabReplace = '    ';
-    hljs.initHighlightingOnLoad();
-    EnhancePostsMD();
-  </script>
-  {{ editor.js() }}
-{% endblock %}
-
-
-{% macro get_action() -%}
-{% if action == 'new_thread' -%}
-{{ url('thread_start', forum=forum.pk, slug=forum.slug) }}
-{%- elif action == 'edit_thread' -%}
-{{ url('thread_edit', thread=thread.pk, slug=thread.slug) }}
-{%- elif action in 'new_reply' -%}
-{%- if quote -%}
-{{ url('thread_reply', thread=thread.pk, slug=thread.slug, quote=quote.pk) }}
-{%- else -%}
-{{ url('thread_reply', thread=thread.pk, slug=thread.slug) }}
-{%- endif -%}
-{%- elif action == 'edit_reply' -%}
-{{ url('post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_title() -%}
-{% if action == 'new_thread' -%}
-{% trans %}Post New Thread{% endtrans %}
-{%- elif action == 'edit_thread' -%}
-{% trans %}Edit Thread{% endtrans %}
-{%- elif action == 'new_reply' -%}
-{% trans %}Post New Reply{% endtrans %}
-{%- elif action == 'edit_reply' -%}
-{% trans %}Edit Reply{% endtrans %}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_info() -%}
-{% if action == 'edit_reply' -%}
-    {% if post.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
-    <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
-    <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
-    <li><i class="icon-pencil"></i> {% if post.edits > 0 -%}
-      {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}
-    {%- else -%}
-      {% trans %}First edit{% endtrans %}
-    {%- endif %}</li>
-{%- else -%}
-    {% if thread.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
-    {% if action == 'edit_thread' %}
-    <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=thread.start_post_id) }}">{{ thread.start|reltimesince }}</a></li>
-    {% else %}
-    <li><i class="icon-time"></i> <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}">{{ thread.last|reltimesince }}</a></li>
-    {% endif %}
-    <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
-    <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
-      {% trans count=thread.replies, replies=thread.replies|intcomma %}One reply{% pluralize %}{{ replies }} replies{% endtrans %}
-    {%- else -%}
-      {% trans %}No replies{% endtrans %}
-    {%- endif %}</li>
-{%- endif %}
-    {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
-{%- endmacro %}
-
-
-{% macro get_button() -%}
-{% if action == 'new_thread' -%}
-{% trans %}Post Thread{% endtrans %}
-{%- elif action == 'new_reply' -%}
-{% trans %}Post Reply{% endtrans %}
-{%- else -%}
-{% trans %}Save Changes{% endtrans %}
-{%- endif %}
-{%- endmacro %}
-
-
-{% macro get_extra() %}
-  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Preview{% endtrans %}</button>
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/editor.html" as editor with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{% if thread -%}
+{{ macros.page_title(title=_(get_title()), parent=thread.name) }}
+{%- else -%}
+{{ macros.page_title(title=_(get_title()), parent=forum.name) }}
+{%- endif %}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url(forum.type, forum=forum.pk, slug=forum.slug) }}">{{ forum.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{% if thread %}<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>{% endif %}
+<li class="active">{{ get_title() }}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{{ get_title() }} <small>{% if thread %}{{ thread.name }}{% else %}{{ forum.name }}{% endif %}</small></h1>
+    {% if thread %}
+    <ul class="unstyled header-stats">
+      {{ get_info() }}
+    </ul>
+    {% endif %}
+  </div>
+</div>
+<div class="container container-primary">
+  <div class="row">
+    <div class="span8 offset2">
+      <div class="posting">
+        <div class="form-container">
+
+          <div class="form-header">
+            <h1>{{ get_title() }}</h1>
+          </div>
+
+          {% if message %}
+          <div class="messages-list">
+            {{ macros.draw_message(message) }}
+          </div>
+          {% endif %}
+
+          {% if preview %}
+          <div class="form-preview">
+            <div class="markdown js-extra">
+              <article>
+                {{ preview|markdown_final|safe }}
+              </article>
+            </div>
+          </div>
+          {% endif %}
+
+          <form action="{{ get_action() }}" method="post">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            {% if 'thread_name' in form.fields %}
+            {{ form_theme.row_widget(form.fields.thread_name, width=8) }}
+            <hr>
+            <h4>{% trans %}Message Body{% endtrans %}</h4>
+            {% endif %}
+            {{ editor.editor(form.fields.post, get_button(), rows=8, extra=get_extra()) }}
+            {% if intersect(form.fields, ('edit_reason', 'thread_weight', 'close_thread')) %}
+            <hr>
+            {% if 'edit_reason' in form.fields %}
+            {{ form_theme.row_widget(form.fields.edit_reason, width=8) }}
+            {% endif %}
+
+            {% if intersect(form.fields, ('thread_weight', 'close_thread')) %}
+            <div class="control-group">
+              <label class="control-label">{% trans %}Thread Status{% endtrans %}:</label>
+              <div class="controls">
+                {% if 'thread_weight' in form.fields %}
+                {{ form_theme.input_radio_select(form.fields.thread_weight, width=8) }}
+                {% endif %}
+                {% if 'close_thread' in form.fields %}
+                {{ form_theme.input_checkbox(form.fields.close_thread, width=8) }}
+                {% endif %}
+              </div>
+            </div>
+            {% endif %}
+
+            <div class="form-actions">
+              <button type="submit" class="btn btn-primary">{{ get_button() }}</button>
+              <button id="editor-preview" name="preview" type="submit" class="btn">{% trans %}Preview{% endtrans %}</button>
+            </div>
+            {% endif %}
+          </form>
+
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock %}
+
+{% block stylesheets %}{{ super() }}
+<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
+{% endblock %}
+
+{% block javascripts %}{{ super() }}
+  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
+  <script type="text/javascript">
+    hljs.tabReplace = '    ';
+    hljs.initHighlightingOnLoad();
+    EnhancePostsMD();
+  </script>
+  {{ editor.js() }}
+{% endblock %}
+
+
+{% macro get_action() -%}
+{% if action == 'new_thread' -%}
+{{ url('thread_start', forum=forum.pk, slug=forum.slug) }}
+{%- elif action == 'edit_thread' -%}
+{{ url('thread_edit', thread=thread.pk, slug=thread.slug) }}
+{%- elif action in 'new_reply' -%}
+{%- if quote -%}
+{{ url('thread_reply', thread=thread.pk, slug=thread.slug, quote=quote.pk) }}
+{%- else -%}
+{{ url('thread_reply', thread=thread.pk, slug=thread.slug) }}
+{%- endif -%}
+{%- elif action == 'edit_reply' -%}
+{{ url('post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_title() -%}
+{% if action == 'new_thread' -%}
+{% trans %}Post New Thread{% endtrans %}
+{%- elif action == 'edit_thread' -%}
+{% trans %}Edit Thread{% endtrans %}
+{%- elif action == 'new_reply' -%}
+{% trans %}Post New Reply{% endtrans %}
+{%- elif action == 'edit_reply' -%}
+{% trans %}Edit Reply{% endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_info() -%}
+{% if action == 'edit_reply' -%}
+    {% if post.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
+    <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{{ post.date|reltimesince }}</a></li>
+    <li><i class="icon-user"></i> {% if post.user %}<a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}">{{ post.user.username }}</a>{% else %}{{ post.user_name }}{% endif %}</li>
+    <li><i class="icon-pencil"></i> {% if post.edits > 0 -%}
+      {% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}
+    {%- else -%}
+      {% trans %}First edit{% endtrans %}
+    {%- endif %}</li>
+{%- else -%}
+    {% if thread.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
+    {% if action == 'edit_thread' %}
+    <li><i class="icon-time"></i> <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=thread.start_post_id) }}">{{ thread.start|reltimesince }}</a></li>
+    {% else %}
+    <li><i class="icon-time"></i> <a href="{{ url('thread_new', thread=thread.pk, slug=thread.slug) }}">{{ thread.last|reltimesince }}</a></li>
+    {% endif %}
+    <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
+    <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
+      {% trans count=thread.replies, replies=thread.replies|intcomma %}One reply{% pluralize %}{{ replies }} replies{% endtrans %}
+    {%- else -%}
+      {% trans %}No replies{% endtrans %}
+    {%- endif %}</li>
+{%- endif %}
+    {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
+{%- endmacro %}
+
+
+{% macro get_button() -%}
+{% if action == 'new_thread' -%}
+{% trans %}Post Thread{% endtrans %}
+{%- elif action == 'new_reply' -%}
+{% trans %}Post Reply{% endtrans %}
+{%- else -%}
+{% trans %}Save Changes{% endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+
+{% macro get_extra() %}
+  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Preview{% endtrans %}</button>
+{% endmacro %}

+ 58 - 58
templates/cranefly/threads/split.html

@@ -1,59 +1,59 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_("Split Thread"),parent=thread.name) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
-<li class="active">{% trans %}Split Thread{% endtrans %}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb">
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{% trans %}Split Thread{% endtrans %} <small>{{ thread.name }}</small></h1>
-  </div>
-</div>
-
-<div class="container container-primary">
-  <div class="row">
-    <div class="span6 offset3">
-      <div class="form-container">
-
-        <div class="form-header">
-          <h1>{% trans %}Split Thread{% endtrans %}</h1>
-        </div>
-
-        {% if message %}
-        <div class="messages-list">
-          {{ macros.draw_message(message) }}
-        </div>
-        {% endif %}
-
-        <form action="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" method="post">
-          <input type="hidden" name="origin" value="posts_form">
-          <input type="hidden" name="list_action" value="split">
-          <input type="hidden" name="do" value="split">
-          {% for post in posts -%}
-          <input type="hidden" name="list_items" value="{{ post }}">
-          {% endfor %}
-          <div class="form-fields">
-            {{ form_theme.form_widget(form, width=6) }}
-          </div>
-          <div class="form-actions">
-            <button type="submit" class="btn btn-primary">{% trans %}Split Thread{% endtrans %}</button>
-            <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
-          </div>
-        </form>
-
-      </div>
-    </div>
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_("Split Thread"),parent=thread.name) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li><a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}">{{ thread.name }}</a> <span class="divider"><i class="icon-chevron-right"></i></span></li>
+<li class="active">{% trans %}Split Thread{% endtrans %}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb">
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{% trans %}Split Thread{% endtrans %} <small>{{ thread.name }}</small></h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+  <div class="row">
+    <div class="span6 offset3">
+      <div class="form-container">
+
+        <div class="form-header">
+          <h1>{% trans %}Split Thread{% endtrans %}</h1>
+        </div>
+
+        {% if message %}
+        <div class="messages-list">
+          {{ macros.draw_message(message) }}
+        </div>
+        {% endif %}
+
+        <form action="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" method="post">
+          <input type="hidden" name="origin" value="posts_form">
+          <input type="hidden" name="list_action" value="split">
+          <input type="hidden" name="do" value="split">
+          {% for post in posts -%}
+          <input type="hidden" name="list_items" value="{{ post }}">
+          {% endfor %}
+          <div class="form-fields">
+            {{ form_theme.form_widget(form, width=6) }}
+          </div>
+          <div class="form-actions">
+            <button type="submit" class="btn btn-primary">{% trans %}Split Thread{% endtrans %}</button>
+            <a href="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+          </div>
+        </form>
+
+      </div>
+    </div>
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 547 - 547
templates/cranefly/threads/thread.html

@@ -1,547 +1,547 @@
-{% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/editor.html" as editor with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=thread.name,parent=forum.name,page=pagination['page']) }}{% endblock %}
-
-{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
-{{ macros.parents_list(parents) }}
-<li class="active">{{ thread.name }}
-{%- endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
-      {{ self.breadcrumb() }}</li>
-    </ul>
-    <h1>{{ thread.name }}</h1>
-    <ul class="unstyled header-stats">
-      {% if thread.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
-      <li><i class="icon-time"></i> {{ thread.last|reltimesince }}</li>
-      <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
-      <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
-        {% trans count=thread.replies, replies=thread.replies|intcomma %}One reply{% pluralize %}{{ replies }} replies{% endtrans %}
-      {%- else -%}
-        {% trans %}No replies{% endtrans %}
-      {%- endif %}</li>
-      {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <div class="thread-buttons">
-    {{ pager() }}
-    {% if user.is_authenticated() %}    
-    {% if acl.threads.can_reply(forum, thread) %}
-    <a href="{{ url('thread_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Reply{% endtrans %}</a>
-    {% endif %}
-    {% if watcher %}
-    <form action="{{ url('thread_unwatch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Remove thread from watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
-    {% if watcher.email %}
-    <form action="{{ url('thread_unwatch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Don't e-mail me anymore if anyone replies to this thread{% endtrans %}"><i class="icon-envelope"></i></button></form>
-    {% else %}
-    <form action="{{ url('thread_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}E-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
-    {% endif %}
-    {% else %}
-    <form action="{{ url('thread_watch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
-    <form action="{{ url('thread_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list and e-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
-    {% endif %}
-    {% if ignored_posts %}
-    <form action="{{ url('thread_show_hidden', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn"><i class="icon-eye-open"></i> {% trans %}Show Hidden Replies{% endtrans %}</button></form>
-    {% endif %}
-    {% endif %}
-  </div>
-
-  <div class="thread-body">
-    {% for post in posts %}
-    <div id="post-{{ post.pk }}" class="post-wrapper">
-      {% if post.message %}
-      <div class="messages-list">
-        {{ macros.draw_message(post.message) }}
-      </div>
-      {% endif %}
-      {% if post.deleted and not acl.threads.can_see_deleted_posts(forum) %}
-      <div class="post-body post-muted">
-        {% if post.user_id %}
-        <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(50) }}" alt="" class="user-avatar"></a>
-        {% else %}
-        <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
-        {% endif %}
-        <div class="post-content">
-          <div class="post-header">
-            <div class="post-header-compact">
-              {% if post.user_id %}
-              <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
-              {% else %}
-              <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
-              {% endif %}
-              <span class="separator">&ndash;</span>
-              <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
-              {% if post.edits %}
-              <span class="separator">&ndash;</span>
-              {% if acl.threads.can_see_changelog(user, forum, post) %}
-              <a href="{{ url('thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
-              {% else %}
-              <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
-              {% endif %}
-              {% endif %}
-            </div>
-
-            <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
-
-            {% if not post.is_read %}
-            <div class="post-extra">
-              <span class="label label-warning">
-                {% trans %}New{% endtrans %}
-              </span>
-            </div>
-            {% endif %}
-
-          </div>
-          <div class="post-message">
-            {% trans user=edit_user(post), date=post.delete_date|reltimesince|low %}{{ user }} has deleted this reply {{ date }}{% endtrans %}
-          </div>
-        </dv>
-      </div>
-      {% elif post.ignored %}
-      <div class="post-body post-muted">
-        <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
-        <div class="post-arrow"></div>
-        <div class="post-content">
-          <div class="post-header">
-            <div class="post-header-compact">
-              <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
-            </div>
-
-            <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
-
-            {% if not post.is_read %}
-            <div class="post-extra">
-              <span class="label label-warning">
-                {% trans %}New{% endtrans %}
-              </span>
-            </div>
-            {% endif %}
-
-          </div>
-          <div class="post-message">
-            {% trans %}This reply was posted by user that is on your ignored list.{% endtrans %}
-          </div>
-        </dv>
-      </div>
-      {% else %}
-      <div class="post-body">
-        {% if post.user_id %}
-        <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(100) }}" alt="" class="user-avatar"></a>
-        {% else %}
-        <img src="{{ macros.avatar_guest(100) }}" alt="" class="user-avatar">
-        {% endif %}
-        <div class="post-arrow"></div>
-        <div class="post-content">
-          <div class="post-header">
-            {% if post.user_id %}
-            <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
-            {% else %}
-            <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
-            {% endif %}
-            <span class="separator">&ndash;</span>
-            <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
-            {% if post.edits %}
-            <span class="separator">&ndash;</span>
-            {% if acl.threads.can_see_changelog(user, forum, post) %}
-            <a href="{{ url('thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
-            {% else %}
-            <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
-            {% endif %}
-            {% endif %}
-
-            {% if user.is_authenticated() and posts_form %}
-            <label class="checkbox post-checkbox"><input form="posts_form" name="{{ posts_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ post.pk }}"{% if posts_form['list_items']['has_value'] and ('' ~ post.pk) in posts_form['list_items']['value'] %} checked="checked"{% endif %}></label>
-            {% endif %}
-
-            <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
-
-            <div class="post-extra">
-              {% if post.protected and acl.threads.can_protect(forum) %}
-              <span class="label label-info">
-                {% trans %}Protected{% endtrans %}
-              </span>
-              {% endif %}
-
-              {% if post.deleted %}
-              <span class="label label-inverse">
-                {% trans %}Deleted{% endtrans %}
-              </span>
-              {% endif %}
-
-              {% if post.moderated %}
-              <span class="label label-purple">
-                {% trans %}Unreviewed{% endtrans %}
-              </span>
-              {% endif %}
-
-              {% if acl.threads.can_mod_posts(forum) and post.reported %}
-              <span class="label label-important">
-                {% trans %}Reported{% endtrans %}
-              </span>
-              {% endif %}
-
-              {% if not post.is_read %}
-              <span class="label label-warning">
-                {% trans %}New{% endtrans %}
-              </span>
-              {% endif %}
-            </div>
-          </div>
-          <div class="post-message">
-            <div class="markdown js-extra">
-              <article>
-                {{ post.post_preparsed|markdown_final|safe }}
-              </article>
-            </div>
-            {% if post.user.signature %}
-            <div class="post-signature">
-              <div class="markdown">
-                {{ post.user.signature_preparsed|markdown_final|safe }}
-              </div>
-            </div>
-            {% endif %}
-          </div>
-          <div class="post-footer">{% filter trim %}
-            {% if acl.threads.can_see_post_score(forum) %}
-            <div{% if user.is_authenticated() and user.pk != post.user_id %} class="post-rating-actions"{% endif %}>
-              <div class="post-rating">
-                {% if acl.threads.can_see_post_score(forum) == 1 %}
-                <span class="post-score post-score-total{% if (post.upvotes - post.downvotes) > 0 %} post-score-good{% elif (post.upvotes - post.downvotes) < 0 %} post-score-bad{% endif %}">{{ post.upvotes - post.downvotes }}</span>
-                {% elif acl.threads.can_see_post_score(forum) == 2%}
-                <span class="post-score post-score-upvotes{% if post.upvotes %} post-score-good{% endif %}">{{ post.upvotes }}</span>
-                {% endif %}
-                {% if user.is_authenticated() and user.pk != post.user_id and acl.threads.can_upvote_posts(forum) %}
-                <form action="{{ url('post_upvote', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-upvote" method="post">
-                  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                  <button type="submit" class="btn btn-link post-like"{% if post.karma_vote and post.karma_vote.score > 0 %} disabled="disabled"{% endif %}>{% trans %}Like{% endtrans %}</button>
-                </form>
-                {% else %}
-                <span class="post-{% if post.upvotes %}like{% else %}neutral{% endif %}">{% trans %}Likes{% endtrans %}</span>
-                {% endif %}
-              {% if acl.threads.can_see_post_score(forum) == 2 %}
-              </div>
-              <div class="post-rating">
-                <span class="post-score post-score-downvotes{% if post.downvotes %} post-score-bad{% endif %}">{{ post.downvotes }}</span>
-              {% endif %}
-                {% if user.is_authenticated() and user.pk != post.user_id and acl.threads.can_downvote_posts(forum) %}
-                <form action="{{ url('post_downvote', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-downvote" method="post">
-                  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                  <button type="submit" class="btn btn-link post-hate"{% if post.karma_vote and post.karma_vote.score < 0 %} disabled="disabled"{% endif %}>{% trans %}Dislike{% endtrans %}</button>
-                </form>
-                {% elif acl.threads.can_see_post_score(forum) == 2 %}
-                <span class="post-{% if post.downvotes %}hate{% else %}neutral{% endif %}">{% trans %}Dislikes{% endtrans %}</span>
-                {% endif %}
-              </div>
-              {% if acl.threads.can_see_post_votes(forum) %}
-              <div class="post-rating">
-                <a href="{{ url('post_votes', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans %}Show Votes{% endtrans %}</a>
-              </div>
-              {% endif %}
-            </div>
-            {% endif %}
-
-            {% if user.is_authenticated() %}
-            <div class="post-actions">
-              {% if acl.users.can_see_users_trails() -%}
-              <a href="{{ url('post_info', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-trail">{% trans %}Info{% endtrans %}</a>
-              {% endif %}
-              {% if post.reported and acl.reports.can_handle() and acl.threads.can_mod_posts(forum) %}
-              <a href="{{ url('post_report_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans %}Show report{% endtrans %}</a>
-              {% endif %}
-              {% if acl.reports.can_report() %}
-              {% if post.reported_by(user) %}
-              <a href="{{ url('post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="tooltip-top" title="{% trans %}You have already reported this post.{% endtrans %}" disabled="disabled">{% trans %}Reported{% endtrans %}</a>
-              {% else %}
-              <form action="{{ url('post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-report" method="post" autocomplete="off">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-report tooltip-top" title="{% trans %}Bring this post to moderator attention.{% endtrans %}">{% trans %}Report{% endtrans %}</button>
-              </form>
-              {% endif %}
-              {% endif %}
-              {% if acl.threads.can_edit_thread(user, forum, thread, post) and thread.start_post_id == post.pk %}
-              <a href="{{ url('thread_edit', thread=thread.pk, slug=thread.slug) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
-              {% elif acl.threads.can_edit_reply(user, forum, thread, post) %}
-              <a href="{{ url('post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
-              {%- endif %}
-              {% if acl.threads.can_reply(forum, thread) %}<a href="{{ url('thread_reply', thread=thread.pk, slug=thread.slug, quote=post.pk) }}" class="post-reply">{% trans %}Reply{% endtrans %}</a>{% endif %}
-            </div>
-            {% if post.pk == thread.start_post_id %}
-            <div class="post-actions">
-              {% if acl.threads.can_delete_thread(user, forum, thread, post) %}
-              {% if post.deleted %}
-              <form action="{{ url('thread_show', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this thread visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
-              </form>
-              {% else %}
-              <form action="{{ url('thread_hide', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this thread from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
-              </form>
-              {% endif %}
-              {% endif %}
-              {% if acl.threads.can_delete_thread(user, forum, thread, post) == 2 %}
-              <form action="{{ url('thread_delete', thread=thread.pk, slug=thread.slug) }}" class="form-inline prompt-delete-thread" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this thread for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
-              </form>
-              {% endif %}
-            </div>
-            {% elif post.pk != thread.start_post_id and acl.threads.can_delete_post(user, forum, thread, post) %}
-            <div class="post-actions">
-              {% if acl.threads.can_delete_post(user, forum, thread, post) %}
-              {% if post.deleted %}
-              <form action="{{ url('post_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this reply visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
-              </form>
-              {% else %}
-              <form action="{{ url('post_hide', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this reply from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
-              </form>
-              {% endif %}
-              {% endif %}
-              {% if acl.threads.can_delete_post(user, forum, thread, post) == 2 -%}
-              <form action="{{ url('post_delete', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline prompt-delete-post" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this reply for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
-              </form>
-              {% endif %}
-            </div>
-            {% endif %}
-            {% endif %}
-          {% endfilter %}</div>
-        </div>
-      </div>
-      {% endif %}
-    </div>
-
-    {% if post.checkpoints_visible %}
-    <div class="post-checkpoints">
-      {% for checkpoint in post.checkpoints_visible %}
-      <div class="post-checkpoint{% if checkpoint.deleted %} checkpoint-deleted{% endif %}">
-        <hr>
-        <span>
-          {%- if checkpoint.action == 'limit' -%}
-          <i class="icon-lock"></i> {% trans  %}This thread has reached its post limit and has been closed.{% endtrans %}
-          {%- elif checkpoint.action == 'accepted' -%}
-          <i class="icon-ok"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} accepted this thread {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'closed' -%}
-          <i class="icon-lock"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} closed this thread {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'opened' -%}
-          <i class="icon-lock"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} opened this thread {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'deleted' -%}
-          <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} deleted this thread {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'undeleted' -%}
-          <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} restored this thread {{ date }}{% endtrans %}
-          {%- elif checkpoint.action == 'moved' and acl.forums.can_see(checkpoint.old_forum_id) -%}
-          <i class="icon-arrow-right"></i> {% trans user=checkpoint_user(checkpoint), forum=checkpoint_forum(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} moved this thread from {{ forum }} {{ date }}{% endtrans %}
-          {%- endif -%}
-          {% if user.is_authenticated() %}
-          {% if acl.threads.can_delete_checkpoint(forum) %}
-          {% if checkpoint.deleted %}
-          <form action="{{ url('post_checkpoint_show', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-            <button type="submit" class="btn btn-link btn-show">{% trans %}Restore{% endtrans %}</button>
-          </form>
-          {% else %}
-          <form action="{{ url('post_checkpoint_hide', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-            <button type="submit" class="btn btn-link btn-hide">{% trans %}Hide{% endtrans %}</button>
-          </form>
-          {% endif %}
-          {% endif %}
-          {% if acl.threads.can_delete_checkpoint(forum) == 2 %}
-          <form action="{{ url('post_checkpoint_delete', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline prompt-delete-checkpoint">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
-            <button type="submit" class="btn btn-link btn-delete">{% trans %}Delete{% endtrans %}</button>
-          </form>
-          {% endif %}
-          {% endif %}
-        </span>
-      </div>
-      {% endfor %}
-    </div>
-    {% endif %}
-    {% endfor %}
-  </div>
-
-  {% if user.is_authenticated() and (thread_form or posts_form) %}
-  <div class="thread-moderation">
-    {% if thread_form%}
-    <form id="thread_form" class="form-inline pull-left" action="{{ request_path }}" method="POST">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <input type="hidden" name="origin" value="thread_form">
-      {{ form_theme.input_select(thread_form['thread_action'],width=3) }}
-      <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-    </form>
-    {% endif %}
-    {% if posts_form%}
-    <form id="posts_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <input type="hidden" name="origin" value="posts_form">
-      {{ form_theme.input_select(posts_form['list_action'],width=3) }}
-      <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
-    </form>
-    {% endif %}
-  </div>
-  {% endif %}
-
-  <div class="thread-buttons">
-    {{ pager(false) }}
-    {% if user.is_authenticated() and acl.threads.can_reply(forum, thread) %}
-    <a href="{{ url('thread_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Reply{% endtrans %}</a>
-    {% elif not user.is_authenticated() and not user.is_crawler() %}
-    <p class="lead thread-signin-message"><a href="{{ url('sign_in') }}">{% trans %}Sign in or register to reply.{% endtrans %}</a></p>
-    {% endif %}
-  </div>
-
-  {% if user.is_authenticated() and acl.threads.can_reply(forum, thread) %}
-  <div class="thread-quick-reply">
-    <form action="{{ url('thread_reply', thread=thread.pk, slug=thread.slug) }}" method="post">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <input type="hidden" name="quick_reply" value="1">
-      <img src="{{ user.get_avatar(100) }}" alt="{% trans %}Your Avatar{% endtrans %}" class="user-avatar">
-      {{ editor.editor(quick_reply.post, _('Post Reply'), extra=editor_extra()) }}
-    </form>
-  </div>
-  {% endif %}
-
-</div>
-{% endblock %}
-
-{% block stylesheets %}{{ super() }}
-<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
-{% endblock %}
-
-{% block javascripts -%}{{ super() }}
-  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
-  <script type="text/javascript">
-    var l_post_reported = "{{ _('Reported!') }}";
-    hljs.tabReplace = '    ';
-    hljs.initHighlightingOnLoad();
-    EnhancePostsMD();
-    {%- if user.is_authenticated() %}
-    $(function () {
-      $('#thread_form').submit(function() {
-        if ($('#id_thread_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete this thread? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });
-      $('#posts_form').submit(function() {
-        if ($('.post-checkbox[]:checked').length == 0) {
-          alert("{% trans %}You have to select at least one post.{% endtrans %}");
-          return false;
-        }
-        if ($('#id_list_action').val() == 'merge') {
-          if ($('.post-checkbox[]:checked').length < 2) {
-              alert("{% trans %}You have to select at least two posts you want to merge.{% endtrans %}");
-              return false;
-          }
-          var decision = confirm("{% trans %}Are you sure you want to merge selected posts? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        if ($('#id_list_action').val() == 'hard') {
-          var decision = confirm("{% trans %}Are you sure you want to delete selected posts? This action is not reversible!{% endtrans %}");
-          return decision;
-        }
-        return true;
-      });
-      $('.prompt-delete-thread').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this thread?{% endtrans %}");
-          return decision;
-      });
-      $('.prompt-delete-post').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this post?{% endtrans %}");
-          return decision;
-      });
-      $('.prompt-delete-checkpoint').submit(function() {
-          var decision = confirm("{% trans %}Are you sure you want to delete this checkpoint?{% endtrans %}");
-          return decision;
-      });
-    });
-    {% endif %}
-  </script>
-  {% if user.is_authenticated() and acl.threads.can_reply(forum, thread) %}
-  {{ editor.js() }}
-  {% endif %}
-{%- endblock %}
-
-
-{% macro user_label(user) -%}
-<{% if user.rank and user.rank.as_tab %}a href="{{ url('users', slug=user.rank.slug) }}"{% else %}span{% endif %} class="label post-author-label{% if user.rank and user.rank.style %} post-label-{{ user.rank.style }}{% endif %}">{{ user.get_title() }}</{% if user.rank and user.rank.as_tab%}a{% else %}span{% endif %}>
-{%- endmacro %}
-
-
-{% macro pager(extra=true) %}
-<div class="pagination pull-left">
-  <ul>
-    {% if pagination['total'] > 0 %}
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('thread', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('thread', slug=thread.slug, thread=thread.id, page=pagination['prev']) }}{% else %}{{ url('thread', slug=thread.slug, thread=thread.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('thread', slug=thread.slug, thread=thread.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Newest Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 and pagination['next'] < pagination['total'] %}<li><a href="{{ url('thread', slug=thread.slug, thread=thread.id, page=pagination['total']) }}" class="tooltip-top" title="{% trans %}Go to last page{% endtrans %}">{% trans %}Last{% endtrans %} <i class="icon-chevron-right"></i></a></li>{% endif -%}
-    {% endif %}
-    {% if extra and user.is_authenticated() %}
-    {% if not is_read %}<li><a href="{{ url('thread_new', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first unread{% endtrans %}"><i class="icon-star"></i> {% trans %}First Unread{% endtrans %}</a></li>{% endif %}
-    {% if thread.replies_moderated > 0 and acl.threads.can_approve(forum) %}<li><a href="{{ url('thread_moderated', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first post awaiting review{% endtrans %}"><i class="icon-eye-close"></i> {% trans %}First Unreviewed{% endtrans %}</a></li>{% endif %}
-    {% if thread.replies_reported > 0 and acl.threads.can_mod_posts(thread) %}<li><a href="{{ url('thread_reported', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first reported post{% endtrans %}"><i class="icon-fire"></i> {% trans %}First Reported{% endtrans %}</a></li>{% endif %}
-    {% endif %}
-  </ul>
-</div>
-{% endmacro %}
-
-
-{% macro checkpoint_user(checkpoint) -%}
-{%- if checkpoint.user_id -%}
-<a href="{{ url('user', user=checkpoint.user_id, username=checkpoint.user_slug) }}">{{ checkpoint.user_name }}</a>
-{%- else -%}
-<strong>{{ checkpoint.user_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro checkpoint_forum(checkpoint) -%}
-{%- if checkpoint.old_forum_id -%}
-<a href="{{ url('forum', forum=checkpoint.old_forum_id, slug=checkpoint.old_forum_slug) }}">{{ checkpoint.old_forum_name }}</a>
-{%- else -%}
-<strong>{{ checkpoint.old_forum_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro edit_user(post) -%}
-{%- if post.edit_user_id -%}
-<a href="{{ url('user', user=post.edit_user_id, username=post.edit_user_slug) }}">{{ post.edit_user_name }}</a>
-{%- else -%}
-<strong>{{ post.edit_user_name }}</strong>
-{%- endif -%}
-{%- endmacro %}
-
-
-{% macro editor_extra() %}
-  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Full Editor{% endtrans %}</button>
-{% endmacro %}
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/editor.html" as editor with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=thread.name,parent=forum.name,page=pagination['page']) }}{% endblock %}
+
+{% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
+{{ macros.parents_list(parents) }}
+<li class="active">{{ thread.name }}
+{%- endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
+      {{ self.breadcrumb() }}</li>
+    </ul>
+    <h1>{{ thread.name }}</h1>
+    <ul class="unstyled header-stats">
+      {% if thread.moderated %}<li><i class="icon-eye-close"></i> {% trans %}Not Reviewed{% endtrans %}</li>{% endif %}
+      <li><i class="icon-time"></i> {{ thread.last|reltimesince }}</li>
+      <li><i class="icon-user"></i> {% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}</li>
+      <li><i class="icon-comment"></i> {% if thread.replies > 0 -%}
+        {% trans count=thread.replies, replies=thread.replies|intcomma %}One reply{% pluralize %}{{ replies }} replies{% endtrans %}
+      {%- else -%}
+        {% trans %}No replies{% endtrans %}
+      {%- endif %}</li>
+      {% if thread.closed %}<li><i class="icon-lock"></i> {% trans %}Locked{% endtrans %}</li>{% endif %}
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="thread-buttons">
+    {{ pager() }}
+    {% if user.is_authenticated() %}    
+    {% if acl.threads.can_reply(forum, thread) %}
+    <a href="{{ url('thread_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Reply{% endtrans %}</a>
+    {% endif %}
+    {% if watcher %}
+    <form action="{{ url('thread_unwatch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Remove thread from watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
+    {% if watcher.email %}
+    <form action="{{ url('thread_unwatch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn btn-success tooltip-top" title="{% trans %}Don't e-mail me anymore if anyone replies to this thread{% endtrans %}"><i class="icon-envelope"></i></button></form>
+    {% else %}
+    <form action="{{ url('thread_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}E-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
+    {% endif %}
+    {% else %}
+    <form action="{{ url('thread_watch', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list{% endtrans %}"><i class="icon-bookmark"></i></button></form>
+    <form action="{{ url('thread_watch_email', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><input type="hidden" name="retreat" value="{{ request_path }}"><button type="submit" class="btn tooltip-top" title="{% trans %}Add thread to watched list and e-mail me if anyone replies{% endtrans %}"><i class="icon-envelope"></i></button></form>
+    {% endif %}
+    {% if ignored_posts %}
+    <form action="{{ url('thread_show_hidden', thread=thread.pk, slug=thread.slug) }}" class="form-inline pull-right" method="post"><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"><button type="submit" class="btn"><i class="icon-eye-open"></i> {% trans %}Show Hidden Replies{% endtrans %}</button></form>
+    {% endif %}
+    {% endif %}
+  </div>
+
+  <div class="thread-body">
+    {% for post in posts %}
+    <div id="post-{{ post.pk }}" class="post-wrapper">
+      {% if post.message %}
+      <div class="messages-list">
+        {{ macros.draw_message(post.message) }}
+      </div>
+      {% endif %}
+      {% if post.deleted and not acl.threads.can_see_deleted_posts(forum) %}
+      <div class="post-body post-muted">
+        {% if post.user_id %}
+        <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(50) }}" alt="" class="user-avatar"></a>
+        {% else %}
+        <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
+        {% endif %}
+        <div class="post-content">
+          <div class="post-header">
+            <div class="post-header-compact">
+              {% if post.user_id %}
+              <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
+              {% else %}
+              <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
+              {% endif %}
+              <span class="separator">&ndash;</span>
+              <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
+              {% if post.edits %}
+              <span class="separator">&ndash;</span>
+              {% if acl.threads.can_see_changelog(user, forum, post) %}
+              <a href="{{ url('thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
+              {% else %}
+              <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
+              {% endif %}
+              {% endif %}
+            </div>
+
+            <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
+
+            {% if not post.is_read %}
+            <div class="post-extra">
+              <span class="label label-warning">
+                {% trans %}New{% endtrans %}
+              </span>
+            </div>
+            {% endif %}
+
+          </div>
+          <div class="post-message">
+            {% trans user=edit_user(post), date=post.delete_date|reltimesince|low %}{{ user }} has deleted this reply {{ date }}{% endtrans %}
+          </div>
+        </dv>
+      </div>
+      {% elif post.ignored %}
+      <div class="post-body post-muted">
+        <img src="{{ macros.avatar_guest(60) }}" alt="" class="user-avatar">
+        <div class="post-arrow"></div>
+        <div class="post-content">
+          <div class="post-header">
+            <div class="post-header-compact">
+              <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
+            </div>
+
+            <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
+
+            {% if not post.is_read %}
+            <div class="post-extra">
+              <span class="label label-warning">
+                {% trans %}New{% endtrans %}
+              </span>
+            </div>
+            {% endif %}
+
+          </div>
+          <div class="post-message">
+            {% trans %}This reply was posted by user that is on your ignored list.{% endtrans %}
+          </div>
+        </dv>
+      </div>
+      {% else %}
+      <div class="post-body">
+        {% if post.user_id %}
+        <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}"><img src="{{ post.user.get_avatar(100) }}" alt="" class="user-avatar"></a>
+        {% else %}
+        <img src="{{ macros.avatar_guest(100) }}" alt="" class="user-avatar">
+        {% endif %}
+        <div class="post-arrow"></div>
+        <div class="post-content">
+          <div class="post-header">
+            {% if post.user_id %}
+            <a href="{{ url('user', user=post.user.pk, username=post.user.username_slug) }}" class="post-author">{{ post.user.username }}</a>{% if post.user.get_title() %} {{ user_label(post.user) }}{% endif %}
+            {% else %}
+            <span class="post-author">{{ post.user_name }}</span> <span class="label post-author-label post-label-guest">{% trans %}Unregistered{% endtrans %}</span>
+            {% endif %}
+            <span class="separator">&ndash;</span>
+            <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-date">{{ post.date|reltimesince }}</a>
+            {% if post.edits %}
+            <span class="separator">&ndash;</span>
+            {% if acl.threads.can_see_changelog(user, forum, post) %}
+            <a href="{{ url('thread_changelog', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-changelog tooltip-bottom" title="{% trans %}Show changelog{% endtrans %}">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</a>
+            {% else %}
+            <span class="post-changelog">{% trans edits=post.edits %}One edit{% pluralize %}{{ edits }} edits{% endtrans %}</span>
+            {% endif %}
+            {% endif %}
+
+            {% if user.is_authenticated() and posts_form %}
+            <label class="checkbox post-checkbox"><input form="posts_form" name="{{ posts_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ post.pk }}"{% if posts_form['list_items']['has_value'] and ('' ~ post.pk) in posts_form['list_items']['value'] %} checked="checked"{% endif %}></label>
+            {% endif %}
+
+            <a href="{{ url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-perma tooltip-left" title="{% trans %}Direct link to this post{% endtrans %}">#{{ pagination['start'] + loop.index }}</a>
+
+            <div class="post-extra">
+              {% if post.protected and acl.threads.can_protect(forum) %}
+              <span class="label label-info">
+                {% trans %}Protected{% endtrans %}
+              </span>
+              {% endif %}
+
+              {% if post.deleted %}
+              <span class="label label-inverse">
+                {% trans %}Deleted{% endtrans %}
+              </span>
+              {% endif %}
+
+              {% if post.moderated %}
+              <span class="label label-purple">
+                {% trans %}Unreviewed{% endtrans %}
+              </span>
+              {% endif %}
+
+              {% if acl.threads.can_mod_posts(forum) and post.reported %}
+              <span class="label label-important">
+                {% trans %}Reported{% endtrans %}
+              </span>
+              {% endif %}
+
+              {% if not post.is_read %}
+              <span class="label label-warning">
+                {% trans %}New{% endtrans %}
+              </span>
+              {% endif %}
+            </div>
+          </div>
+          <div class="post-message">
+            <div class="markdown js-extra">
+              <article>
+                {{ post.post_preparsed|markdown_final|safe }}
+              </article>
+            </div>
+            {% if post.user.signature %}
+            <div class="post-signature">
+              <div class="markdown">
+                {{ post.user.signature_preparsed|markdown_final|safe }}
+              </div>
+            </div>
+            {% endif %}
+          </div>
+          <div class="post-footer">{% filter trim %}
+            {% if acl.threads.can_see_post_score(forum) %}
+            <div{% if user.is_authenticated() and user.pk != post.user_id %} class="post-rating-actions"{% endif %}>
+              <div class="post-rating">
+                {% if acl.threads.can_see_post_score(forum) == 1 %}
+                <span class="post-score post-score-total{% if (post.upvotes - post.downvotes) > 0 %} post-score-good{% elif (post.upvotes - post.downvotes) < 0 %} post-score-bad{% endif %}">{{ post.upvotes - post.downvotes }}</span>
+                {% elif acl.threads.can_see_post_score(forum) == 2%}
+                <span class="post-score post-score-upvotes{% if post.upvotes %} post-score-good{% endif %}">{{ post.upvotes }}</span>
+                {% endif %}
+                {% if user.is_authenticated() and user.pk != post.user_id and acl.threads.can_upvote_posts(forum) %}
+                <form action="{{ url('post_upvote', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-upvote" method="post">
+                  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                  <button type="submit" class="btn btn-link post-like"{% if post.karma_vote and post.karma_vote.score > 0 %} disabled="disabled"{% endif %}>{% trans %}Like{% endtrans %}</button>
+                </form>
+                {% else %}
+                <span class="post-{% if post.upvotes %}like{% else %}neutral{% endif %}">{% trans %}Likes{% endtrans %}</span>
+                {% endif %}
+              {% if acl.threads.can_see_post_score(forum) == 2 %}
+              </div>
+              <div class="post-rating">
+                <span class="post-score post-score-downvotes{% if post.downvotes %} post-score-bad{% endif %}">{{ post.downvotes }}</span>
+              {% endif %}
+                {% if user.is_authenticated() and user.pk != post.user_id and acl.threads.can_downvote_posts(forum) %}
+                <form action="{{ url('post_downvote', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-downvote" method="post">
+                  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                  <button type="submit" class="btn btn-link post-hate"{% if post.karma_vote and post.karma_vote.score < 0 %} disabled="disabled"{% endif %}>{% trans %}Dislike{% endtrans %}</button>
+                </form>
+                {% elif acl.threads.can_see_post_score(forum) == 2 %}
+                <span class="post-{% if post.downvotes %}hate{% else %}neutral{% endif %}">{% trans %}Dislikes{% endtrans %}</span>
+                {% endif %}
+              </div>
+              {% if acl.threads.can_see_post_votes(forum) %}
+              <div class="post-rating">
+                <a href="{{ url('post_votes', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans %}Show Votes{% endtrans %}</a>
+              </div>
+              {% endif %}
+            </div>
+            {% endif %}
+
+            {% if user.is_authenticated() %}
+            <div class="post-actions">
+              {% if acl.users.can_see_users_trails() -%}
+              <a href="{{ url('post_info', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-trail">{% trans %}Info{% endtrans %}</a>
+              {% endif %}
+              {% if post.reported and acl.reports.can_handle() and acl.threads.can_mod_posts(forum) %}
+              <a href="{{ url('post_report_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}">{% trans %}Show report{% endtrans %}</a>
+              {% endif %}
+              {% if acl.reports.can_report() %}
+              {% if post.reported_by(user) %}
+              <a href="{{ url('post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="tooltip-top" title="{% trans %}You have already reported this post.{% endtrans %}" disabled="disabled">{% trans %}Reported{% endtrans %}</a>
+              {% else %}
+              <form action="{{ url('post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-report" method="post" autocomplete="off">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-report tooltip-top" title="{% trans %}Bring this post to moderator attention.{% endtrans %}">{% trans %}Report{% endtrans %}</button>
+              </form>
+              {% endif %}
+              {% endif %}
+              {% if acl.threads.can_edit_thread(user, forum, thread, post) and thread.start_post_id == post.pk %}
+              <a href="{{ url('thread_edit', thread=thread.pk, slug=thread.slug) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
+              {% elif acl.threads.can_edit_reply(user, forum, thread, post) %}
+              <a href="{{ url('post_edit', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="post-edit">{% trans %}Edit{% endtrans %}</a>
+              {%- endif %}
+              {% if acl.threads.can_reply(forum, thread) %}<a href="{{ url('thread_reply', thread=thread.pk, slug=thread.slug, quote=post.pk) }}" class="post-reply">{% trans %}Reply{% endtrans %}</a>{% endif %}
+            </div>
+            {% if post.pk == thread.start_post_id %}
+            <div class="post-actions">
+              {% if acl.threads.can_delete_thread(user, forum, thread, post) %}
+              {% if post.deleted %}
+              <form action="{{ url('thread_show', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this thread visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
+              </form>
+              {% else %}
+              <form action="{{ url('thread_hide', thread=thread.pk, slug=thread.slug) }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this thread from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
+              </form>
+              {% endif %}
+              {% endif %}
+              {% if acl.threads.can_delete_thread(user, forum, thread, post) == 2 %}
+              <form action="{{ url('thread_delete', thread=thread.pk, slug=thread.slug) }}" class="form-inline prompt-delete-thread" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this thread for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
+              </form>
+              {% endif %}
+            </div>
+            {% elif post.pk != thread.start_post_id and acl.threads.can_delete_post(user, forum, thread, post) %}
+            <div class="post-actions">
+              {% if acl.threads.can_delete_post(user, forum, thread, post) %}
+              {% if post.deleted %}
+              <form action="{{ url('post_show', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Make this reply visible to other users{% endtrans %}">{% trans %}Restore{% endtrans %}</button>
+              </form>
+              {% else %}
+              <form action="{{ url('post_hide', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link btn-hide tooltip-top" title="{% trans %}Hide this reply from other users{% endtrans %}">{% trans %}Hide{% endtrans %}</button>
+              </form>
+              {% endif %}
+              {% endif %}
+              {% if acl.threads.can_delete_post(user, forum, thread, post) == 2 -%}
+              <form action="{{ url('post_delete', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline prompt-delete-post" method="post">
+                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Delete this reply for good{% endtrans %}">{% trans %}Delete{% endtrans %}</button>
+              </form>
+              {% endif %}
+            </div>
+            {% endif %}
+            {% endif %}
+          {% endfilter %}</div>
+        </div>
+      </div>
+      {% endif %}
+    </div>
+
+    {% if post.checkpoints_visible %}
+    <div class="post-checkpoints">
+      {% for checkpoint in post.checkpoints_visible %}
+      <div class="post-checkpoint{% if checkpoint.deleted %} checkpoint-deleted{% endif %}">
+        <hr>
+        <span>
+          {%- if checkpoint.action == 'limit' -%}
+          <i class="icon-lock"></i> {% trans  %}This thread has reached its post limit and has been closed.{% endtrans %}
+          {%- elif checkpoint.action == 'accepted' -%}
+          <i class="icon-ok"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} accepted this thread {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'closed' -%}
+          <i class="icon-lock"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} closed this thread {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'opened' -%}
+          <i class="icon-lock"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} opened this thread {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'deleted' -%}
+          <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} deleted this thread {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'undeleted' -%}
+          <i class="icon-trash"></i> {% trans user=checkpoint_user(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} restored this thread {{ date }}{% endtrans %}
+          {%- elif checkpoint.action == 'moved' and acl.forums.can_see(checkpoint.old_forum_id) -%}
+          <i class="icon-arrow-right"></i> {% trans user=checkpoint_user(checkpoint), forum=checkpoint_forum(checkpoint), date=checkpoint.date|reltimesince|low %}{{ user }} moved this thread from {{ forum }} {{ date }}{% endtrans %}
+          {%- endif -%}
+          {% if user.is_authenticated() %}
+          {% if acl.threads.can_delete_checkpoint(forum) %}
+          {% if checkpoint.deleted %}
+          <form action="{{ url('post_checkpoint_show', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+            <button type="submit" class="btn btn-link btn-show">{% trans %}Restore{% endtrans %}</button>
+          </form>
+          {% else %}
+          <form action="{{ url('post_checkpoint_hide', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+            <button type="submit" class="btn btn-link btn-hide">{% trans %}Hide{% endtrans %}</button>
+          </form>
+          {% endif %}
+          {% endif %}
+          {% if acl.threads.can_delete_checkpoint(forum) == 2 %}
+          <form action="{{ url('post_checkpoint_delete', slug=thread.slug, thread=thread.pk, checkpoint=checkpoint.pk) }}" method="post" class="form-inline prompt-delete-checkpoint">
+            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+            <input type="hidden" name="retreat" value="{{ request_path }}#post-{{ post.pk }}">
+            <button type="submit" class="btn btn-link btn-delete">{% trans %}Delete{% endtrans %}</button>
+          </form>
+          {% endif %}
+          {% endif %}
+        </span>
+      </div>
+      {% endfor %}
+    </div>
+    {% endif %}
+    {% endfor %}
+  </div>
+
+  {% if user.is_authenticated() and (thread_form or posts_form) %}
+  <div class="thread-moderation">
+    {% if thread_form%}
+    <form id="thread_form" class="form-inline pull-left" action="{{ request_path }}" method="POST">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <input type="hidden" name="origin" value="thread_form">
+      {{ form_theme.input_select(thread_form['thread_action'],width=3) }}
+      <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+    </form>
+    {% endif %}
+    {% if posts_form%}
+    <form id="posts_form" class="form-inline pull-right" action="{{ request_path }}" method="POST">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <input type="hidden" name="origin" value="posts_form">
+      {{ form_theme.input_select(posts_form['list_action'],width=3) }}
+      <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+    </form>
+    {% endif %}
+  </div>
+  {% endif %}
+
+  <div class="thread-buttons">
+    {{ pager(false) }}
+    {% if user.is_authenticated() and acl.threads.can_reply(forum, thread) %}
+    <a href="{{ url('thread_reply', thread=thread.pk, slug=thread.slug) }}" class="btn btn-inverse pull-right"><i class="icon-pencil"></i> {% trans %}Reply{% endtrans %}</a>
+    {% elif not user.is_authenticated() and not user.is_crawler() %}
+    <p class="lead thread-signin-message"><a href="{{ url('sign_in') }}">{% trans %}Sign in or register to reply.{% endtrans %}</a></p>
+    {% endif %}
+  </div>
+
+  {% if user.is_authenticated() and acl.threads.can_reply(forum, thread) %}
+  <div class="thread-quick-reply">
+    <form action="{{ url('thread_reply', thread=thread.pk, slug=thread.slug) }}" method="post">
+      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+      <input type="hidden" name="quick_reply" value="1">
+      <img src="{{ user.get_avatar(100) }}" alt="{% trans %}Your Avatar{% endtrans %}" class="user-avatar">
+      {{ editor.editor(quick_reply.post, _('Post Reply'), extra=editor_extra()) }}
+    </form>
+  </div>
+  {% endif %}
+
+</div>
+{% endblock %}
+
+{% block stylesheets %}{{ super() }}
+<link href="{{ STATIC_URL }}cranefly/highlight/styles/monokai.css" rel="stylesheet">
+{% endblock %}
+
+{% block javascripts -%}{{ super() }}
+  <script src="{{ STATIC_URL }}cranefly/highlight/highlight.pack.js"></script>
+  <script type="text/javascript">
+    var l_post_reported = "{{ _('Reported!') }}";
+    hljs.tabReplace = '    ';
+    hljs.initHighlightingOnLoad();
+    EnhancePostsMD();
+    {%- if user.is_authenticated() %}
+    $(function () {
+      $('#thread_form').submit(function() {
+        if ($('#id_thread_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete this thread? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });
+      $('#posts_form').submit(function() {
+        if ($('.post-checkbox[]:checked').length == 0) {
+          alert("{% trans %}You have to select at least one post.{% endtrans %}");
+          return false;
+        }
+        if ($('#id_list_action').val() == 'merge') {
+          if ($('.post-checkbox[]:checked').length < 2) {
+              alert("{% trans %}You have to select at least two posts you want to merge.{% endtrans %}");
+              return false;
+          }
+          var decision = confirm("{% trans %}Are you sure you want to merge selected posts? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        if ($('#id_list_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete selected posts? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });
+      $('.prompt-delete-thread').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this thread?{% endtrans %}");
+          return decision;
+      });
+      $('.prompt-delete-post').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this post?{% endtrans %}");
+          return decision;
+      });
+      $('.prompt-delete-checkpoint').submit(function() {
+          var decision = confirm("{% trans %}Are you sure you want to delete this checkpoint?{% endtrans %}");
+          return decision;
+      });
+    });
+    {% endif %}
+  </script>
+  {% if user.is_authenticated() and acl.threads.can_reply(forum, thread) %}
+  {{ editor.js() }}
+  {% endif %}
+{%- endblock %}
+
+
+{% macro user_label(user) -%}
+<{% if user.rank and user.rank.as_tab %}a href="{{ url('users', slug=user.rank.slug) }}"{% else %}span{% endif %} class="label post-author-label{% if user.rank and user.rank.style %} post-label-{{ user.rank.style }}{% endif %}">{{ user.get_title() }}</{% if user.rank and user.rank.as_tab%}a{% else %}span{% endif %}>
+{%- endmacro %}
+
+
+{% macro pager(extra=true) %}
+<div class="pagination pull-left">
+  <ul>
+    {% if pagination['total'] > 0 %}
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('thread', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('thread', slug=thread.slug, thread=thread.id, page=pagination['prev']) }}{% else %}{{ url('thread', slug=thread.slug, thread=thread.id) }}{% endif %}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('thread', slug=thread.slug, thread=thread.id, page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Newest Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 and pagination['next'] < pagination['total'] %}<li><a href="{{ url('thread', slug=thread.slug, thread=thread.id, page=pagination['total']) }}" class="tooltip-top" title="{% trans %}Go to last page{% endtrans %}">{% trans %}Last{% endtrans %} <i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {% endif %}
+    {% if extra and user.is_authenticated() %}
+    {% if not is_read %}<li><a href="{{ url('thread_new', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first unread{% endtrans %}"><i class="icon-star"></i> {% trans %}First Unread{% endtrans %}</a></li>{% endif %}
+    {% if thread.replies_moderated > 0 and acl.threads.can_approve(forum) %}<li><a href="{{ url('thread_moderated', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first post awaiting review{% endtrans %}"><i class="icon-eye-close"></i> {% trans %}First Unreviewed{% endtrans %}</a></li>{% endif %}
+    {% if thread.replies_reported > 0 and acl.threads.can_mod_posts(thread) %}<li><a href="{{ url('thread_reported', slug=thread.slug, thread=thread.id) }}" class="tooltip-top" title="{% trans %}Go to first reported post{% endtrans %}"><i class="icon-fire"></i> {% trans %}First Reported{% endtrans %}</a></li>{% endif %}
+    {% endif %}
+  </ul>
+</div>
+{% endmacro %}
+
+
+{% macro checkpoint_user(checkpoint) -%}
+{%- if checkpoint.user_id -%}
+<a href="{{ url('user', user=checkpoint.user_id, username=checkpoint.user_slug) }}">{{ checkpoint.user_name }}</a>
+{%- else -%}
+<strong>{{ checkpoint.user_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro checkpoint_forum(checkpoint) -%}
+{%- if checkpoint.old_forum_id -%}
+<a href="{{ url('forum', forum=checkpoint.old_forum_id, slug=checkpoint.old_forum_slug) }}">{{ checkpoint.old_forum_name }}</a>
+{%- else -%}
+<strong>{{ checkpoint.old_forum_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro edit_user(post) -%}
+{%- if post.edit_user_id -%}
+<a href="{{ url('user', user=post.edit_user_id, username=post.edit_user_slug) }}">{{ post.edit_user_name }}</a>
+{%- else -%}
+<strong>{{ post.edit_user_name }}</strong>
+{%- endif -%}
+{%- endmacro %}
+
+
+{% macro editor_extra() %}
+  <button id="editor-preview" name="preview" type="submit" class="btn pull-right">{% trans %}Full Editor{% endtrans %}</button>
+{% endmacro %}

+ 47 - 47
templates/cranefly/usercp/avatar.html

@@ -1,48 +1,48 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Change your Avatar')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <div class="media usercp-avatar-menu">
-    <span class="pull-left">
-      <img class="media-object" src="{{ user.get_avatar(126) }}">
-    </span>
-    <div class="media-body">
-      <div class="form-header">
-        <h1>{% if user.avatar_type == 'upload' -%}
-        {% trans %}Uploaded Avatar{% endtrans %}
-        {%- elif user.avatar_type == 'gallery' -%}
-        {% trans %}Gallery Avatar{% endtrans %}
-        {%- else -%}
-        {% trans %}Gravatar{% endtrans %}
-        {%- endif %}</h1>
-      </div>
-      <ul class="unstyled">
-        {% if 'gravatar' in settings.avatars_types and user.avatar_type != 'gravatar' %}<li><form action="{{ url('usercp_avatar_gravatar') }}" method="post" class="form-button"><button type="submit" class="btn btn-link"><i class="icon-share"></i> {% trans %}Use Gravatar{% endtrans %}</button><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"></form></li>{% endif %}
-        {% if 'gallery' in settings.avatars_types %}<li><a href="{{ url('usercp_avatar_gallery') }}" class="btn btn-link"><i class="icon-th"></i> {% trans %}Pick Avatar from Gallery{% endtrans %}</a></li>{% endif %}
-        {% if user.avatar_type == 'upload' %}<li id="crop-js" style="display: none;"><a href="{{ url('usercp_avatar_crop') }}" class="btn btn-link"><i class="icon-fullscreen"></i> {% trans %}Crop Your Avatar{% endtrans %}</a></li>{% endif %}
-        {% if 'upload' in settings.avatars_types %}<li><a href="{{ url('usercp_avatar_upload') }}" class="btn btn-link"><i class="icon-picture"></i> {% trans %}Upload Avatar{% endtrans %}</a></li>{% endif %}
-      </ul>
-    </div>
-  </div>
-
-</div>
-{% endblock %}
-
-{% block javascripts %}
-{{ super() }}
-    <script type="text/javascript">
-      $(function($){
-        $('#crop-js').show();
-      });
-    </script>
+{% extends "cranefly/usercp/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Change your Avatar')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="media usercp-avatar-menu">
+    <span class="pull-left">
+      <img class="media-object" src="{{ user.get_avatar(126) }}">
+    </span>
+    <div class="media-body">
+      <div class="form-header">
+        <h1>{% if user.avatar_type == 'upload' -%}
+        {% trans %}Uploaded Avatar{% endtrans %}
+        {%- elif user.avatar_type == 'gallery' -%}
+        {% trans %}Gallery Avatar{% endtrans %}
+        {%- else -%}
+        {% trans %}Gravatar{% endtrans %}
+        {%- endif %}</h1>
+      </div>
+      <ul class="unstyled">
+        {% if 'gravatar' in settings.avatars_types and user.avatar_type != 'gravatar' %}<li><form action="{{ url('usercp_avatar_gravatar') }}" method="post" class="form-button"><button type="submit" class="btn btn-link"><i class="icon-share"></i> {% trans %}Use Gravatar{% endtrans %}</button><input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}"></form></li>{% endif %}
+        {% if 'gallery' in settings.avatars_types %}<li><a href="{{ url('usercp_avatar_gallery') }}" class="btn btn-link"><i class="icon-th"></i> {% trans %}Pick Avatar from Gallery{% endtrans %}</a></li>{% endif %}
+        {% if user.avatar_type == 'upload' %}<li id="crop-js" style="display: none;"><a href="{{ url('usercp_avatar_crop') }}" class="btn btn-link"><i class="icon-fullscreen"></i> {% trans %}Crop Your Avatar{% endtrans %}</a></li>{% endif %}
+        {% if 'upload' in settings.avatars_types %}<li><a href="{{ url('usercp_avatar_upload') }}" class="btn btn-link"><i class="icon-picture"></i> {% trans %}Upload Avatar{% endtrans %}</a></li>{% endif %}
+      </ul>
+    </div>
+  </div>
+
+</div>
+{% endblock %}
+
+{% block javascripts %}
+{{ super() }}
+    <script type="text/javascript">
+      $(function($){
+        $('#crop-js').show();
+      });
+    </script>
 {% endblock %}
 {% endblock %}

+ 21 - 21
templates/cranefly/usercp/avatar_banned.html

@@ -1,22 +1,22 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Change your Avatar')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  <div class="form-header">
-    <h1>{% trans %}Change your Avatar{% endtrans %}</h1>
-  </div>
-
-  {% if user.avatar_ban_reason_user %}
-  <p class="lead">{% trans username=user.username %}{{ username }}, your ability to change your avatar has been removed for following reason:{% endtrans %}</p>
-  <div class="markdown">
-  {{ user.avatar_ban_reason_user|markdown|safe }}
-  </div>
-  {% else %}
-  <p class="lead">{% trans username=user.username %}{{ username }}, your ability to change your avatar has been removed.{% endtrans %}</p>
-  {% endif %}
-</div>
+{% extends "cranefly/usercp/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Change your Avatar')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  <div class="form-header">
+    <h1>{% trans %}Change your Avatar{% endtrans %}</h1>
+  </div>
+
+  {% if user.avatar_ban_reason_user %}
+  <p class="lead">{% trans username=user.username %}{{ username }}, your ability to change your avatar has been removed for following reason:{% endtrans %}</p>
+  <div class="markdown">
+  {{ user.avatar_ban_reason_user|markdown|safe }}
+  </div>
+  {% else %}
+  <p class="lead">{% trans username=user.username %}{{ username }}, your ability to change your avatar has been removed.{% endtrans %}</p>
+  {% endif %}
+</div>
 {% endblock %}
 {% endblock %}

+ 90 - 90
templates/cranefly/usercp/avatar_crop.html

@@ -1,90 +1,90 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Crop Avatar')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container usercp-avatar-crop">
-
-  <form action="{% if after_upload %}{{ url('usercp_avatar_upload_crop') }}{% else %}{{ url('usercp_avatar_crop') }}{% endif %}" method="post">
-
-    <div class="form-header">
-      <div class="avatar-crop-preview">
-        <img src="{{ MEDIA_URL }}{{ source }}" id="preview" alt="{% trans %}Avatar Preview{% endtrans %}" class="jcrop-preview" />
-      </div>
-      <h1>{% trans %}Crop Avatar{% endtrans %} <small>{% trans %}Change your Avatar{% endtrans %}</small></h1>
-      <a href="{{ url('usercp_avatar') }}" class="btn btn-inverse pull-right">{% trans %}Cancel{% endtrans %}</a>
-      <button name="save" type="submit" class="btn btn-danger pull-right">{% trans %}Crop Avatar{% endtrans %}</button>
-    </div>
-
-    {% if message %}
-    <div class="messages-list">
-      {{ macros.draw_message(message) }}
-    </div>
-    {% endif %}
-
-    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-    <input type="hidden" id="crop-b" name="crop_b" value="">
-    <input type="hidden" id="crop-w" name="crop_w" value="">
-    <input type="hidden" id="crop-x" name="crop_x" value="">
-    <input type="hidden" id="crop-y" name="crop_y" value="">
-
-    <div class="avatar-crop-target"><img src="{{ MEDIA_URL }}{{ source }}" id="target" alt="{% trans %}Uploaded Image{% endtrans %}"></div>
-
-  </form>
-  
-</div>
-{% endblock %}
-
-{% block javascripts %}
-{{ super() }}
-    <script src="{{ STATIC_URL }}cranefly/js/jquery.Jcrop.min.js"></script>
-    <script type="text/javascript">
-      $(function($){
-        // Create variables (in this scope) to hold the API and image size
-        var jcrop_api, boundx, boundy;
-        var crop_b = $('#crop-b');
-        var crop_w = $('#crop-w');
-        var crop_x = $('#crop-x');
-        var crop_y = $('#crop-y');
-        var afx = $('.avatar-crop-preview').width() / {{ avatar_size }};
-
-        $('#target').Jcrop({
-          onChange: updatePreview,
-          onSelect: updatePreview,
-          aspectRatio: 1,
-          minSize: [50, 50],
-          setSelect: [ 0, 0, {{ avatar_size }}, {{ avatar_size }} ],
-        },function(){
-          // Use the API to get the real image size
-          var bounds = this.getBounds();
-          boundx = bounds[0];
-          boundy = bounds[1];
-          $('#crop-b').val(boundx);
-          // Store the API in the jcrop_api variable
-          jcrop_api = this;
-        });
-
-        function updatePreview(c)
-        {
-          if (parseInt(c.w) > 0)
-          {
-            var rx = {{ avatar_size }} / c.w;
-            var ry = {{ avatar_size }} / c.h;
-            
-            $(crop_w).val(c.w);
-            $(crop_x).val(c.x);
-            $(crop_y).val(c.y);
-
-            $('#preview').css({
-              width: Math.round(rx * boundx * afx) + 'px',
-              height: Math.round(ry * boundy * afx) + 'px',
-              marginLeft: '-' + Math.round(rx * c.x * afx) + 'px',
-              marginTop: '-' + Math.round(ry * c.y * afx) + 'px'
-            });
-          }
-        };
-      });
-    </script>
-{% endblock %}
+{% extends "cranefly/usercp/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Crop Avatar')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container usercp-avatar-crop">
+
+  <form action="{% if after_upload %}{{ url('usercp_avatar_upload_crop') }}{% else %}{{ url('usercp_avatar_crop') }}{% endif %}" method="post">
+
+    <div class="form-header">
+      <div class="avatar-crop-preview">
+        <img src="{{ MEDIA_URL }}{{ source }}" id="preview" alt="{% trans %}Avatar Preview{% endtrans %}" class="jcrop-preview" />
+      </div>
+      <h1>{% trans %}Crop Avatar{% endtrans %} <small>{% trans %}Change your Avatar{% endtrans %}</small></h1>
+      <a href="{{ url('usercp_avatar') }}" class="btn btn-inverse pull-right">{% trans %}Cancel{% endtrans %}</a>
+      <button name="save" type="submit" class="btn btn-danger pull-right">{% trans %}Crop Avatar{% endtrans %}</button>
+    </div>
+
+    {% if message %}
+    <div class="messages-list">
+      {{ macros.draw_message(message) }}
+    </div>
+    {% endif %}
+
+    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+    <input type="hidden" id="crop-b" name="crop_b" value="">
+    <input type="hidden" id="crop-w" name="crop_w" value="">
+    <input type="hidden" id="crop-x" name="crop_x" value="">
+    <input type="hidden" id="crop-y" name="crop_y" value="">
+
+    <div class="avatar-crop-target"><img src="{{ MEDIA_URL }}{{ source }}" id="target" alt="{% trans %}Uploaded Image{% endtrans %}"></div>
+
+  </form>
+  
+</div>
+{% endblock %}
+
+{% block javascripts %}
+{{ super() }}
+    <script src="{{ STATIC_URL }}cranefly/js/jquery.Jcrop.min.js"></script>
+    <script type="text/javascript">
+      $(function($){
+        // Create variables (in this scope) to hold the API and image size
+        var jcrop_api, boundx, boundy;
+        var crop_b = $('#crop-b');
+        var crop_w = $('#crop-w');
+        var crop_x = $('#crop-x');
+        var crop_y = $('#crop-y');
+        var afx = $('.avatar-crop-preview').width() / {{ avatar_size }};
+
+        $('#target').Jcrop({
+          onChange: updatePreview,
+          onSelect: updatePreview,
+          aspectRatio: 1,
+          minSize: [50, 50],
+          setSelect: [ 0, 0, {{ avatar_size }}, {{ avatar_size }} ],
+        },function(){
+          // Use the API to get the real image size
+          var bounds = this.getBounds();
+          boundx = bounds[0];
+          boundy = bounds[1];
+          $('#crop-b').val(boundx);
+          // Store the API in the jcrop_api variable
+          jcrop_api = this;
+        });
+
+        function updatePreview(c)
+        {
+          if (parseInt(c.w) > 0)
+          {
+            var rx = {{ avatar_size }} / c.w;
+            var ry = {{ avatar_size }} / c.h;
+            
+            $(crop_w).val(c.w);
+            $(crop_x).val(c.x);
+            $(crop_y).val(c.y);
+
+            $('#preview').css({
+              width: Math.round(rx * boundx * afx) + 'px',
+              height: Math.round(ry * boundy * afx) + 'px',
+              marginLeft: '-' + Math.round(rx * c.x * afx) + 'px',
+              marginTop: '-' + Math.round(ry * c.y * afx) + 'px'
+            });
+          }
+        };
+      });
+    </script>
+{% endblock %}

+ 36 - 36
templates/cranefly/usercp/avatar_gallery.html

@@ -1,37 +1,37 @@
-{% extends "cranefly/usercp/avatar.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Avatars Gallery')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  <div class="form-header">
-    <h1>{% trans %}Pick Avatar from Gallery{% endtrans %} <small>{% trans %}Change your Avatar{% endtrans %}</small></h1>
-  </div>
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <div class="usercp-avatar-select">
-  {% for gallery in galleries %}
-    {% if loop.index0 > 0 and gallery.avatars %}<hr>{% endif %}
-    <div class="usercp-avatar-gallery">
-      {% for avatar in gallery.avatars %}
-      <form action="{{ url('usercp_avatar_gallery') }}" class="usercp-avatar-select-form" method="post">
-        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-        <input type="hidden" name="avatar_image" value="{{ avatar }}">
-        <button type="submit" class="btn btn-link usercp-btn-avatar">
-          <img src="{{ STATIC_URL }}avatars/{{ avatar }}" alt="{% trans %}Gallery Avatar{% endtrans %}" class="img-polaroid">
-        </button>
-      </form>
-      {% endfor %}
-    </div>
-  {% endfor %}
-  </div>
-  
-</div>
+{% extends "cranefly/usercp/avatar.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Avatars Gallery')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  <div class="form-header">
+    <h1>{% trans %}Pick Avatar from Gallery{% endtrans %} <small>{% trans %}Change your Avatar{% endtrans %}</small></h1>
+  </div>
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="usercp-avatar-select">
+  {% for gallery in galleries %}
+    {% if loop.index0 > 0 and gallery.avatars %}<hr>{% endif %}
+    <div class="usercp-avatar-gallery">
+      {% for avatar in gallery.avatars %}
+      <form action="{{ url('usercp_avatar_gallery') }}" class="usercp-avatar-select-form" method="post">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        <input type="hidden" name="avatar_image" value="{{ avatar }}">
+        <button type="submit" class="btn btn-link usercp-btn-avatar">
+          <img src="{{ STATIC_URL }}avatars/{{ avatar }}" alt="{% trans %}Gallery Avatar{% endtrans %}" class="img-polaroid">
+        </button>
+      </form>
+      {% endfor %}
+    </div>
+  {% endfor %}
+  </div>
+  
+</div>
 {% endblock %}
 {% endblock %}

+ 40 - 40
templates/cranefly/usercp/avatar_upload.html

@@ -1,41 +1,41 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Upload Avatar')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  <div class="form-header">
-    <h1>{% trans %}Upload Avatar{% endtrans %} <small>{% trans %}Change your Avatar{% endtrans %}</h1>
-  </div>
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <form action="{{ url('usercp_avatar_upload') }}" method="post" enctype="multipart/form-data">
-    {{ form_theme.hidden_fields(form) }}
-    <input type="hidden" id="js_check" name="js_check" value="0">
-    {{ form_theme.row(form.avatar_upload) }}
-    <div class="form-actions">
-      <button name="save" type="submit" class="btn btn-primary">{% trans %}Upload Avatar{% endtrans %}</button>
-      <a href="{{ url('usercp_avatar') }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
-    </div>
-  </form>
-  
-</div>
-{% endblock %}
-
-
-{% block javascripts %}
-{{ super() }}
-    <script type="text/javascript">
-      $(function($){
-        $('#js_check').val(1);
-      });
-    </script>
+{% extends "cranefly/usercp/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Upload Avatar')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  <div class="form-header">
+    <h1>{% trans %}Upload Avatar{% endtrans %} <small>{% trans %}Change your Avatar{% endtrans %}</h1>
+  </div>
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <form action="{{ url('usercp_avatar_upload') }}" method="post" enctype="multipart/form-data">
+    {{ form_theme.hidden_fields(form) }}
+    <input type="hidden" id="js_check" name="js_check" value="0">
+    {{ form_theme.row(form.avatar_upload) }}
+    <div class="form-actions">
+      <button name="save" type="submit" class="btn btn-primary">{% trans %}Upload Avatar{% endtrans %}</button>
+      <a href="{{ url('usercp_avatar') }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+    </div>
+  </form>
+  
+</div>
+{% endblock %}
+
+
+{% block javascripts %}
+{{ super() }}
+    <script type="text/javascript">
+      $(function($){
+        $('#js_check').val(1);
+      });
+    </script>
 {% endblock %}
 {% endblock %}

+ 32 - 32
templates/cranefly/usercp/credentials.html

@@ -1,33 +1,33 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Change your Sign-In Credentials')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  <div class="form-header">
-    <h1>{% trans %}Change Sign-In Credentials{% endtrans %}</h1>
-  </div>
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <form action="{{ url('usercp_credentials') }}" method="post">
-    {{ form_theme.hidden_fields(form) }}
-    <div class="form-fields">
-      {{ form_theme.row(form.new_email) }}
-      {{ form_theme.row(form.new_password) }}
-      {{ form_theme.row(form.current_password) }}
-    </div>
-    <div class="form-actions">
-  	  <button name="save" type="submit" class="btn btn-primary">{% trans %}Change Credentials{% endtrans %}</button>
-    </div>
-  </form>
-  
-</div>
+{% extends "cranefly/usercp/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Change your Sign-In Credentials')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  <div class="form-header">
+    <h1>{% trans %}Change Sign-In Credentials{% endtrans %}</h1>
+  </div>
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <form action="{{ url('usercp_credentials') }}" method="post">
+    {{ form_theme.hidden_fields(form) }}
+    <div class="form-fields">
+      {{ form_theme.row(form.new_email) }}
+      {{ form_theme.row(form.new_password) }}
+      {{ form_theme.row(form.current_password) }}
+    </div>
+    <div class="form-actions">
+  	  <button name="save" type="submit" class="btn btn-primary">{% trans %}Change Credentials{% endtrans %}</button>
+    </div>
+  </form>
+  
+</div>
 {% endblock %}
 {% endblock %}

+ 20 - 20
templates/cranefly/usercp/layout.html

@@ -1,21 +1,21 @@
-{% extends "cranefly/layout.html" %}
-
-{% block content %}
-<div class="row usercp">
-  <div class="span3">
-
-    <ul class="nav nav-pills usercp-tabs">
-      <li class="nav-header">{% trans %}Your Control Panel{% endtrans %}</li>
-      {% for link in tabs %}
-      <li{% if link.active %} class="active"{% endif %}><a href="{{ url(link.route) }}">{{ link.name }}</a></li>
-      {% endfor %}
-    </ul>
-
-  </div>
-  <div class="span9 usercp-action">
-
-    {% block action %}{% endblock %}
-
-  </div>
-</div>
+{% extends "cranefly/layout.html" %}
+
+{% block content %}
+<div class="row usercp">
+  <div class="span3">
+
+    <ul class="nav nav-pills usercp-tabs">
+      <li class="nav-header">{% trans %}Your Control Panel{% endtrans %}</li>
+      {% for link in tabs %}
+      <li{% if link.active %} class="active"{% endif %}><a href="{{ url(link.route) }}">{{ link.name }}</a></li>
+      {% endfor %}
+    </ul>
+
+  </div>
+  <div class="span9 usercp-action">
+
+    {% block action %}{% endblock %}
+
+  </div>
+</div>
 {% endblock %}
 {% endblock %}

+ 46 - 46
templates/cranefly/usercp/options.html

@@ -1,47 +1,47 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Change Forum Options')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  <div class="form-header">
-    <h1>{% trans %}Change Forum Options{% endtrans %}</h1>
-  </div>
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <form action="{{ url('usercp_options') }}" method="post">
-    {{ form_theme.hidden_fields(form) }}
-    <div class="form-fields">
-      {# form_theme.form_widget(form, width=9) #}
-      <fieldset>
-        <legend>{% trans %}Privacy{% endtrans %}</legend>
-        {{ form_theme.row(form.hide_activity) }}
-        {{ form_theme.row(form.allow_pds) }}
-      </fieldset>
-      <fieldset>
-        <legend>{% trans %}Forum Options{% endtrans %}</legend>
-        {{ form_theme.row(form.timezone) }}
-        {{ form_theme.row(form.newsletters, attrs={'inline': _("Yes, I want to subscribe forum newsletter.")}) }}
-      </fieldset>
-      <fieldset>
-        <legend>{% trans %}Watching Threads{% endtrans %}</legend>
-        {{ form_theme.row(form.subscribe_start) }}
-        {{ form_theme.row(form.subscribe_reply) }}
-      </fieldset>
-    </div>
-    <div class="form-actions">
-      <button name="save" type="submit" class="btn btn-primary">{% trans %}Change Options{% endtrans %}</button>
-      <a href="{{ url('usercp') }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
-    </div>
-  </form>
-  
-</div>
+{% extends "cranefly/usercp/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Change Forum Options')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  <div class="form-header">
+    <h1>{% trans %}Change Forum Options{% endtrans %}</h1>
+  </div>
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <form action="{{ url('usercp_options') }}" method="post">
+    {{ form_theme.hidden_fields(form) }}
+    <div class="form-fields">
+      {# form_theme.form_widget(form, width=9) #}
+      <fieldset>
+        <legend>{% trans %}Privacy{% endtrans %}</legend>
+        {{ form_theme.row(form.hide_activity) }}
+        {{ form_theme.row(form.allow_pds) }}
+      </fieldset>
+      <fieldset>
+        <legend>{% trans %}Forum Options{% endtrans %}</legend>
+        {{ form_theme.row(form.timezone) }}
+        {{ form_theme.row(form.newsletters, attrs={'inline': _("Yes, I want to subscribe forum newsletter.")}) }}
+      </fieldset>
+      <fieldset>
+        <legend>{% trans %}Watching Threads{% endtrans %}</legend>
+        {{ form_theme.row(form.subscribe_start) }}
+        {{ form_theme.row(form.subscribe_reply) }}
+      </fieldset>
+    </div>
+    <div class="form-actions">
+      <button name="save" type="submit" class="btn btn-primary">{% trans %}Change Options{% endtrans %}</button>
+      <a href="{{ url('usercp') }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+    </div>
+  </form>
+  
+</div>
 {% endblock %}
 {% endblock %}

+ 41 - 41
templates/cranefly/usercp/signature.html

@@ -1,42 +1,42 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "cranefly/editor.html" as editor with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Edit your Signature')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  <div class="form-header">
-    <h1>{% trans %}Edit your Signature{% endtrans %}</h1>
-  </div>
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  {% if user.signature_preparsed %}
-  <div class="form-preview signature-preview">
-    <div class="markdown">
-      {{ user.signature_preparsed|markdown_final|safe }}
-    </div>
-  </div>
-  {% endif %}
-
-  <form action="{{ url('usercp_signature') }}" method="post">
-    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-    {{ editor.editor(form.fields.signature, _('Save Signature'),
-      hide_links=(not acl.usercp.allow_signature_links()),
-      hide_images=(not acl.usercp.allow_signature_images()),
-      hide_hr=True) }}
-  </form>
-  
-</div>
-{% endblock %}
-
-{% block javascripts %}
-{{ super() }}
-  {{ editor.js() }}
+{% extends "cranefly/usercp/layout.html" %}
+{% import "cranefly/editor.html" as editor with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Edit your Signature')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  <div class="form-header">
+    <h1>{% trans %}Edit your Signature{% endtrans %}</h1>
+  </div>
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  {% if user.signature_preparsed %}
+  <div class="form-preview signature-preview">
+    <div class="markdown">
+      {{ user.signature_preparsed|markdown_final|safe }}
+    </div>
+  </div>
+  {% endif %}
+
+  <form action="{{ url('usercp_signature') }}" method="post">
+    <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+    {{ editor.editor(form.fields.signature, _('Save Signature'),
+      hide_links=(not acl.usercp.allow_signature_links()),
+      hide_images=(not acl.usercp.allow_signature_images()),
+      hide_hr=True) }}
+  </form>
+  
+</div>
+{% endblock %}
+
+{% block javascripts %}
+{{ super() }}
+  {{ editor.js() }}
 {% endblock %}
 {% endblock %}

+ 21 - 21
templates/cranefly/usercp/signature_banned.html

@@ -1,22 +1,22 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Edit your Signature')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  <div class="form-header">
-    <h1>{% trans %}Edit your Signature{% endtrans %}</h1>
-  </div>
-
-  {% if user.signature_ban_reason_user %}
-  <p class="lead">{% trans username=user.username %}{{ username }}, your ability to edit your signature has been removed for following reason:{% endtrans %}</p>
-  <div class="markdown">
-  {{ user.signature_ban_reason_user|markdown|safe }}
-  </div>
-  {% else %}
-  <p class="lead">{% trans username=user.username %}{{ username }}, your ability to edit your signature has been removed.{% endtrans %}</p>
-  {% endif %}
-</div>
+{% extends "cranefly/usercp/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Edit your Signature')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  <div class="form-header">
+    <h1>{% trans %}Edit your Signature{% endtrans %}</h1>
+  </div>
+
+  {% if user.signature_ban_reason_user %}
+  <p class="lead">{% trans username=user.username %}{{ username }}, your ability to edit your signature has been removed for following reason:{% endtrans %}</p>
+  <div class="markdown">
+  {{ user.signature_ban_reason_user|markdown|safe }}
+  </div>
+  {% else %}
+  <p class="lead">{% trans username=user.username %}{{ username }}, your ability to edit your signature has been removed.{% endtrans %}</p>
+  {% endif %}
+</div>
 {% endblock %}
 {% endblock %}

+ 45 - 45
templates/cranefly/usercp/username.html

@@ -1,46 +1,46 @@
-{% extends "cranefly/usercp/layout.html" %}
-{% import "forms.html" as form_theme with context %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Change your Username')) }}{% endblock %}
-
-{% block action %}
-<div class="form-container">
-
-  <div class="form-header">
-    <h1>{% trans %}Change your Username{% endtrans %}</h1>
-  </div>
-
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  <form action="{{ url('usercp_username') }}" method="post">
-    {{ form_theme.hidden_fields(form) }}
-    <div class="form-fields">
-      {% if changes_left == 0 %}
-      {{ form_theme.row(form.username, attrs={'disabled': 'disabled'}) }}
-      {% else %}
-      {{ form_theme.row(form.username) }}
-      {% endif %}
-    </div>
-    <div class="form-actions">
-      <button name="save" type="submit" class="btn btn-primary"{% if changes_left == 0 %} disabled="disabled"{% endif %}>{% trans %}Change Name{% endtrans %}</button>
-      <span class="form-actions-protip">{% if changes_left > 0 -%}
-      {% trans changes=changes_left -%}
-      You can change your username one more time.
-      {%- pluralize -%}
-      You can change your username {{ changes }} more times.
-      {%- endtrans %}
-      {%- elif acl.usercp.changes_expire() -%}
-      {% trans next_change=next_change|reldate|low %}You will be able to change your username on {{ next_change }}{% endtrans %}
-      {%- else -%}
-      {% trans %}You have exceeded the maximum number of name changes.{% endtrans %}
-      {%- endif %}
-      </span>
-    </div>
-  </form>
-</div>
+{% extends "cranefly/usercp/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Change your Username')) }}{% endblock %}
+
+{% block action %}
+<div class="form-container">
+
+  <div class="form-header">
+    <h1>{% trans %}Change your Username{% endtrans %}</h1>
+  </div>
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <form action="{{ url('usercp_username') }}" method="post">
+    {{ form_theme.hidden_fields(form) }}
+    <div class="form-fields">
+      {% if changes_left == 0 %}
+      {{ form_theme.row(form.username, attrs={'disabled': 'disabled'}) }}
+      {% else %}
+      {{ form_theme.row(form.username) }}
+      {% endif %}
+    </div>
+    <div class="form-actions">
+      <button name="save" type="submit" class="btn btn-primary"{% if changes_left == 0 %} disabled="disabled"{% endif %}>{% trans %}Change Name{% endtrans %}</button>
+      <span class="form-actions-protip">{% if changes_left > 0 -%}
+      {% trans changes=changes_left -%}
+      You can change your username one more time.
+      {%- pluralize -%}
+      You can change your username {{ changes }} more times.
+      {%- endtrans %}
+      {%- elif acl.usercp.changes_expire() -%}
+      {% trans next_change=next_change|reldate|low %}You will be able to change your username on {{ next_change }}{% endtrans %}
+      {%- else -%}
+      {% trans %}You have exceeded the maximum number of name changes.{% endtrans %}
+      {%- endif %}
+      </span>
+    </div>
+  </form>
+</div>
 {% endblock %}
 {% endblock %}

+ 208 - 208
templates/cranefly/watched.html

@@ -1,209 +1,209 @@
-{% extends "cranefly/layout.html" %}
-{% import "cranefly/macros.html" as macros with context %}
-
-{% block title %}{{ macros.page_title(title=_('Threads you are watching')) }}{% endblock %}
-
-{% block container %}
-<div class="page-header header-primary">
-  <div class="container">
-    {{ messages_list(messages) }}
-    <h1>{% trans %}Threads you are watching{% endtrans %}</h1>
-
-    <ul class="nav nav-tabs header-tabs">
-      <li class="{% if not new %}active{% endif %}"><a href="{{ url('watched_threads') }}">{% trans %}All Threads{% endtrans %}</a></li>
-      <li class="{% if new %}active{% endif %}"><a href="{{ url('watched_threads_new') }}">{% trans %}Unread Threads{% endtrans %}</a></li>
-    </ul>
-  </div>
-</div>
-
-<div class="container container-primary">
-  {% if message %}
-  <div class="messages-list">
-    {{ macros.draw_message(message) }}
-  </div>
-  {% endif %}
-
-  {% if threads %}
-  {{ pager() }}
-  <div class="forum-threads-list watched-threads">
-    <div class="header">
-      <div class="row-fluid">
-        <div class="span7">{% trans %}Thread{% endtrans %}</div>
-        <div class="span5 thread-activity">
-          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
-        </div>
-      </div>
-    </div>
-    {% for thread in threads %}
-    <div id="watch-{{ loop.index }}" class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
-      <div class="row-fluid">
-        <div class="span7">
-          {% if thread.is_read %}
-          <a href="{{ thread_url(thread, 'new') }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
-          {% else %}
-          <a href="{{ thread_url(thread, 'new') }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
-          {% endif %}
-
-          {{ macros.thread_flags(thread) }}
-
-          <a href="{{ thread_url(thread) }}" class="thread-name">{{ thread.name }}</a>
-
-          <div class="thread-details">
-            {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
-          </div>
-
-        </div>
-        <div class="span5 thread-activity">
-          {% if settings.avatars_on_threads_list %}
-          <div class="thread-last-avatar">
-            {% if thread.last_poster_id %}
-            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
-            {% else %}
-            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
-            {% endif %}
-          </div>
-          {% endif %}
-
-          <div class="thread-replies">
-            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
-            {{ replies(thread.replies) }}, <span{% if (thread.upvotes-thread.downvotes) > 0 %} class="text-success"{% elif (thread.upvotes-thread.downvotes) < 0 %} class="text-error"{% endif %}><strong>{% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}</strong>{% trans rating=(thread.upvotes-thread.downvotes)|abs|intcomma %}{{ rating }} thread rating{% endtrans %}</span>
-          </div>
-
-          <div class="thread-options">
-            <form action="{% if thread.send_email %}{{ thread_url(thread, 'unwatch_email') }}{% else %}{{ thread_url(thread, 'watch_email') }}{% endif %}" method="post">
-              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-              <input type="hidden" name="retreat" value="{{ request_path }}#watch-{{ loop.index }}">
-              <button type="submit" class="btn btn-{% if thread.send_email %}success{% else %}inverse{% endif %} tooltip-top" title="{% if thread.send_email %}{% trans %}Don't notify with e-mail{% endtrans %}{% else %}{% trans %}Notify with e-mail{% endtrans %}{% endif %}"><i class="icon-envelope"></i></button>
-            </form>
-
-            <form action="{{ thread_url(thread, 'unwatch') }}" method="post">
-              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-              <input type="hidden" name="retreat" value="{{ delete_retreat(loop) }}">
-              <button type="submit" class="btn btn-danger tooltip-top" title="{% trans %}Unwatch{% endtrans %}"><i class="icon-remove"></i></button>
-            </form>
-          </div>
-        </div>
-      </div>
-    </div>
-    {% endfor %}
-    {#<table class="table">
-      <thead>
-        <tr>
-          <th>{% trans %}Thread{% endtrans %}</th>
-          <th colspan="2" class="span5">{% trans %}Activity{% endtrans %}</th>
-        </tr>
-      </thead>
-      <tbody>
-        {% for thread in threads %}
-        <tr id="watch-{{ loop.index }}">
-          <td>
-            <a href="{{ thread_url(thread, 'new') }}" class="thread-icon{% if not thread.is_read %} thread-new{% endif %} tooltip-top" title="{% if not thread.is_read -%}
-            {% trans %}Click to see first unread post{% endtrans %}
-            {%- else -%}
-            {% trans %}Click to see last post{% endtrans %}
-            {%- endif %}"><i class="icon-comment"></i></a>
-            <a href="{{ thread_url(thread) }}" class="thread-name">{{ thread.name }}</a>
-            <span class="thread-details">
-              {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
-            </span>
-          </td>
-          <td>
-            <div class="thread-last-reply">
-              {{ replies(thread.replies) }} - {% trans user=thread_reply(thread), last=thread.last|reltimesince|low %}last by {{ user }} {{ last }}{% endtrans %}
-            </div>
-          </td>
-          <td class="watched-thread-flags">
-            <form action="{{ thread_url(thread, 'unwatch') }}" method="post">
-              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-              <input type="hidden" name="retreat" value="{{ delete_retreat(loop) }}">
-              <button type="submit" class="btn btn-danger tooltip-top" title="{% trans %}Unwatch{% endtrans %}"><i class="icon-remove"></i></button>
-            </form>
-
-            <form action="{% if thread.send_email %}{{ thread_url(thread, 'unwatch_email') }}{% else %}{{ thread_url(thread, 'watch_email') }}{% endif %}" method="post">
-              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-              <input type="hidden" name="retreat" value="{{ request_path }}#watch-{{ loop.index }}">
-              <button type="submit" class="btn btn-{% if thread.send_email %}success{% else %}inverse{% endif %} tooltip-top" title="{% if thread.send_email %}{% trans %}Don't notify with e-mail{% endtrans %}{% else %}{% trans %}Notify with e-mail{% endtrans %}{% endif %}"><i class="icon-envelope"></i></button>
-            </form>
-          </td>
-        </tr>
-        {% endfor %}
-      </tbody>
-    </table>#}
-  </div>
-  {{ pager() }}
-  {% else %}
-  <p class="lead">{% if new -%}
-    {% trans %}There are no unread threads that you are watching.{% endtrans %}
-    {%- else -%}
-    {% trans %}You are not watching any threads.{% endtrans %}
-    {%- endif %}</p>
-  {% endif %}
-</div>
-{% endblock %}
-
-
-{% macro thread_url(thread, route='') -%}
-{%- if route %}{% set route = '_'~route %}{% endif %}
-{% if thread.forum_id == private_threads.pk -%}
-{{ url('private_thread' ~ route, thread=thread.pk, slug=thread.slug) }}
-{%- elif thread.forum_id == reports.pk -%}
-{{ url('report' ~ route, thread=thread.pk, slug=thread.slug) }}
-{%- else -%}
-{{ url('thread' ~ route, thread=thread.pk, slug=thread.slug) }}
-{%- endif -%}
-{%- endmacro %}
-
-{% macro replies(thread_replies) -%}
-{% trans count=thread_replies, replies=('<strong>' ~ (thread_replies|intcomma) ~ '</strong>')|safe -%}
-{{ replies }} reply
-{%- pluralize -%}
-{{ replies }} replies
-{%- endtrans %}
-{%- endmacro %}
-
-{% macro thread_starter(thread) -%}
-{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro thread_forum(thread) -%}
-<a href="{{ thread.forum.url }}" class="forum-link">{{ thread.forum }}</a>
-{%- endmacro %}
-
-{% macro thread_reply(thread) -%}
-{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
-{%- endmacro %}
-
-{% macro pager() -%}
-{% if pagination['total'] > 1 %}
-<div class="pagination">
-  <ul>
-    <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {% if new %}
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('watched_threads_new') }}" class="tooltip-top" title="{% trans %}Latest Threads{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}Latest{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('watched_threads_new', page=pagination['prev']) }}{% else %}{{ url('watched_threads_new') }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('watched_threads_new', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-    {% else %}
-    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('watched_threads') }}" class="tooltip-top" title="{% trans %}Latest Threads{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}Latest{% endtrans %}</a></li>{% endif -%}
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('watched_threads', page=pagination['prev']) }}{% else %}{{ url('watched_threads') }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ url('watched_threads', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
-    {% endif %}
-  </ul>
-</div>
-{% endif %}
-{%- endmacro %}
-
-{% macro delete_retreat(loop) -%}
-{%- if pagination['page'] == 1 -%}
-  {{ request_path }}{% if not (loop.first and loop.last) %}#watch-{{ loop.index }}{% endif %}
-{%- else -%}
-  {%- if loop.first and loop.last -%}
-    {%- if new -%}
-      {%- if pagination['prev'] > 1 %}{{ url('watched_threads_new', page=pagination['prev']) }}{% else %}{{ url('watched_threads_new') }}{% endif %}
-    {%- else -%}
-      {%- if pagination['prev'] > 1 %}{{ url('watched_threads', page=pagination['prev']) }}{% else %}{{ url('watched_threads') }}{% endif %}
-    {%- endif -%}#watch-{{ settings.threads_per_page }}
-  {%- else -%}
-    {{ request_path }}#watch-{{ loop.index }}
-  {%- endif -%}
-{%- endif  -%}
+{% extends "cranefly/layout.html" %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Threads you are watching')) }}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% trans %}Threads you are watching{% endtrans %}</h1>
+
+    <ul class="nav nav-tabs header-tabs">
+      <li class="{% if not new %}active{% endif %}"><a href="{{ url('watched_threads') }}">{% trans %}All Threads{% endtrans %}</a></li>
+      <li class="{% if new %}active{% endif %}"><a href="{{ url('watched_threads_new') }}">{% trans %}Unread Threads{% endtrans %}</a></li>
+    </ul>
+  </div>
+</div>
+
+<div class="container container-primary">
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  {% if threads %}
+  {{ pager() }}
+  <div class="forum-threads-list watched-threads">
+    <div class="header">
+      <div class="row-fluid">
+        <div class="span7">{% trans %}Thread{% endtrans %}</div>
+        <div class="span5 thread-activity">
+          <div class="thread-replies">{% trans %}Activity{% endtrans %}</div>
+        </div>
+      </div>
+    </div>
+    {% for thread in threads %}
+    <div id="watch-{{ loop.index }}" class="thread-row{% if not thread.is_read %} thread-new{% endif %}{% if loop.last %} thread-last{% endif %}">
+      <div class="row-fluid">
+        <div class="span7">
+          {% if thread.is_read %}
+          <a href="{{ thread_url(thread, 'new') }}" class="thread-icon thread-icon-last tooltip-top" title="{% trans %}Click to see last post{% endtrans %}"><i class="icon-asterisk"></i></a>
+          {% else %}
+          <a href="{{ thread_url(thread, 'new') }}" class="thread-icon thread-icon-new tooltip-top" title="{% trans %}Click to see first unread post{% endtrans %}"><i class="icon-fire"></i></a>
+          {% endif %}
+
+          {{ macros.thread_flags(thread) }}
+
+          <a href="{{ thread_url(thread) }}" class="thread-name">{{ thread.name }}</a>
+
+          <div class="thread-details">
+            {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
+          </div>
+
+        </div>
+        <div class="span5 thread-activity">
+          {% if settings.avatars_on_threads_list %}
+          <div class="thread-last-avatar">
+            {% if thread.last_poster_id %}
+            <a href="{{ url('user', user=thread.last_poster.pk, username=thread.last_poster.username_slug) }}"><img src="{{ thread.last_poster.get_avatar(40) }}" alt=""></a>
+            {% else %}
+            <img src="{{ macros.avatar_guest(40) }}" alt="" class="user-avatar">
+            {% endif %}
+          </div>
+          {% endif %}
+
+          <div class="thread-replies">
+            <strong class="lead">{{ thread_reply(thread) }}, {{ thread.last|reldate|low }}</strong><br>
+            {{ replies(thread.replies) }}, <span{% if (thread.upvotes-thread.downvotes) > 0 %} class="text-success"{% elif (thread.upvotes-thread.downvotes) < 0 %} class="text-error"{% endif %}><strong>{% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}</strong>{% trans rating=(thread.upvotes-thread.downvotes)|abs|intcomma %}{{ rating }} thread rating{% endtrans %}</span>
+          </div>
+
+          <div class="thread-options">
+            <form action="{% if thread.send_email %}{{ thread_url(thread, 'unwatch_email') }}{% else %}{{ thread_url(thread, 'watch_email') }}{% endif %}" method="post">
+              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+              <input type="hidden" name="retreat" value="{{ request_path }}#watch-{{ loop.index }}">
+              <button type="submit" class="btn btn-{% if thread.send_email %}success{% else %}inverse{% endif %} tooltip-top" title="{% if thread.send_email %}{% trans %}Don't notify with e-mail{% endtrans %}{% else %}{% trans %}Notify with e-mail{% endtrans %}{% endif %}"><i class="icon-envelope"></i></button>
+            </form>
+
+            <form action="{{ thread_url(thread, 'unwatch') }}" method="post">
+              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+              <input type="hidden" name="retreat" value="{{ delete_retreat(loop) }}">
+              <button type="submit" class="btn btn-danger tooltip-top" title="{% trans %}Unwatch{% endtrans %}"><i class="icon-remove"></i></button>
+            </form>
+          </div>
+        </div>
+      </div>
+    </div>
+    {% endfor %}
+    {#<table class="table">
+      <thead>
+        <tr>
+          <th>{% trans %}Thread{% endtrans %}</th>
+          <th colspan="2" class="span5">{% trans %}Activity{% endtrans %}</th>
+        </tr>
+      </thead>
+      <tbody>
+        {% for thread in threads %}
+        <tr id="watch-{{ loop.index }}">
+          <td>
+            <a href="{{ thread_url(thread, 'new') }}" class="thread-icon{% if not thread.is_read %} thread-new{% endif %} tooltip-top" title="{% if not thread.is_read -%}
+            {% trans %}Click to see first unread post{% endtrans %}
+            {%- else -%}
+            {% trans %}Click to see last post{% endtrans %}
+            {%- endif %}"><i class="icon-comment"></i></a>
+            <a href="{{ thread_url(thread) }}" class="thread-name">{{ thread.name }}</a>
+            <span class="thread-details">
+              {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
+            </span>
+          </td>
+          <td>
+            <div class="thread-last-reply">
+              {{ replies(thread.replies) }} - {% trans user=thread_reply(thread), last=thread.last|reltimesince|low %}last by {{ user }} {{ last }}{% endtrans %}
+            </div>
+          </td>
+          <td class="watched-thread-flags">
+            <form action="{{ thread_url(thread, 'unwatch') }}" method="post">
+              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+              <input type="hidden" name="retreat" value="{{ delete_retreat(loop) }}">
+              <button type="submit" class="btn btn-danger tooltip-top" title="{% trans %}Unwatch{% endtrans %}"><i class="icon-remove"></i></button>
+            </form>
+
+            <form action="{% if thread.send_email %}{{ thread_url(thread, 'unwatch_email') }}{% else %}{{ thread_url(thread, 'watch_email') }}{% endif %}" method="post">
+              <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+              <input type="hidden" name="retreat" value="{{ request_path }}#watch-{{ loop.index }}">
+              <button type="submit" class="btn btn-{% if thread.send_email %}success{% else %}inverse{% endif %} tooltip-top" title="{% if thread.send_email %}{% trans %}Don't notify with e-mail{% endtrans %}{% else %}{% trans %}Notify with e-mail{% endtrans %}{% endif %}"><i class="icon-envelope"></i></button>
+            </form>
+          </td>
+        </tr>
+        {% endfor %}
+      </tbody>
+    </table>#}
+  </div>
+  {{ pager() }}
+  {% else %}
+  <p class="lead">{% if new -%}
+    {% trans %}There are no unread threads that you are watching.{% endtrans %}
+    {%- else -%}
+    {% trans %}You are not watching any threads.{% endtrans %}
+    {%- endif %}</p>
+  {% endif %}
+</div>
+{% endblock %}
+
+
+{% macro thread_url(thread, route='') -%}
+{%- if route %}{% set route = '_'~route %}{% endif %}
+{% if thread.forum_id == private_threads.pk -%}
+{{ url('private_thread' ~ route, thread=thread.pk, slug=thread.slug) }}
+{%- elif thread.forum_id == reports.pk -%}
+{{ url('report' ~ route, thread=thread.pk, slug=thread.slug) }}
+{%- else -%}
+{{ url('thread' ~ route, thread=thread.pk, slug=thread.slug) }}
+{%- endif -%}
+{%- endmacro %}
+
+{% macro replies(thread_replies) -%}
+{% trans count=thread_replies, replies=('<strong>' ~ (thread_replies|intcomma) ~ '</strong>')|safe -%}
+{{ replies }} reply
+{%- pluralize -%}
+{{ replies }} replies
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro thread_starter(thread) -%}
+{% if thread.start_poster_id %}<a href="{{ url('user', user=thread.start_poster_id, username=thread.start_poster_slug) }}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro thread_forum(thread) -%}
+<a href="{{ thread.forum.url }}" class="forum-link">{{ thread.forum }}</a>
+{%- endmacro %}
+
+{% macro thread_reply(thread) -%}
+{% if thread.last_poster_id %}<a href="{{ url('user', user=thread.last_poster_id, username=thread.last_poster_slug) }}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}
+{%- endmacro %}
+
+{% macro pager() -%}
+{% if pagination['total'] > 1 %}
+<div class="pagination">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {% if new %}
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('watched_threads_new') }}" class="tooltip-top" title="{% trans %}Latest Threads{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}Latest{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('watched_threads_new', page=pagination['prev']) }}{% else %}{{ url('watched_threads_new') }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('watched_threads_new', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {% else %}
+    {%- if pagination['prev'] > 1 %}<li><a href="{{ url('watched_threads') }}" class="tooltip-top" title="{% trans %}Latest Threads{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}Latest{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ url('watched_threads', page=pagination['prev']) }}{% else %}{{ url('watched_threads') }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{{ url('watched_threads', page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {% endif %}
+  </ul>
+</div>
+{% endif %}
+{%- endmacro %}
+
+{% macro delete_retreat(loop) -%}
+{%- if pagination['page'] == 1 -%}
+  {{ request_path }}{% if not (loop.first and loop.last) %}#watch-{{ loop.index }}{% endif %}
+{%- else -%}
+  {%- if loop.first and loop.last -%}
+    {%- if new -%}
+      {%- if pagination['prev'] > 1 %}{{ url('watched_threads_new', page=pagination['prev']) }}{% else %}{{ url('watched_threads_new') }}{% endif %}
+    {%- else -%}
+      {%- if pagination['prev'] > 1 %}{{ url('watched_threads', page=pagination['prev']) }}{% else %}{{ url('watched_threads') }}{% endif %}
+    {%- endif -%}#watch-{{ settings.threads_per_page }}
+  {%- else -%}
+    {{ request_path }}#watch-{{ loop.index }}
+  {%- endif -%}
+{%- endif  -%}
 {%- endmacro %}
 {%- endmacro %}

+ 21 - 21
templates/debug_toolbar/panels/acl.html

@@ -1,21 +1,21 @@
-{% load i18n %}
-
-{% for provider in acl %}
-<h4>{{ provider }}</h4>
-<table>
-    <thead>
-        <tr>
-            <th style="width: 180px;">{% trans 'Permission' %}</th>
-            <th>{% trans 'Value' %}</th>
-        </tr>
-    </thead>
-    <tbody>
-        {% for perm, value in provider.acl.items %}
-        <tr>
-            <td>{{ perm }}</td>
-            <td>{{ value|default:"None" }}</td>
-        </tr>
-        {% endfor %}
-    </tbody>
-</table>
-{% endfor %}
+{% load i18n %}
+
+{% for provider in acl %}
+<h4>{{ provider }}</h4>
+<table>
+    <thead>
+        <tr>
+            <th style="width: 180px;">{% trans 'Permission' %}</th>
+            <th>{% trans 'Value' %}</th>
+        </tr>
+    </thead>
+    <tbody>
+        {% for perm, value in provider.acl.items %}
+        <tr>
+            <td>{{ perm }}</td>
+            <td>{{ value|default:"None" }}</td>
+        </tr>
+        {% endfor %}
+    </tbody>
+</table>
+{% endfor %}

+ 137 - 137
templates/forms.html

@@ -1,138 +1,138 @@
-{# Forms macros for rendering forms and fields and stuff in templates. #}
-
-{% macro hidden_fields(form) -%}
-  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-  {% for field in form.hidden_fields() %}
-  <input type="hidden" name="{{ field.html_name }}" value="{{ field.value() }}">
-  {% endfor %}
-{%- endmacro %}
-
-{% macro row(_field, label=None, help_text=None, width=9, attrs=None) -%}
-  <div id="{{ _field.html_name }}-control-group" class="control-group{% if _field.errors %} error{% endif %}">
-    <label class="control-label" for="id_{{ _field.html_name }}">{% if label %}{{ label }}{% elif _field.label %}{{ _field.label }}{% else %}{{ _field.html_name }}{% endif %}</label>
-    <div class="controls">
-      {% if attrs == None %}{% set attrs = {} %}{% endif %}
-      {% if _field.field.widget.__class__.__name__ == 'ForumTOS' %}{% do attrs.update({'inline': make_tos_label()}) %}{% endif %}
-      {% if _field.field.widget.__class__.__name__ in ('CheckboxInput', 'ForumTOS') %}
-        <label class="checkbox">
-          {{ field(_field, width=width, attrs=attrs)|trim }}
-          {% if 'inline' in attrs %}
-          {{ attrs.inline }}
-          {% else %}
-          {% if help_text %}
-          {{ help_text }}
-          {% elif _field.help_text %}
-          {{ _field.help_text }}
-          {% endif %}
-          {% endif %}
-        </label>
-      {% else %}
-        {{ field(_field, width=width, attrs=attrs)|trim }}
-      {% endif %}
-      {% for error in _field.errors %}
-      <p class="help-block" style="font-weight: bold;">{{ error }}</p>
-      {% endfor %}
-      {% if 'inline' in attrs or _field.field.widget.__class__.__name__ not in ('CheckboxInput', 'ForumTOS') %}
-      {% if help_text %}
-      <p class="help-block">{{ help_text }}</p>
-      {% elif _field.help_text %}
-      <p class="help-block">{{ _field.help_text }}</p>
-      {% endif %}
-      {% endif %}
-    </div>
-  </div>
-{%- endmacro %}
-
-{% macro repeat(form, fields, label=None, help_text=None, attrs=None) -%}
-  <div id="{{ fields[0].html_name }}-control-group" class="control-group{% if fields[0].errors or fields[1].errors or (fields[0].name ~ '_' ~ fields[1].name) in form.errors %} error{% endif %}">
-    <label class="control-label" for="id_{{ fields[0].html_name }}">{% if label %}{{ label }}{% elif fields[0].label %}{{ fields[0].label }}{% else %}{{ fields[0].html_name }}{% endif %}</label>
-    <div class="controls controls-nested">
-      {% if attrs == None %}{% set attrs = ({}, {}) %}{% endif %}
-      <div class="row">
-        <div{% if 'class' in attrs[0] %} class="{{ attrs[0].class }}"{% endif %}>{{ field(fields[0], attrs=attrs[0]) }}</div>
-        <div{% if 'class' in attrs[1] %} class="{{ attrs[1].class }}"{% endif %}>{{ field(fields[1], attrs=attrs[1]) }}</div>
-      </div>
-      {% for error in form.errors[(fields[0].name ~ '_' ~ fields[1].name)] %}
-      <p class="help-block" style="font-weight: bold;">{{ error }}</p>
-      {% endfor %}
-      {% for error in fields[0].errors %}
-      <p class="help-block" style="font-weight: bold;">{{ error }}</p>
-      {% endfor %}
-      {% for error in fields[1].errors %}
-      {% if not error in fields[0].errors %}
-      <p class="help-block" style="font-weight: bold;">{{ error }}</p>
-      {% endif %}
-      {% endfor %}
-      {% if help_text %}
-      <p class="help-block">{{ help_text }}</p>
-      {% elif fields[0].help_text %}
-      <p class="help-block">{{ fields[0].help_text }}</p>
-      {% endif %}
-    </div>
-  </div>
-{%- endmacro %}
-
-{% macro make_tos_label() -%}
-{% trans forum_tos=make_tos_link()|safe %}I have read and accept this forums {{forum_tos}}.{% endtrans %}
-{%- endmacro %}
-
-{% macro make_tos_link() -%}
-<a href="{% if settings.tos_url %}{{ settings.tos_url }}{% else %}{{ url('tos') }}{% endif %}">{% if settings.tos_title %}{{ settings.tos_title }}{% else %}{% trans %}Terms of Service{% endtrans %}{% endif %}</a>
-{%- endmacro %}
-
-{% macro captcha(form, width=9) -%}
-{% if 'recaptcha' in form.fields %}
-{{ row(form.recaptcha) }}
-{% endif %}
-{% if 'captcha_qa' in form.fields %}
-{{ row(form.captcha_qa, width=width) }}
-{% endif %}
-{%- endmacro %}
-
-{% macro field(_field, attrs=None, width=9) -%}
-{% set widget = _field.field.widget.__class__.__name__ %}
-{% set context = _field.field.widget.get_context(_field.html_name, _field.value(), attrs=attrs) %}
-{% if not 'class' in context['attrs'] and widget not in ('CheckboxInput', 'ForumTOS') %}
-{% do context['attrs'].update({'class': ('span' ~ width)}) %}
-{% endif %}
-{% if 'inline' in context.attrs %}{% do context.attrs.pop('inline') %}{% endif %}
-{% if widget == 'Textarea' %}
-{{ _textarea(_field, context) }}
-{% elif widget == 'Select' %}
-{{ _select(_field, context) }}
-{% elif widget == 'ReCaptchaWidget' %}
-{{ _field.field.widget.render()|safe }}
-{% else %}
-{{ _input(_field, context) }}
-{% endif %}
-{%- endmacro %}
-
-{% macro attributes(attrs) -%}
-{% for name, value in attrs.items() %} {{ name }}{% if value != True %}="{{ value }}"{% endif %}{% endfor %}
-{%- endmacro %}
-
-{% macro _input(_field, context) -%}
-<input type="{{ context.type }}" id="id_{{ context.name }}" name="{{ context.name }}" {{ attributes(context.attrs)|trim }}{% if context.type != 'password' and 'value' in context and context.value|length > 0 %} value="{{ context.value }}"{% endif %}>
-{%- endmacro %}
-
-{% macro _textarea(_field, context) -%}
-<textarea id="id_{{ context.name }}" name="{{ context.name }}" {{ attributes(context.attrs)|trim }}>{% if 'value' in context and context.value|length > 0 %}{{ context.value }}{% endif %}</textarea>
-{%- endmacro %}
-
-{% macro _select(_field, context) -%}
-<select id="id_{{ context.name }}" name="{{ context.name }}" {{ attributes(context.attrs)|trim }}>
-  {% if context['optgroups']|length > 1 %}
-  {% for optgroup in context['optgroups'] %}
-  <optgroup label="{{ optgroup[0] }}">
-    {% for option in optgroup[1] %}
-    <option value="{{ option[0] }}"{% if 'value' in context and option[0] in context.value %} selected="selected"{% endif %}>{{ option[1] }}</option>
-    {% endfor %}
-  </optgroup>
-  {% endfor %}
-  {% else %}
-  {% for option in context['optgroups'][0][1] %}
-  <option value="{{ option[0] }}"{% if 'value' in context and option[0] in context.value %} selected="selected"{% endif %}>{{ option[1] }}</option>
-  {% endfor %}
-  {% endif %}
-</select>
+{# Forms macros for rendering forms and fields and stuff in templates. #}
+
+{% macro hidden_fields(form) -%}
+  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+  {% for field in form.hidden_fields() %}
+  <input type="hidden" name="{{ field.html_name }}" value="{{ field.value() }}">
+  {% endfor %}
+{%- endmacro %}
+
+{% macro row(_field, label=None, help_text=None, width=9, attrs=None) -%}
+  <div id="{{ _field.html_name }}-control-group" class="control-group{% if _field.errors %} error{% endif %}">
+    <label class="control-label" for="id_{{ _field.html_name }}">{% if label %}{{ label }}{% elif _field.label %}{{ _field.label }}{% else %}{{ _field.html_name }}{% endif %}</label>
+    <div class="controls">
+      {% if attrs == None %}{% set attrs = {} %}{% endif %}
+      {% if _field.field.widget.__class__.__name__ == 'ForumTOS' %}{% do attrs.update({'inline': make_tos_label()}) %}{% endif %}
+      {% if _field.field.widget.__class__.__name__ in ('CheckboxInput', 'ForumTOS') %}
+        <label class="checkbox">
+          {{ field(_field, width=width, attrs=attrs)|trim }}
+          {% if 'inline' in attrs %}
+          {{ attrs.inline }}
+          {% else %}
+          {% if help_text %}
+          {{ help_text }}
+          {% elif _field.help_text %}
+          {{ _field.help_text }}
+          {% endif %}
+          {% endif %}
+        </label>
+      {% else %}
+        {{ field(_field, width=width, attrs=attrs)|trim }}
+      {% endif %}
+      {% for error in _field.errors %}
+      <p class="help-block" style="font-weight: bold;">{{ error }}</p>
+      {% endfor %}
+      {% if 'inline' in attrs or _field.field.widget.__class__.__name__ not in ('CheckboxInput', 'ForumTOS') %}
+      {% if help_text %}
+      <p class="help-block">{{ help_text }}</p>
+      {% elif _field.help_text %}
+      <p class="help-block">{{ _field.help_text }}</p>
+      {% endif %}
+      {% endif %}
+    </div>
+  </div>
+{%- endmacro %}
+
+{% macro repeat(form, fields, label=None, help_text=None, attrs=None) -%}
+  <div id="{{ fields[0].html_name }}-control-group" class="control-group{% if fields[0].errors or fields[1].errors or (fields[0].name ~ '_' ~ fields[1].name) in form.errors %} error{% endif %}">
+    <label class="control-label" for="id_{{ fields[0].html_name }}">{% if label %}{{ label }}{% elif fields[0].label %}{{ fields[0].label }}{% else %}{{ fields[0].html_name }}{% endif %}</label>
+    <div class="controls controls-nested">
+      {% if attrs == None %}{% set attrs = ({}, {}) %}{% endif %}
+      <div class="row">
+        <div{% if 'class' in attrs[0] %} class="{{ attrs[0].class }}"{% endif %}>{{ field(fields[0], attrs=attrs[0]) }}</div>
+        <div{% if 'class' in attrs[1] %} class="{{ attrs[1].class }}"{% endif %}>{{ field(fields[1], attrs=attrs[1]) }}</div>
+      </div>
+      {% for error in form.errors[(fields[0].name ~ '_' ~ fields[1].name)] %}
+      <p class="help-block" style="font-weight: bold;">{{ error }}</p>
+      {% endfor %}
+      {% for error in fields[0].errors %}
+      <p class="help-block" style="font-weight: bold;">{{ error }}</p>
+      {% endfor %}
+      {% for error in fields[1].errors %}
+      {% if not error in fields[0].errors %}
+      <p class="help-block" style="font-weight: bold;">{{ error }}</p>
+      {% endif %}
+      {% endfor %}
+      {% if help_text %}
+      <p class="help-block">{{ help_text }}</p>
+      {% elif fields[0].help_text %}
+      <p class="help-block">{{ fields[0].help_text }}</p>
+      {% endif %}
+    </div>
+  </div>
+{%- endmacro %}
+
+{% macro make_tos_label() -%}
+{% trans forum_tos=make_tos_link()|safe %}I have read and accept this forums {{forum_tos}}.{% endtrans %}
+{%- endmacro %}
+
+{% macro make_tos_link() -%}
+<a href="{% if settings.tos_url %}{{ settings.tos_url }}{% else %}{{ url('tos') }}{% endif %}">{% if settings.tos_title %}{{ settings.tos_title }}{% else %}{% trans %}Terms of Service{% endtrans %}{% endif %}</a>
+{%- endmacro %}
+
+{% macro captcha(form, width=9) -%}
+{% if 'recaptcha' in form.fields %}
+{{ row(form.recaptcha) }}
+{% endif %}
+{% if 'captcha_qa' in form.fields %}
+{{ row(form.captcha_qa, width=width) }}
+{% endif %}
+{%- endmacro %}
+
+{% macro field(_field, attrs=None, width=9) -%}
+{% set widget = _field.field.widget.__class__.__name__ %}
+{% set context = _field.field.widget.get_context(_field.html_name, _field.value(), attrs=attrs) %}
+{% if not 'class' in context['attrs'] and widget not in ('CheckboxInput', 'ForumTOS') %}
+{% do context['attrs'].update({'class': ('span' ~ width)}) %}
+{% endif %}
+{% if 'inline' in context.attrs %}{% do context.attrs.pop('inline') %}{% endif %}
+{% if widget == 'Textarea' %}
+{{ _textarea(_field, context) }}
+{% elif widget == 'Select' %}
+{{ _select(_field, context) }}
+{% elif widget == 'ReCaptchaWidget' %}
+{{ _field.field.widget.render()|safe }}
+{% else %}
+{{ _input(_field, context) }}
+{% endif %}
+{%- endmacro %}
+
+{% macro attributes(attrs) -%}
+{% for name, value in attrs.items() %} {{ name }}{% if value != True %}="{{ value }}"{% endif %}{% endfor %}
+{%- endmacro %}
+
+{% macro _input(_field, context) -%}
+<input type="{{ context.type }}" id="id_{{ context.name }}" name="{{ context.name }}" {{ attributes(context.attrs)|trim }}{% if context.type != 'password' and 'value' in context and context.value|length > 0 %} value="{{ context.value }}"{% endif %}>
+{%- endmacro %}
+
+{% macro _textarea(_field, context) -%}
+<textarea id="id_{{ context.name }}" name="{{ context.name }}" {{ attributes(context.attrs)|trim }}>{% if 'value' in context and context.value|length > 0 %}{{ context.value }}{% endif %}</textarea>
+{%- endmacro %}
+
+{% macro _select(_field, context) -%}
+<select id="id_{{ context.name }}" name="{{ context.name }}" {{ attributes(context.attrs)|trim }}>
+  {% if context['optgroups']|length > 1 %}
+  {% for optgroup in context['optgroups'] %}
+  <optgroup label="{{ optgroup[0] }}">
+    {% for option in optgroup[1] %}
+    <option value="{{ option[0] }}"{% if 'value' in context and option[0] in context.value %} selected="selected"{% endif %}>{{ option[1] }}</option>
+    {% endfor %}
+  </optgroup>
+  {% endfor %}
+  {% else %}
+  {% for option in context['optgroups'][0][1] %}
+  <option value="{{ option[0] }}"{% if 'value' in context and option[0] in context.value %} selected="selected"{% endif %}>{{ option[1] }}</option>
+  {% endfor %}
+  {% endif %}
+</select>
 {%- endmacro %}
 {%- endmacro %}

+ 1 - 1
templates/search/indexes/misago/post_text.txt

@@ -1,2 +1,2 @@
-{{ object.thread.name }}
+{{ object.thread.name }}
 {{ object.post_clean }}
 {{ object.post_clean }}