Skip to content
Browse files

first commit

  • Loading branch information...
0 parents commit ba5e9d3a8680ae51bb046189da6461486d54a17f @justquick committed Jan 29, 2010
Showing with 23,444 additions and 0 deletions.
  1. +58 −0 README.txt
  2. +1 −0 __init__.py
  3. BIN __init__.pyc
  4. 0 apps/api/__init__.py
  5. BIN apps/api/__init__.pyc
  6. +31 −0 apps/api/handlers.py
  7. BIN apps/api/handlers.pyc
  8. +9 −0 apps/api/urls.py
  9. BIN apps/api/urls.pyc
  10. +3 −0 apps/django_ext/__init__.py
  11. BIN apps/django_ext/__init__.pyc
  12. 0 apps/django_ext/admin.py
  13. BIN apps/django_ext/admin.pyc
  14. +25 −0 apps/django_ext/cache.py
  15. +133 −0 apps/django_ext/fields.py
  16. +1 −0 apps/django_ext/management/__init__.py
  17. BIN apps/django_ext/management/__init__.pyc
  18. 0 apps/django_ext/management/commands/__init__.py
  19. +20 −0 apps/django_ext/management/commands/delete_contenttypes.py
  20. +17 −0 apps/django_ext/management/commands/flush_all_memcached.py
  21. +87 −0 apps/django_ext/managers.py
  22. +48 −0 apps/django_ext/middleware.py
  23. +1 −0 apps/django_ext/models.py
  24. BIN apps/django_ext/models.pyc
  25. +1 −0 apps/django_ext/templatetags/__init__.py
  26. BIN apps/django_ext/templatetags/__init__.pyc
  27. +98 −0 apps/django_ext/templatetags/fb.py
  28. BIN apps/django_ext/templatetags/fb.pyc
  29. +104 −0 apps/django_ext/templatetags/listutil.py
  30. BIN apps/django_ext/templatetags/listutil.pyc
  31. +333 −0 apps/django_ext/templatetags/smart_if.py
  32. BIN apps/django_ext/templatetags/smart_if.pyc
  33. +14 −0 apps/django_ext/views.py
  34. BIN apps/django_ext/views.pyc
  35. +28 −0 bin/ext-status.sh
  36. +32 −0 bin/install.sh
  37. +28 −0 bin/pull-ext.sh
  38. +28 −0 bin/push-ext.sh
  39. +8 −0 bin/upgrade.sh
  40. +18 −0 conf/EffervescentCollective.wsgi
  41. +35 −0 conf/apache2-EffervescentCollective
  42. +23 −0 conf/nginx-EffervescentCollective
  43. +13 −0 conf/proxy.conf
  44. BIN dev.db
  45. +18 −0 local_settings.py
  46. BIN local_settings.pyc
  47. +18 −0 manage.py
  48. BIN media/.DS_Store
  49. BIN media/Nautica05b/Thumbs.db
  50. +263 −0 media/Nautica05b/contact.html
  51. +203 −0 media/Nautica05b/css/html.css
  52. +551 −0 media/Nautica05b/css/layout.css
  53. BIN media/Nautica05b/images/Thumbs.db
  54. BIN media/Nautica05b/images/bg/Thumbs.db
  55. BIN media/Nautica05b/images/bg/blank.gif
  56. BIN media/Nautica05b/images/bg/header.gif
  57. BIN media/Nautica05b/images/bg/header_image.jpg
  58. BIN media/Nautica05b/images/bg/header_image2.jpg
  59. BIN media/Nautica05b/images/bg/light_body.gif
  60. BIN media/Nautica05b/images/bg/menu.gif
  61. BIN media/Nautica05b/images/bg/submenu1.gif
  62. BIN media/Nautica05b/images/bg/submenu2.gif
  63. BIN media/Nautica05b/images/firefox.jpg
  64. BIN media/Nautica05b/images/icon_samples.gif
  65. BIN media/Nautica05b/images/logo.gif
  66. BIN media/Nautica05b/images/logo.jpg
  67. BIN media/Nautica05b/images/thumbs/01.jpg
  68. BIN media/Nautica05b/images/thumbs/Thumbs.db
  69. +322 −0 media/Nautica05b/index.html
  70. +245 −0 media/Nautica05b/onecol.html
  71. +10 −0 media/Nautica05b/readme.txt
  72. +278 −0 media/Nautica05b/twocol_a.html
  73. +304 −0 media/Nautica05b/twocol_b.html
  74. +746 −0 media/admin/css/base.css
  75. +255 −0 media/admin/css/changelists.css
  76. +24 −0 media/admin/css/dashboard.css
  77. +327 −0 media/admin/css/forms.css
  78. +51 −0 media/admin/css/ie.css
  79. +54 −0 media/admin/css/login.css
  80. +195 −0 media/admin/css/rtl.css
  81. +506 −0 media/admin/css/widgets.css
  82. BIN media/admin/img/admin/arrow-down.gif
  83. BIN media/admin/img/admin/arrow-up.gif
  84. BIN media/admin/img/admin/changelist-bg.gif
  85. BIN media/admin/img/admin/changelist-bg_rtl.gif
  86. BIN media/admin/img/admin/chooser-bg.gif
  87. BIN media/admin/img/admin/chooser_stacked-bg.gif
  88. BIN media/admin/img/admin/default-bg-reverse.gif
  89. BIN media/admin/img/admin/default-bg.gif
  90. BIN media/admin/img/admin/deleted-overlay.gif
  91. BIN media/admin/img/admin/icon-no.gif
  92. BIN media/admin/img/admin/icon-unknown.gif
  93. BIN media/admin/img/admin/icon-yes.gif
  94. BIN media/admin/img/admin/icon_addlink.gif
  95. BIN media/admin/img/admin/icon_alert.gif
  96. BIN media/admin/img/admin/icon_calendar.gif
  97. BIN media/admin/img/admin/icon_changelink.gif
  98. BIN media/admin/img/admin/icon_clock.gif
  99. BIN media/admin/img/admin/icon_deletelink.gif
  100. BIN media/admin/img/admin/icon_error.gif
  101. BIN media/admin/img/admin/icon_searchbox.png
  102. BIN media/admin/img/admin/icon_success.gif
  103. BIN media/admin/img/admin/inline-delete-8bit.png
  104. BIN media/admin/img/admin/inline-delete.png
  105. BIN media/admin/img/admin/inline-restore-8bit.png
  106. BIN media/admin/img/admin/inline-restore.png
  107. BIN media/admin/img/admin/inline-splitter-bg.gif
  108. BIN media/admin/img/admin/nav-bg-grabber.gif
  109. BIN media/admin/img/admin/nav-bg-reverse.gif
  110. BIN media/admin/img/admin/nav-bg.gif
  111. BIN media/admin/img/admin/selector-add.gif
  112. BIN media/admin/img/admin/selector-addall.gif
  113. BIN media/admin/img/admin/selector-remove.gif
  114. BIN media/admin/img/admin/selector-removeall.gif
  115. BIN media/admin/img/admin/selector-search.gif
  116. BIN media/admin/img/admin/selector_stacked-add.gif
  117. BIN media/admin/img/admin/selector_stacked-remove.gif
  118. BIN media/admin/img/admin/tool-left.gif
  119. BIN media/admin/img/admin/tool-left_over.gif
  120. BIN media/admin/img/admin/tool-right.gif
  121. BIN media/admin/img/admin/tool-right_over.gif
  122. BIN media/admin/img/admin/tooltag-add.gif
  123. BIN media/admin/img/admin/tooltag-add_over.gif
  124. BIN media/admin/img/admin/tooltag-arrowright.gif
  125. BIN media/admin/img/admin/tooltag-arrowright_over.gif
  126. BIN media/admin/img/gis/move_vertex_off.png
  127. BIN media/admin/img/gis/move_vertex_on.png
  128. +111 −0 media/admin/js/SelectBox.js
  129. +113 −0 media/admin/js/SelectFilter2.js
  130. +19 −0 media/admin/js/actions.js
  131. +85 −0 media/admin/js/admin/CollapsedFieldsets.js
  132. +255 −0 media/admin/js/admin/DateTimeShortcuts.js
  133. +92 −0 media/admin/js/admin/RelatedObjectLookups.js
  134. +137 −0 media/admin/js/admin/ordering.js
  135. +143 −0 media/admin/js/calendar.js
  136. +176 −0 media/admin/js/core.js
  137. +233 −0 media/admin/js/dateparse.js
  138. +167 −0 media/admin/js/getElementsBySelector.js
  139. +94 −0 media/admin/js/timeparse.js
  140. +140 −0 media/admin/js/urlify.js
  141. +203 −0 media/css/html.css
  142. +551 −0 media/css/layout.css
  143. +16 −0 media/css/screen.css
  144. BIN media/images/Thumbs.db
  145. BIN media/images/bg/.DS_Store
  146. BIN media/images/bg/blank.gif
  147. BIN media/images/bg/header.gif
  148. BIN media/images/bg/header_image.jpg
  149. BIN media/images/bg/header_image2.jpg
  150. BIN media/images/bg/light_body.gif
  151. BIN media/images/bg/menu.gif
  152. BIN media/images/bg/submenu1.gif
  153. BIN media/images/bg/submenu2.gif
  154. BIN media/images/dsc_0041.jpg
  155. BIN media/images/firefox.jpg
  156. BIN media/images/icon_samples.gif
  157. BIN media/images/logo.gif
  158. BIN media/images/logo.jpg
  159. +1 −0 media/images/temporary.txt
  160. +1 −0 media/js/global.js
  161. +4,376 −0 media/js/jquery-1.3.2.js
  162. +141 −0 media/js/jquery.tag.editor.js
  163. BIN media/js/tiny_mce/.DS_Store
  164. +154 −0 media/js/tiny_mce/langs/en.js
  165. +504 −0 media/js/tiny_mce/license.txt
  166. +5 −0 media/js/tiny_mce/plugins/advhr/css/advhr.css
  167. +1 −0 media/js/tiny_mce/plugins/advhr/editor_plugin.js
  168. +54 −0 media/js/tiny_mce/plugins/advhr/editor_plugin_src.js
  169. +43 −0 media/js/tiny_mce/plugins/advhr/js/rule.js
  170. +5 −0 media/js/tiny_mce/plugins/advhr/langs/en_dlg.js
  171. +62 −0 media/js/tiny_mce/plugins/advhr/rule.htm
  172. +13 −0 media/js/tiny_mce/plugins/advimage/css/advimage.css
  173. +1 −0 media/js/tiny_mce/plugins/advimage/editor_plugin.js
  174. +47 −0 media/js/tiny_mce/plugins/advimage/editor_plugin_src.js
  175. +237 −0 media/js/tiny_mce/plugins/advimage/image.htm
  176. BIN media/js/tiny_mce/plugins/advimage/img/sample.gif
  177. +443 −0 media/js/tiny_mce/plugins/advimage/js/image.js
  178. +43 −0 media/js/tiny_mce/plugins/advimage/langs/en_dlg.js
  179. +8 −0 media/js/tiny_mce/plugins/advlink/css/advlink.css
  180. +1 −0 media/js/tiny_mce/plugins/advlink/editor_plugin.js
  181. +58 −0 media/js/tiny_mce/plugins/advlink/editor_plugin_src.js
  182. +528 −0 media/js/tiny_mce/plugins/advlink/js/advlink.js
  183. +52 −0 media/js/tiny_mce/plugins/advlink/langs/en_dlg.js
  184. +338 −0 media/js/tiny_mce/plugins/advlink/link.htm
  185. +1 −0 media/js/tiny_mce/plugins/autoresize/editor_plugin.js
  186. +114 −0 media/js/tiny_mce/plugins/autoresize/editor_plugin_src.js
  187. +1 −0 media/js/tiny_mce/plugins/autosave/editor_plugin.js
  188. +51 −0 media/js/tiny_mce/plugins/autosave/editor_plugin_src.js
  189. +1 −0 media/js/tiny_mce/plugins/bbcode/editor_plugin.js
  190. +117 −0 media/js/tiny_mce/plugins/bbcode/editor_plugin_src.js
  191. +1 −0 media/js/tiny_mce/plugins/contextmenu/editor_plugin.js
  192. +95 −0 media/js/tiny_mce/plugins/contextmenu/editor_plugin_src.js
  193. +1 −0 media/js/tiny_mce/plugins/directionality/editor_plugin.js
  194. +79 −0 media/js/tiny_mce/plugins/directionality/editor_plugin_src.js
  195. +1 −0 media/js/tiny_mce/plugins/emotions/editor_plugin.js
  196. +40 −0 media/js/tiny_mce/plugins/emotions/editor_plugin_src.js
  197. +40 −0 media/js/tiny_mce/plugins/emotions/emotions.htm
  198. BIN media/js/tiny_mce/plugins/emotions/img/smiley-cool.gif
  199. BIN media/js/tiny_mce/plugins/emotions/img/smiley-cry.gif
  200. BIN media/js/tiny_mce/plugins/emotions/img/smiley-embarassed.gif
  201. BIN media/js/tiny_mce/plugins/emotions/img/smiley-foot-in-mouth.gif
  202. BIN media/js/tiny_mce/plugins/emotions/img/smiley-frown.gif
  203. BIN media/js/tiny_mce/plugins/emotions/img/smiley-innocent.gif
  204. BIN media/js/tiny_mce/plugins/emotions/img/smiley-kiss.gif
  205. BIN media/js/tiny_mce/plugins/emotions/img/smiley-laughing.gif
  206. BIN media/js/tiny_mce/plugins/emotions/img/smiley-money-mouth.gif
  207. BIN media/js/tiny_mce/plugins/emotions/img/smiley-sealed.gif
  208. BIN media/js/tiny_mce/plugins/emotions/img/smiley-smile.gif
  209. BIN media/js/tiny_mce/plugins/emotions/img/smiley-surprised.gif
  210. BIN media/js/tiny_mce/plugins/emotions/img/smiley-tongue-out.gif
  211. BIN media/js/tiny_mce/plugins/emotions/img/smiley-undecided.gif
  212. BIN media/js/tiny_mce/plugins/emotions/img/smiley-wink.gif
  213. BIN media/js/tiny_mce/plugins/emotions/img/smiley-yell.gif
  214. +22 −0 media/js/tiny_mce/plugins/emotions/js/emotions.js
  215. +20 −0 media/js/tiny_mce/plugins/emotions/langs/en_dlg.js
  216. +27 −0 media/js/tiny_mce/plugins/example/dialog.htm
  217. +1 −0 media/js/tiny_mce/plugins/example/editor_plugin.js
  218. +81 −0 media/js/tiny_mce/plugins/example/editor_plugin_src.js
  219. BIN media/js/tiny_mce/plugins/example/img/example.gif
  220. +19 −0 media/js/tiny_mce/plugins/example/js/dialog.js
  221. +3 −0 media/js/tiny_mce/plugins/example/langs/en.js
  222. +3 −0 media/js/tiny_mce/plugins/example/langs/en_dlg.js
  223. +182 −0 media/js/tiny_mce/plugins/fullpage/css/fullpage.css
  224. +1 −0 media/js/tiny_mce/plugins/fullpage/editor_plugin.js
  225. +146 −0 media/js/tiny_mce/plugins/fullpage/editor_plugin_src.js
  226. +576 −0 media/js/tiny_mce/plugins/fullpage/fullpage.htm
  227. +461 −0 media/js/tiny_mce/plugins/fullpage/js/fullpage.js
  228. +85 −0 media/js/tiny_mce/plugins/fullpage/langs/en_dlg.js
  229. +1 −0 media/js/tiny_mce/plugins/fullscreen/editor_plugin.js
  230. +145 −0 media/js/tiny_mce/plugins/fullscreen/editor_plugin_src.js
  231. +110 −0 media/js/tiny_mce/plugins/fullscreen/fullscreen.htm
  232. +1 −0 media/js/tiny_mce/plugins/iespell/editor_plugin.js
  233. +51 −0 media/js/tiny_mce/plugins/iespell/editor_plugin_src.js
  234. +1 −0 media/js/tiny_mce/plugins/inlinepopups/editor_plugin.js
  235. +632 −0 media/js/tiny_mce/plugins/inlinepopups/editor_plugin_src.js
  236. BIN media/js/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/alert.gif
  237. BIN media/js/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/button.gif
  238. BIN media/js/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif
  239. BIN media/js/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif
  240. BIN media/js/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/corners.gif
  241. BIN media/js/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif
  242. BIN media/js/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif
  243. +90 −0 media/js/tiny_mce/plugins/inlinepopups/skins/clearlooks2/window.css
  244. +387 −0 media/js/tiny_mce/plugins/inlinepopups/template.htm
  245. +1 −0 media/js/tiny_mce/plugins/insertdatetime/editor_plugin.js
  246. +80 −0 media/js/tiny_mce/plugins/insertdatetime/editor_plugin_src.js
  247. +1 −0 media/js/tiny_mce/plugins/layer/editor_plugin.js
  248. +209 −0 media/js/tiny_mce/plugins/layer/editor_plugin_src.js
  249. +6 −0 media/js/tiny_mce/plugins/media/css/content.css
  250. +16 −0 media/js/tiny_mce/plugins/media/css/media.css
  251. +1 −0 media/js/tiny_mce/plugins/media/editor_plugin.js
  252. +411 −0 media/js/tiny_mce/plugins/media/editor_plugin_src.js
  253. BIN media/js/tiny_mce/plugins/media/img/flash.gif
  254. BIN media/js/tiny_mce/plugins/media/img/flv_player.swf
  255. BIN media/js/tiny_mce/plugins/media/img/quicktime.gif
  256. BIN media/js/tiny_mce/plugins/media/img/realmedia.gif
  257. BIN media/js/tiny_mce/plugins/media/img/shockwave.gif
  258. BIN media/js/tiny_mce/plugins/media/img/trans.gif
  259. BIN media/js/tiny_mce/plugins/media/img/windowsmedia.gif
  260. +73 −0 media/js/tiny_mce/plugins/media/js/embed.js
  261. +630 −0 media/js/tiny_mce/plugins/media/js/media.js
  262. +103 −0 media/js/tiny_mce/plugins/media/langs/en_dlg.js
  263. +822 −0 media/js/tiny_mce/plugins/media/media.htm
  264. +1 −0 media/js/tiny_mce/plugins/nonbreaking/editor_plugin.js
  265. +50 −0 media/js/tiny_mce/plugins/nonbreaking/editor_plugin_src.js
  266. +1 −0 media/js/tiny_mce/plugins/noneditable/editor_plugin.js
  267. +87 −0 media/js/tiny_mce/plugins/noneditable/editor_plugin_src.js
  268. +1 −0 media/js/tiny_mce/plugins/pagebreak/css/content.css
  269. +1 −0 media/js/tiny_mce/plugins/pagebreak/editor_plugin.js
  270. +74 −0 media/js/tiny_mce/plugins/pagebreak/editor_plugin_src.js
  271. BIN media/js/tiny_mce/plugins/pagebreak/img/pagebreak.gif
  272. BIN media/js/tiny_mce/plugins/pagebreak/img/trans.gif
  273. +1 −0 media/js/tiny_mce/plugins/paste/editor_plugin.js
  274. +531 −0 media/js/tiny_mce/plugins/paste/editor_plugin_src.js
  275. +36 −0 media/js/tiny_mce/plugins/paste/js/pastetext.js
  276. +51 −0 media/js/tiny_mce/plugins/paste/js/pasteword.js
  277. +5 −0 media/js/tiny_mce/plugins/paste/langs/en_dlg.js
  278. +33 −0 media/js/tiny_mce/plugins/paste/pastetext.htm
  279. +27 −0 media/js/tiny_mce/plugins/paste/pasteword.htm
  280. +1 −0 media/js/tiny_mce/plugins/preview/editor_plugin.js
  281. +50 −0 media/js/tiny_mce/plugins/preview/editor_plugin_src.js
  282. +28 −0 media/js/tiny_mce/plugins/preview/example.html
  283. +73 −0 media/js/tiny_mce/plugins/preview/jscripts/embed.js
  284. +17 −0 media/js/tiny_mce/plugins/preview/preview.html
  285. +1 −0 media/js/tiny_mce/plugins/print/editor_plugin.js
  286. +31 −0 media/js/tiny_mce/plugins/print/editor_plugin_src.js
  287. +1 −0 media/js/tiny_mce/plugins/safari/blank.htm
  288. +1 −0 media/js/tiny_mce/plugins/safari/editor_plugin.js
  289. +438 −0 media/js/tiny_mce/plugins/safari/editor_plugin_src.js
  290. +1 −0 media/js/tiny_mce/plugins/save/editor_plugin.js
  291. +98 −0 media/js/tiny_mce/plugins/save/editor_plugin_src.js
  292. +6 −0 media/js/tiny_mce/plugins/searchreplace/css/searchreplace.css
  293. +1 −0 media/js/tiny_mce/plugins/searchreplace/editor_plugin.js
  294. +54 −0 media/js/tiny_mce/plugins/searchreplace/editor_plugin_src.js
  295. +126 −0 media/js/tiny_mce/plugins/searchreplace/js/searchreplace.js
  296. +16 −0 media/js/tiny_mce/plugins/searchreplace/langs/en_dlg.js
  297. +104 −0 media/js/tiny_mce/plugins/searchreplace/searchreplace.htm
  298. +1 −0 media/js/tiny_mce/plugins/spellchecker/css/content.css
  299. +1 −0 media/js/tiny_mce/plugins/spellchecker/editor_plugin.js
  300. +338 −0 media/js/tiny_mce/plugins/spellchecker/editor_plugin_src.js
Sorry, we could not display the entire diff because too many files (440) changed.
58 README.txt
@@ -0,0 +1,58 @@
+GETTING SET UP FOR THE FIRST TIME
+---------------------------------
+
+1. Bootstrap your environment. This only needs to be done once:
+
+ ./setup/_bootstrap.sh
+
+
+2. Create a virtual environment (it doesn't need to be named EffervescentCollective,
+ but name it something you'll remember):
+
+ mkvirtualenv EffervescentCollective
+
+
+3. Install the "pip" Python package manager in the new virtual environment:
+
+ easy_install pip
+
+
+4. Upgrade to the latest version of the packages:
+
+ ./bin/upgrade.sh
+
+5. Link the source directory into the project folder for easier access
+
+ ln -s ~/.virtualenvs/EffervescentCollective/src/ externals
+
+
+WHAT YOU SHOULD DO PERIODICALLY
+-------------------------------
+
+1. Switch to the correct virtual environment:
+
+ workon EffervescentCollective
+
+
+2. Upgrade to the latest version of the packages:
+
+ ./bin/upgrade.sh
+
+
+
+WHAT YOU SHOULD DO EVERY DAY
+----------------------------
+
+1. Upgrade to the latest code:
+
+ git pull
+
+
+2. Switch to the correct virtual environment:
+
+ workon EffervescentCollective
+
+
+3. Start your local development server:
+
+ python manage.py runserver 0.0.0.0:8000
1 __init__.py
@@ -0,0 +1 @@
+
BIN __init__.pyc
Binary file not shown.
0 apps/api/__init__.py
No changes.
BIN apps/api/__init__.pyc
Binary file not shown.
31 apps/api/handlers.py
@@ -0,0 +1,31 @@
+from piston.handler import BaseHandler
+from tagging.models import Tag, TaggedItem
+from django.db.models import get_model
+from django.core.urlresolvers import reverse
+from pprint import pprint
+
+class TagHandler(BaseHandler):
+ allowed_methods = ('GET',)
+ model = Tag
+
+ def read(self, request):
+ if 'referer' in request.GET:
+ bits = filter(None, request.GET['referer'].replace('/admin/','').split('/'))
+ model = get_model(*bits[:2])
+
+ if len(bits) == 2:
+ r = []
+ for x in Tag.objects.cloud_for_model(model):
+ r.append({'size':x.font_size,'name':x.name})
+ return r
+ elif len(bits) == 3:
+ if bits[2] == 'add':
+ return []
+ obj = model.objects.get(pk=bits[2])
+ tags = Tag.objects.get_for_object(obj)
+ if 'tags' in request.GET:
+ obj.tags = request.GET['tags']
+ else:
+ return tags
+
+
BIN apps/api/handlers.pyc
Binary file not shown.
9 apps/api/urls.py
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import *
+from piston.resource import Resource
+from handlers import TagHandler
+
+tag_handler = Resource(TagHandler)
+
+urlpatterns = patterns('',
+ url(r'^tags/?$', tag_handler),
+)
BIN apps/api/urls.pyc
Binary file not shown.
3 apps/django_ext/__init__.py
@@ -0,0 +1,3 @@
+from django.template import add_to_builtins
+
+add_to_builtins('django_ext.templatetags.smart_if')
BIN apps/django_ext/__init__.pyc
Binary file not shown.
0 apps/django_ext/admin.py
No changes.
BIN apps/django_ext/admin.pyc
Binary file not shown.
25 apps/django_ext/cache.py
@@ -0,0 +1,25 @@
+from django.core.cache import cache
+from django.utils.encoding import smart_str
+import inspect
+
+# Check if the cache backend supports min_compress_len. If so, add it.
+if cache._cache:
+ if 'min_compress_len' in inspect.getargspec(cache._cache.add)[0] and \
+ 'min_compress_len' in inspect.getargspec(cache._cache.set)[0]:
+ class CacheClass(cache.__class__):
+ def add(self, key, value, timeout=None, min_compress_len=150000):
+ if isinstance(value, unicode):
+ value = value.encode('utf-8')
+ # Allow infinite timeouts
+ if timeout is None:
+ timeout = self.default_timeout
+ return self._cache.add(smart_str(key), value, timeout, min_compress_len)
+
+ def set(self, key, value, timeout=None, min_compress_len=150000):
+ if isinstance(value, unicode):
+ value = value.encode('utf-8')
+ if timeout is None:
+ timeout = self.default_timeout
+ self._cache.set(smart_str(key), value, timeout, min_compress_len)
+
+ cache.__class__ = CacheClass
133 apps/django_ext/fields.py
@@ -0,0 +1,133 @@
+import functools
+from django.db.models.fields.related import ManyToManyField, ReverseManyRelatedObjectsDescriptor, ManyRelatedObjectsDescriptor
+from django.db.models.query import QuerySet
+from django.db.models import signals
+from django_ext.cache import cache
+from types import MethodType
+
+CACHE_DURATION = 60 * 30
+
+def invalidate_cache(obj, field):
+ cache.set(obj._get_cache_key(field=field), None, 5)
+
+def fix_where(where, modified=False):
+ def wrap_add(f):
+ @functools.wraps(f)
+ def add(self, *args, **kwargs):
+ """
+ Wraps django.db.models.sql.where.add to indicate that a new
+ 'where' condition has been added.
+ """
+ self.modified = True
+ return f(*args, **kwargs)
+ return add
+ where.modified = modified
+ where.add = MethodType(wrap_add(where.add), where, where.__class__)
+ return where
+
+
+def get_pk_list_query_set(superclass):
+ class PKListQuerySet(superclass):
+ """
+ QuerySet that, when unfiltered, fetches objects individually from
+ the datastore by pk.
+
+ The `pk_list` attribute is a list of primary keys for objects that
+ should be fetched.
+
+ """
+ def __init__(self, pk_list=[], from_cache=False, *args, **kwargs):
+ super(PKListQuerySet, self).__init__(*args, **kwargs)
+ self.pk_list = pk_list
+ self.from_cache = from_cache
+ self.query.where = fix_where(self.query.where)
+
+ def iterator(self):
+ if not self.query.where.modified:
+ for pk in self.pk_list:
+ yield self.model._default_manager.get(pk=pk)
+ else:
+ superiter = super(PKListQuerySet, self).iterator()
+ while True:
+ yield superiter.next()
+
+ def _clone(self, *args, **kwargs):
+ c = super(PKListQuerySet, self)._clone(*args, **kwargs)
+ c.query.where = fix_where(c.query.where, modified=self.query.where.modified)
+ c.pk_list = self.pk_list
+ c.from_cache = self.from_cache
+ return c
+ return PKListQuerySet
+
+
+def get_caching_related_manager(superclass, instance, field_name, related_name):
+ class CachingRelatedManager(superclass):
+ def all(self):
+ key = instance._get_cache_key(field=field_name)
+ qs = super(CachingRelatedManager, self).get_query_set()
+ PKListQuerySet = get_pk_list_query_set(qs.__class__)
+ qs = qs._clone(klass=PKListQuerySet)
+ pk_list = cache.get(key)
+ if pk_list is None:
+ pk_list = qs.values_list('pk', flat=True)
+ cache.add(key, pk_list, CACHE_DURATION)
+ else:
+ qs.from_cache = True
+ qs.pk_list = pk_list
+ return qs
+
+ def add(self, *objs):
+ super(CachingRelatedManager, self).add(*objs)
+ for obj in objs:
+ invalidate_cache(obj, related_name)
+ invalidate_cache(instance, field_name)
+
+ def remove(self, *objs):
+ super(CachingRelatedManager, self).remove(*objs)
+ for obj in objs:
+ invalidate_cache(obj, related_name)
+ invalidate_cache(instance, field_name)
+
+ def clear(self):
+ objs = list(self.all())
+ super(CachingRelatedManager, self).clear()
+ for obj in objs:
+ invalidate_cache(obj, related_name)
+ invalidate_cache(instance, field_name)
+ return CachingRelatedManager
+
+
+class CachingReverseManyRelatedObjectsDescriptor(ReverseManyRelatedObjectsDescriptor):
+ def __get__(self, instance, cls=None):
+ manager = super(CachingReverseManyRelatedObjectsDescriptor, self).__get__(instance, cls)
+
+ CachingRelatedManager = get_caching_related_manager(manager.__class__,
+ instance,
+ self.field.name,
+ self.field.rel.related_name)
+
+ manager.__class__ = CachingRelatedManager
+ return manager
+
+
+class CachingManyRelatedObjectsDescriptor(ManyRelatedObjectsDescriptor):
+ def __get__(self, instance, cls=None):
+ manager = super(CachingManyRelatedObjectsDescriptor, self).__get__(instance, cls)
+
+ CachingRelatedManager = get_caching_related_manager(manager.__class__,
+ instance,
+ self.related.get_accessor_name(),
+ self.related.field.name)
+
+ manager.__class__ = CachingRelatedManager
+ return manager
+
+
+class CachingManyToManyField(ManyToManyField):
+ def contribute_to_class(self, cls, name):
+ super(CachingManyToManyField, self).contribute_to_class(cls, name)
+ setattr(cls, self.name, CachingReverseManyRelatedObjectsDescriptor(self))
+
+ def contribute_to_related_class(self, cls, related):
+ super(CachingManyToManyField, self).contribute_to_related_class(cls, related)
+ setattr(cls, related.get_accessor_name(), CachingManyRelatedObjectsDescriptor(related))
1 apps/django_ext/management/__init__.py
@@ -0,0 +1 @@
+
BIN apps/django_ext/management/__init__.pyc
Binary file not shown.
0 apps/django_ext/management/commands/__init__.py
No changes.
20 apps/django_ext/management/commands/delete_contenttypes.py
@@ -0,0 +1,20 @@
+from django.core.management.base import NoArgsCommand
+from django.db import transaction
+
+class Command(NoArgsCommand):
+ help = "Delete contenttypes crapola"
+
+ def handle_noargs(self, **options):
+ from django.db import connection
+ print "Deleting content types and permissions"
+ transaction.commit_unless_managed()
+ transaction.enter_transaction_management()
+ transaction.managed(True)
+ cursor = connection.cursor()
+ cursor.execute("DELETE FROM django_content_type", [])
+ print cursor.fetchall()
+ cursor.execute("DELETE FROM auth_permission", [])
+ print cursor.fetchall()
+ transaction.commit()
+ transaction.leave_transaction_management()
+ print "Contenttypes deleted"
17 apps/django_ext/management/commands/flush_all_memcached.py
@@ -0,0 +1,17 @@
+from django.core.management.base import NoArgsCommand
+from django.db import transaction
+from django.conf import settings
+
+class Command(NoArgsCommand):
+ help = "Flushes memcached"
+
+ def handle_noargs(self, **options):
+ try:
+ import memcache
+ except ImportError:
+ return
+ if 'memcached' in settings.CACHE_BACKEND:
+ servers = settings.CACHE_BACKEND.replace('memcached://',
+ '').replace('/','').split(';')
+ mc = memcache.Client(servers, debug=0)
+ mc.flush_all()
87 apps/django_ext/managers.py
@@ -0,0 +1,87 @@
+from django.db import models
+from django.db.models import signals
+from django_ext.cache import cache
+from django.db.models.query import QuerySet
+
+CACHE_DURATION = 60 * 30
+
+def _cache_key(model, pk, field=None):
+ if field:
+ return "%s:%s.%s:%s" % (field, model._meta.app_label, model._meta.module_name, pk)
+ else:
+ return "%s.%s:%s" % (model._meta.app_label, model._meta.module_name, pk)
+
+def _get_cache_key(self, field=None):
+ return self._cache_key(self.pk, field)
+
+
+class CachingManager(models.Manager):
+ def __init__(self, use_for_related_fields=True, *args, **kwargs):
+ self.use_for_related_fields = use_for_related_fields
+ super(CachingManager, self).__init__(*args, **kwargs)
+
+ def get_query_set(self):
+ return CachingQuerySet(self.model)
+
+ def contribute_to_class(self, model, name):
+ signals.post_save.connect(self._post_save, sender=model)
+ signals.post_delete.connect(self._post_delete, sender=model)
+ setattr(model, '_cache_key', classmethod(_cache_key))
+ setattr(model, '_get_cache_key', _get_cache_key)
+ setattr(model, 'cache_key', property(_get_cache_key))
+ return super(CachingManager, self).contribute_to_class(model, name)
+
+ def _invalidate_cache(self, instance):
+ """
+ Explicitly set a None value instead of just deleting so we don't have any race
+ conditions where:
+ Thread 1 -> Cache miss, get object from DB
+ Thread 2 -> Object saved, deleted from cache
+ Thread 1 -> Store (stale) object fetched from DB in cache
+ Five second should be more than enough time to prevent this from happening for
+ a web app.
+ """
+ cache.set(instance.cache_key, None, 5)
+
+ def _post_save(self, instance, **kwargs):
+ self._invalidate_cache(instance)
+
+ def _post_delete(self, instance, **kwargs):
+ self._invalidate_cache(instance)
+
+
+class CachingQuerySet(QuerySet):
+ def iterator(self):
+ superiter = super(CachingQuerySet, self).iterator()
+ while True:
+ obj = superiter.next()
+ # Use cache.add instead of cache.set to prevent race conditions (see CachingManager)
+ cache.add(obj.cache_key, obj, CACHE_DURATION)
+ yield obj
+
+ def get(self, *args, **kwargs):
+ """
+ Checks the cache to see if there's a cached entry for this pk. If not, fetches
+ using super then stores the result in cache.
+
+ Most of the logic here was gathered from a careful reading of
+ ``django.db.models.sql.query.add_filter``
+ """
+ if self.query.where:
+ # If there is any other ``where`` filter on this QuerySet just call
+ # super. There will be a where clause if this QuerySet has already
+ # been filtered/cloned.
+ return super(CachingQuerySet, self).get(*args, **kwargs)
+
+ # Punt on anything more complicated than get by pk/id only...
+ if len(kwargs) == 1:
+ k = kwargs.keys()[0]
+ if k in ('pk', 'pk__exact', '%s' % self.model._meta.pk.attname,
+ '%s__exact' % self.model._meta.pk.attname):
+ obj = cache.get(self.model._cache_key(pk=kwargs.values()[0]))
+ if obj is not None:
+ obj.from_cache = True
+ return obj
+
+ # Calls self.iterator to fetch objects, storing object in cache.
+ return super(CachingQuerySet, self).get(*args, **kwargs)
48 apps/django_ext/middleware.py
@@ -0,0 +1,48 @@
+from django.db import connection
+from django.conf import settings
+import os
+
+def terminal_width():
+ """
+ Function to compute the terminal width.
+ WARNING: This is not my code, but I've been using it forever and
+ I don't remember where it came from.
+ """
+ width = 0
+ try:
+ import struct, fcntl, termios
+ s = struct.pack('HHHH', 0, 0, 0, 0)
+ x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
+ width = struct.unpack('HHHH', x)[1]
+ except:
+ pass
+ if width <= 0:
+ try:
+ width = int(os.environ['COLUMNS'])
+ except:
+ pass
+ if width <= 0:
+ width = 80
+ return width
+
+class SqlPrintingMiddleware(object):
+ """
+ Middleware which prints out a list of all SQL queries done
+ for each view that is processed. This is only useful for debugging.
+ """
+ def process_response(self, request, response):
+ indentation = 2
+ if len(connection.queries) > 0 and settings.DEBUG:
+ width = terminal_width()
+ total_time = 0.0
+ for query in connection.queries:
+ nice_sql = query['sql'].replace('"', '').replace(',',', ')
+ sql = "\033[1;31m[%s]\033[0m %s" % (query['time'], nice_sql)
+ total_time = total_time + float(query['time'])
+ while len(sql) > width-indentation:
+ print "%s%s" % (" "*indentation, sql[:width-indentation])
+ sql = sql[width-indentation:]
+ print "%s%s\n" % (" "*indentation, sql)
+ replace_tuple = (" "*indentation, str(total_time))
+ print "%s\033[1;32m[TOTAL TIME: %s seconds]\033[0m" % replace_tuple
+ return response
1 apps/django_ext/models.py
@@ -0,0 +1 @@
+
BIN apps/django_ext/models.pyc
Binary file not shown.
1 apps/django_ext/templatetags/__init__.py
@@ -0,0 +1 @@
+
BIN apps/django_ext/templatetags/__init__.pyc
Binary file not shown.
98 apps/django_ext/templatetags/fb.py
@@ -0,0 +1,98 @@
+from django import template
+from django.conf import settings
+
+from django.utils.encoding import smart_str
+
+register = template.Library()
+
+class URLNode(template.Node):
+ def __init__(self, view_name, args, kwargs, asvar):
+ self.view_name = view_name
+ self.args = args
+ self.kwargs = kwargs
+ self.asvar = asvar
+
+ def render(self, context):
+ from django.core.urlresolvers import reverse, NoReverseMatch
+ args = [arg.resolve(context) for arg in self.args]
+ kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
+ for k, v in self.kwargs.items()])
+
+ # Try to look up the URL twice: once given the view name, and again
+ # relative to what we guess is the "main" app. If they both fail,
+ # re-raise the NoReverseMatch unless we're using the
+ # {% url ... as var %} construct in which cause return nothing.
+ url = ''
+ try:
+ url = reverse(self.view_name, args=args, kwargs=kwargs)
+ except NoReverseMatch:
+ project_name = settings.SETTINGS_MODULE.split('.')[0]
+ try:
+ url = reverse(project_name + '.' + self.view_name,
+ args=args, kwargs=kwargs)
+ except NoReverseMatch:
+ if self.asvar is None:
+ raise
+
+ if self.asvar:
+ context[self.asvar] = settings.URL_PREFIX + url
+ return ''
+ else:
+ return settings.URL_PREFIX + url
+
+def fburl(parser, token):
+ """
+ Returns an absolute URL matching given view with its parameters.
+
+ This is a way to define links that aren't tied to a particular URL
+ configuration::
+
+ {% url path.to.some_view arg1,arg2,name1=value1 %}
+
+ The first argument is a path to a view. It can be an absolute python path
+ or just ``app_name.view_name`` without the project name if the view is
+ located inside the project. Other arguments are comma-separated values
+ that will be filled in place of positional and keyword arguments in the
+ URL. All arguments for the URL should be present.
+
+ For example if you have a view ``app_name.client`` taking client's id and
+ the corresponding line in a URLconf looks like this::
+
+ ('^client/(\d+)/$', 'app_name.client')
+
+ and this app's URLconf is included into the project's URLconf under some
+ path::
+
+ ('^clients/', include('project_name.app_name.urls'))
+
+ then in a template you can create a link for a certain client like this::
+
+ {% url app_name.client client.id %}
+
+ The URL will look like ``/clients/client/123/``.
+ """
+ bits = token.contents.split(' ')
+ if len(bits) < 2:
+ raise template.TemplateSyntaxError("'%s' takes at least one argument"
+ " (path to a view)" % bits[0])
+ viewname = bits[1]
+ args = []
+ kwargs = {}
+ asvar = None
+
+ if len(bits) > 2:
+ bits = iter(bits[2:])
+ for bit in bits:
+ if bit == 'as':
+ asvar = bits.next()
+ break
+ else:
+ for arg in bit.split(","):
+ if '=' in arg:
+ k, v = arg.split('=', 1)
+ k = k.strip()
+ kwargs[k] = parser.compile_filter(v)
+ elif arg:
+ args.append(parser.compile_filter(arg))
+ return URLNode(viewname, args, kwargs, asvar)
+fburl = register.tag(fburl)
BIN apps/django_ext/templatetags/fb.pyc
Binary file not shown.
104 apps/django_ext/templatetags/listutil.py
@@ -0,0 +1,104 @@
+"""
+Template tags for working with lists.
+
+You'll use these in templates thusly::
+
+ {% load listutil %}
+ {% for sublist in mylist|parition:"3" %}
+ {% for item in mylist %}
+ do something with {{ item }}
+ {% endfor %}
+ {% endfor %}
+"""
+
+from math import ceil
+
+from django import template
+
+register = template.Library()
+
+@register.filter
+def partition(thelist, n):
+ """
+ Break a list into ``n`` pieces. The last list may be larger than the rest if
+ the list doesn't break cleanly. That is::
+
+ >>> l = range(10)
+
+ >>> partition(l, 2)
+ [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
+
+ >>> partition(l, 3)
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]
+
+ >>> partition(l, 4)
+ [[0, 1], [2, 3], [4, 5], [6, 7, 8, 9]]
+
+ >>> partition(l, 5)
+ [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
+
+ """
+ try:
+ n = int(n)
+ thelist = list(thelist)
+ except (ValueError, TypeError):
+ return [thelist]
+ p = len(thelist) / n
+ return [thelist[p*i:p*(i+1)] for i in range(n - 1)] + [thelist[p*(i+1):]]
+register.filter(partition)
+
+
+@register.filter
+def partition_horizontal(thelist, n):
+ """
+ Break a list into ``n`` peices, but "horizontally." That is,
+ ``partition_horizontal(range(10), 3)`` gives::
+
+ [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9],
+ [10]]
+
+ Clear as mud?
+ """
+ try:
+ n = int(n)
+ thelist = list(thelist)
+ except (ValueError, TypeError):
+ return [thelist]
+ newlists = [list() for i in range(int(ceil(len(thelist) / float(n))))]
+ for i, val in enumerate(thelist):
+ newlists[i/n].append(val)
+ return newlists
+register.filter(partition_horizontal)
+
+
+@register.filter
+def partition_horizontal_twice(thelist, numbers):
+ """
+ numbers is split on a comma to n and n2.
+ Break a list into peices each peice alternating between n and n2 items long
+ ``partition_horizontal_twice(range(14), "3,4")`` gives::
+
+ [[0, 1, 2],
+ [3, 4, 5, 6],
+ [7, 8, 9],
+ [10, 11, 12, 13]]
+
+ Clear as mud?
+ """
+ n, n2 = numbers.split(',')
+ try:
+ n = int(n)
+ n2 = int(n2)
+ thelist = list(thelist)
+ except (ValueError, TypeError):
+ return [thelist]
+ newlists = []
+ while thelist:
+ newlists.append(thelist[:n])
+ thelist = thelist[n:]
+ newlists.append(thelist[:n2])
+ thelist = thelist[n2:]
+ return newlists
+register.filter(partition_horizontal_twice)
BIN apps/django_ext/templatetags/listutil.pyc
Binary file not shown.
333 apps/django_ext/templatetags/smart_if.py
@@ -0,0 +1,333 @@
+'''
+A smarter {% if %} tag for django templates.
+
+While retaining current Django functionality, it also handles equality,
+greater than and less than operators. Some common case examples::
+
+ {% if articles|length >= 5 %}...{% endif %}
+ {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
+'''
+import unittest
+from django import template
+
+
+register = template.Library()
+
+
+#===============================================================================
+# Calculation objects
+#===============================================================================
+
+class BaseCalc(object):
+ def __init__(self, var1, var2=None, negate=False):
+ self.var1 = var1
+ self.var2 = var2
+ self.negate = negate
+
+ def resolve(self, context):
+ try:
+ var1, var2 = self.resolve_vars(context)
+ outcome = self.calculate(var1, var2)
+ except:
+ outcome = False
+ if self.negate:
+ return not outcome
+ return outcome
+
+ def resolve_vars(self, context):
+ var2 = self.var2 and self.var2.resolve(context)
+ return self.var1.resolve(context), var2
+
+ def calculate(self, var1, var2):
+ raise NotImplementedError()
+
+
+class Or(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 or var2
+
+
+class And(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 and var2
+
+
+class Equals(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 == var2
+
+
+class Greater(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 > var2
+
+
+class GreaterOrEqual(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 >= var2
+
+
+class In(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 in var2
+
+
+#===============================================================================
+# Tests
+#===============================================================================
+
+class TestVar(object):
+ """
+ A basic self-resolvable object similar to a Django template variable. Used
+ to assist with tests.
+ """
+ def __init__(self, value):
+ self.value = value
+
+ def resolve(self, context):
+ return self.value
+
+
+class SmartIfTests(unittest.TestCase):
+ def setUp(self):
+ self.true = TestVar(True)
+ self.false = TestVar(False)
+ self.high = TestVar(9000)
+ self.low = TestVar(1)
+
+ def assertCalc(self, calc, context=None):
+ """
+ Test a calculation is True, also checking the inverse "negate" case.
+ """
+ context = context or {}
+ self.assert_(calc.resolve(context))
+ calc.negate = not calc.negate
+ self.assertFalse(calc.resolve(context))
+
+ def assertCalcFalse(self, calc, context=None):
+ """
+ Test a calculation is False, also checking the inverse "negate" case.
+ """
+ context = context or {}
+ self.assertFalse(calc.resolve(context))
+ calc.negate = not calc.negate
+ self.assert_(calc.resolve(context))
+
+ def test_or(self):
+ self.assertCalc(Or(self.true))
+ self.assertCalcFalse(Or(self.false))
+ self.assertCalc(Or(self.true, self.true))
+ self.assertCalc(Or(self.true, self.false))
+ self.assertCalc(Or(self.false, self.true))
+ self.assertCalcFalse(Or(self.false, self.false))
+
+ def test_and(self):
+ self.assertCalc(And(self.true, self.true))
+ self.assertCalcFalse(And(self.true, self.false))
+ self.assertCalcFalse(And(self.false, self.true))
+ self.assertCalcFalse(And(self.false, self.false))
+
+ def test_equals(self):
+ self.assertCalc(Equals(self.low, self.low))
+ self.assertCalcFalse(Equals(self.low, self.high))
+
+ def test_greater(self):
+ self.assertCalc(Greater(self.high, self.low))
+ self.assertCalcFalse(Greater(self.low, self.low))
+ self.assertCalcFalse(Greater(self.low, self.high))
+
+ def test_greater_or_equal(self):
+ self.assertCalc(GreaterOrEqual(self.high, self.low))
+ self.assertCalc(GreaterOrEqual(self.low, self.low))
+ self.assertCalcFalse(GreaterOrEqual(self.low, self.high))
+
+ def test_in(self):
+ list_ = TestVar([1,2,3])
+ invalid_list = TestVar(None)
+ self.assertCalc(In(self.low, list_))
+ self.assertCalcFalse(In(self.low, invalid_list))
+
+ def test_parse_bits(self):
+ var = IfParser([True]).parse()
+ self.assert_(var.resolve({}))
+ var = IfParser([False]).parse()
+ self.assertFalse(var.resolve({}))
+
+ var = IfParser([False, 'or', True]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([False, 'and', True]).parse()
+ self.assertFalse(var.resolve({}))
+
+ var = IfParser(['not', False, 'and', 'not', False]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([1, '=', 1]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([1, '!=', 1]).parse()
+ self.assertFalse(var.resolve({}))
+
+ var = IfParser([3, '>', 2]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([1, '<', 2]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([2, 'not', 'in', [2, 3]]).parse()
+ self.assertFalse(var.resolve({}))
+
+
+OPERATORS = {
+ '=': (Equals, True),
+ '==': (Equals, True),
+ '!=': (Equals, False),
+ '>': (Greater, True),
+ '>=': (GreaterOrEqual, True),
+ '<=': (Greater, False),
+ '<': (GreaterOrEqual, False),
+ 'or': (Or, True),
+ 'and': (And, True),
+ 'in': (In, True),
+}
+
+
+class IfParser(object):
+ error_class = ValueError
+
+ def __init__(self, tokens):
+ self.tokens = tokens
+
+ def _get_tokens(self):
+ return self._tokens
+
+ def _set_tokens(self, tokens):
+ self._tokens = tokens
+ self.len = len(tokens)
+ self.pos = 0
+
+ tokens = property(_get_tokens, _set_tokens)
+
+ def parse(self):
+ if self.at_end():
+ raise self.error_class('No variables provided.')
+ var1 = self.get_var()
+ while not self.at_end():
+ token = self.get_token()
+ if token == 'not':
+ if self.at_end():
+ raise self.error_class('No variable provided after "not".')
+ token = self.get_token()
+ negate = True
+ else:
+ negate = False
+ if token not in OPERATORS:
+ raise self.error_class('%s is not a valid operator.' % token)
+ if self.at_end():
+ raise self.error_class('No variable provided after "%s"' % token)
+ op, true = OPERATORS[token]
+ if not true:
+ negate = not negate
+ var2 = self.get_var()
+ var1 = op(var1, var2, negate=negate)
+ return var1
+
+ def get_token(self):
+ token = self.tokens[self.pos]
+ self.pos += 1
+ return token
+
+ def at_end(self):
+ return self.pos >= self.len
+
+ def create_var(self, value):
+ return TestVar(value)
+
+ def get_var(self):
+ token = self.get_token()
+ if token == 'not':
+ if self.at_end():
+ raise self.error_class('No variable provided after "not".')
+ token = self.get_token()
+ return Or(self.create_var(token), negate=True)
+ return self.create_var(token)
+
+
+#===============================================================================
+# Actual templatetag code.
+#===============================================================================
+
+class TemplateIfParser(IfParser):
+ error_class = template.TemplateSyntaxError
+
+ def __init__(self, parser, *args, **kwargs):
+ self.template_parser = parser
+ return super(TemplateIfParser, self).__init__(*args, **kwargs)
+
+ def create_var(self, value):
+ return self.template_parser.compile_filter(value)
+
+
+class SmartIfNode(template.Node):
+ def __init__(self, var, nodelist_true, nodelist_false=None):
+ self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
+ self.var = var
+
+ def render(self, context):
+ if self.var.resolve(context):
+ return self.nodelist_true.render(context)
+ if self.nodelist_false:
+ return self.nodelist_false.render(context)
+ return ''
+
+ def __repr__(self):
+ return "<Smart If node>"
+
+ def __iter__(self):
+ for node in self.nodelist_true:
+ yield node
+ if self.nodelist_false:
+ for node in self.nodelist_false:
+ yield node
+
+ def get_nodes_by_type(self, nodetype):
+ nodes = []
+ if isinstance(self, nodetype):
+ nodes.append(self)
+ nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
+ if self.nodelist_false:
+ nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
+ return nodes
+
+
+@register.tag('if')
+def smart_if(parser, token):
+ '''
+ A smarter {% if %} tag for django templates.
+
+ While retaining current Django functionality, it also handles equality,
+ greater than and less than operators. Some common case examples::
+
+ {% if articles|length >= 5 %}...{% endif %}
+ {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
+
+ Arguments and operators _must_ have a space between them, so
+ ``{% if 1>2 %}`` is not a valid smart if tag.
+
+ All supported operators are: ``or``, ``and``, ``in``, ``=`` (or ``==``),
+ ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
+ '''
+ bits = token.split_contents()[1:]
+ var = TemplateIfParser(parser, bits).parse()
+ nodelist_true = parser.parse(('else', 'endif'))
+ token = parser.next_token()
+ if token.contents == 'else':
+ nodelist_false = parser.parse(('endif',))
+ parser.delete_first_token()
+ else:
+ nodelist_false = None
+ return SmartIfNode(var, nodelist_true, nodelist_false)
+
+
+if __name__ == '__main__':
+ unittest.main()
BIN apps/django_ext/templatetags/smart_if.pyc
Binary file not shown.
14 apps/django_ext/views.py
@@ -0,0 +1,14 @@
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+
+clean = lambda i: '/'+'/'.join(i['location'].split('/')[3:])
+
+def sitemap(request, sitemaps):
+ maps = {}
+ for section, site in sitemaps.items():
+ if callable(site):
+ maps[section] = map(clean, site().get_urls())
+ else:
+ maps[section] = map(clean, site.get_urls())
+ return render_to_response('sitemap.html',{'maps':maps},
+ context_instance=RequestContext(request))
BIN apps/django_ext/views.pyc
Binary file not shown.
28 bin/ext-status.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+if [ "$VIRTUAL_ENV" = "" ]; then
+ echo "You must be in a virtualenv environment. Type workon for a list."
+ exit
+fi
+
+FILES=$VIRTUAL_ENV/src/*
+
+for f in $FILES
+do
+ if [ -d $f ]
+ then
+ cd $f
+ if [ -e .git ]
+ then
+ git status
+ elif [ -e .bzr ]
+ then
+ bzr status
+ elif [ -e .hg ]
+ then
+ hg status
+ elif [ -e .svn ]
+ then
+ svn status
+ fi
+ fi
+done
32 bin/install.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+if [ "$VIRTUAL_ENV" = "" ]; then
+ echo "You must be in a virtualenv environment. Type workon for a list."
+ exit
+fi
+
+PWD=`pwd`
+POSTACTIVATE=$VIRTUAL_ENV/bin/postactivate
+
+if [ -e externals ]; then
+ echo 'Externals link exists.'
+else
+ echo 'Creating link: externals.'
+ ln -s $VIRTUAL_ENV/src externals
+fi
+
+if [ -e $POSTACTIVATE ]; then
+ echo 'Postactivate script exists.'
+else
+ echo 'Creating postactivate script.'
+ cat > $POSTACTIVATE <<END
+ #!/bin/bash/
+ cd $PWD
+END
+ chmod +x $POSTACTIVATE
+fi
+
+pip install -U -r setup/requirements.txt
+rm -Rf src
+rm -Rf build
+rm -Rf pip-log.txt
28 bin/pull-ext.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+if [ "$VIRTUAL_ENV" = "" ]; then
+ echo "You must be in a virtualenv environment. Type workon for a list."
+ exit
+fi
+
+FILES=$VIRTUAL_ENV/src/*
+
+for f in $FILES
+do
+ if [ -d $f ]
+ then
+ cd $f
+ if [ -e .git ]
+ then
+ git pull origin master
+ elif [ -e .bzr ]
+ then
+ bzr merge
+ elif [ -e .hg ]
+ then
+ hg pull
+ elif [ -e .svn ]
+ then
+ svn up
+ fi
+ fi
+done
28 bin/push-ext.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+if [ "$VIRTUAL_ENV" = "" ]; then
+ echo "You must be in a virtualenv environment. Type workon for a list."
+ exit
+fi
+
+FILES=$VIRTUAL_ENV/src/*
+
+for f in $FILES
+do
+ if [ -d $f ]
+ then
+ cd $f
+ if [ -e .git ]
+ then
+ git push origin master
+ # elif [ -e .bzr ]
+ # then
+ # bzr merge
+ # elif [ -e .hg ]
+ # then
+ # hg pull
+ # elif [ -e .svn ]
+ # then
+ # svn up
+ fi
+ fi
+done
8 bin/upgrade.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+if [ "$VIRTUAL_ENV" = "" ]; then
+ echo "You must be in a virtualenv environment. Type workon for a list."
+ exit
+fi
+
+git pull origin master
+bin/pull-ext.sh
18 conf/EffervescentCollective.wsgi
@@ -0,0 +1,18 @@
+import os, sys, site
+
+site.addsitedir('/home/webdev/EffervescentCollective/.virtualenvs/EffervescentCollective/lib/python2.5/site-packages')
+
+PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+sys.path.insert(0, os.path.join(PROJECT_ROOT,"apps"))
+sys.path.insert(0, os.path.join(PROJECT_ROOT,"lib"))
+sys.path.insert(0, PROJECT_ROOT)
+
+sys.stdout = sys.stderr
+
+if PROJECT_ROOT not in sys.path:
+ sys.path.append(PROJECT_ROOT)
+os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+
+import django.core.handlers.wsgi
+
+application = django.core.handlers.wsgi.WSGIHandler()
35 conf/apache2-EffervescentCollective
@@ -0,0 +1,35 @@
+<VirtualHost *:80>
+ ServerAdmin webmaster@washingtontimes.com
+ ServerName EffervescentCollective
+ ServerAlias media-EffervescentCollective
+ DocumentRoot /var/code/EffervescentCollective/media
+
+ WSGIDaemonProcess EffervescentCollective user=webdev group=webdev processes=3 threads=1 maximum-requests=1000 python-path=/home/webdev/.virtualenvs/EffervescentCollective/lib/python2.5/site-packages
+ WSGIProcessGroup EffervescentCollective
+ WSGIScriptAlias / /var/code/EffervescentCollective/conf/EffervescentCollective.wsgi
+
+ Alias /media /var/code/EffervescentCollective/media
+ <Directory /var/code/EffervescentCollective/media>
+ SetHandler None
+ AddOutputFilterByType DEFLATE text/plain
+ AddOutputFilterByType DEFLATE text/xml
+ AddOutputFilterByType DEFLATE application/xhtml+xml
+ AddOutputFilterByType DEFLATE text/css
+ AddOutputFilterByType DEFLATE application/xml
+ AddOutputFilterByType DEFLATE image/svg+xml
+ AddOutputFilterByType DEFLATE application/rss+xml
+ AddOutputFilterByType DEFLATE application/atom_xml
+ AddOutputFilterByType DEFLATE application/x-javascript
+ AddOutputFilterByType DEFLATE application/x-httpd-php
+ AddOutputFilterByType DEFLATE application/x-httpd-fastphp
+ AddOutputFilterByType DEFLATE application/x-httpd-eruby
+ AddOutputFilterByType DEFLATE text/html
+ FileETag INode MTime Size
+ </Directory>
+
+ Alias /admin-media /home/webdev/.virtualenvs/EffervescentCollective/lib/python2.5/site-packages/django/contrib/admin/media
+ <Directory /home/webdev/.virtualenvs/EffervescentCollective/lib/python2.5/site-packages/django/contrib/admin/media>
+ SetHandler None
+ </Directory>
+
+</VirtualHost>
23 conf/nginx-EffervescentCollective
@@ -0,0 +1,23 @@
+#upstream webcluster {
+# server 127.0.0.1:8080;
+#}
+server {
+ listen 80;
+ server_name media-$$$$DEV_APP_HOST$$$$;
+ access_log /var/log/nginx/media-$$$$DEV_APP_HOST$$$$.access.log;
+ location / {
+ autoindex on;
+ index index.html;
+ root /var/code/EffervescentCollective/media;
+ expires max;
+ }
+}
+server {
+ listen 80;
+ server_name $$$$DEV_APP_HOST$$$$;
+ access_log /var/log/nginx/$$$$DEV_APP_HOST$$$$.access.log;
+ location / {
+ proxy_pass http://webcluster;
+ include /var/code/EffervescentCollective/conf/proxy.conf;
+ }
+}
13 conf/proxy.conf
@@ -0,0 +1,13 @@
+proxy_redirect off;
+proxy_set_header Host $host;
+proxy_set_header X-Real-IP $remote_addr;
+proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+client_max_body_size 10m;
+client_body_buffer_size 128k;
+proxy_connect_timeout 90;
+proxy_send_timeout 90;
+proxy_read_timeout 90;
+proxy_buffer_size 4k;
+proxy_buffers 4 32k;
+proxy_busy_buffers_size 64k;
+proxy_temp_file_write_size 64k;
BIN dev.db
Binary file not shown.
18 local_settings.py
@@ -0,0 +1,18 @@
+import os
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+DATABASE_ENGINE = 'sqlite3'
+DATABASE_NAME = 'dev.db'
+DATABASE_USER = ''
+DATABASE_PASSWORD = ''
+DATABASE_HOST = ''
+DATABASE_PORT = ''
+
+MEDIA_URL = '/media/'
+MEDIA_ROOT = os.path.join(os.path.dirname(__file__),'media')
+ADMIN_MEDIA_PREFIX = '/admin-media/'
+
+
+CACHE_BACKEND = "dummy:///"
BIN local_settings.pyc
Binary file not shown.
18 manage.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+import os, sys
+
+PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
+sys.path.insert(0, os.path.join(PROJECT_ROOT,"apps"))
+sys.path.insert(0, os.path.join(PROJECT_ROOT,"lib"))
+sys.path.insert(0, PROJECT_ROOT)
+
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ execute_manager(settings)
BIN media/.DS_Store
Binary file not shown.
BIN media/Nautica05b/Thumbs.db
Binary file not shown.
263 media/Nautica05b/contact.html
@@ -0,0 +1,263 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+
+
+ <title>Nautica 05 Dark Contact Layout</title>
+
+ <meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8" />
+ <meta name="author" content="fullahead.org - studio7designs.com" />
+ <meta name="description" content="Site Description Here" />
+ <meta name="keywords" content="keywords, here" />
+ <meta name="robots" content="index, follow, noarchive" />
+ <meta name="googlebot" content="noarchive" />
+
+ <link rel="stylesheet" type="text/css" href="css/layout.css" media="screen, projection, tv " />
+ <link rel="stylesheet" type="text/css" href="css/html.css" media="screen, projection, tv " />
+
+ <!-- CSS specific to current theme -->
+ <link rel="stylesheet" type="text/css" href="css/light.css" title="light" media="screen, projection, tv " />
+ <link rel="alternate stylesheet" type="text/css" href="css/dark.css" title="dark" media="screen, projection, tv " />
+
+</head>
+
+<body>
+
+<!-- #content: holds all except site footer - causes footer to stick to bottom -->
+<div id="content">
+
+ <!-- #header: holds the logo and top links -->
+ <div id="header" class="width">
+
+ <img src="images/logo.gif" alt="Your logo goes here"/>
+
+ <ul>
+ <li><a href="index.html">Back to Main page</a></li>
+ <li><a href="">About us</a></li>
+ <li><a href="">Streaming Video</a></li>
+ <li><a href="" class="last">RSS Feeds</a></li>
+ </ul>
+
+ </div>
+ <!-- #header end -->
+
+
+ <!-- #headerImg: holds the main header image or flash -->
+ <div id="headerImg" class="width"></div>
+
+
+
+
+ <!-- #menu: the main large box site menu -->
+ <div id="menu" class="width">
+
+ <ul>
+ <li>
+ <a href="onecol.html" onfocus="blur()">
+ <span class="title ">One Column Layout</span>
+ <span class="desc">View the included layout</span> </a> </li>
+ <li>
+ <a href="twocol_a.html" class="forum" onfocus="blur()">
+ <span class="title ">Two Column Layout A</span>
+ <span class="desc style3">View the included layout</span> </a> </li>
+ <li>
+ <a href="twocol_b.html" onfocus="blur()">
+ <span class="title ">Two Column Layout B</span>
+ <span class="desc">View the included layout</span>
+ </a>
+ </li>
+ <li>
+ <a href="contact.html" onfocus="blur()">
+ <span class="title ">Contact Us Layout</span>
+ <span class="desc">View the included layout</span>
+ </a>
+ </li>
+ </ul>
+
+ </div>
+ <!-- #menu end -->
+
+
+
+ <!-- #page: holds the page content -->
+ <div id="page">
+
+
+ <!-- #columns: holds the columns of the page -->
+ <div id="columns" class="widthPad">
+
+
+ <!-- Left column -->
+ <div class="floatLeft width25 lightBlueBg horzPad">
+
+ <h2>Details</h2>
+
+ <p>
+ Please make sure your info is correct, otherwise we can't get back to you.
+ </p>
+
+ <ul>
+ <li>613.555.5555</li>
+ <li><a href="">info@yourname.com</a></li>
+ </ul>
+
+ <p>
+ Nulla commodo. In nunc justo, mollis sed, gravida at, aliquam sit amet, urna. Nulla commodo. In pharetra justo eget turpis. Nulla commodo.
+ </p>
+
+
+
+ </div>
+ <!-- Left column end -->
+
+
+ <!-- Right column -->
+ <div class="floatRight width73">
+
+ <h1>Contact <span class="dark">Us</span></h1>
+
+ <p>
+ Nulla commodo. In nunc justo, mollis sed, gravida at, aliquam sit amet, urna. Nulla commodo. In pharetra justo eget turpis. Nulla commodo. In pharetra justo eget turpis. In nunc justo, mollis sed, gravida at, <a href="">aliquam sit</a> amet, urna.
+ </p>
+
+ <form id="contact" action="" method="post" onsubmit="return configForm(this);" style="position: relative; z-index: 1;">
+
+
+
+ <p>
+ <input type="text" name="name" value="Name" class="width75" onfocus="clearValue(this, 'Name');" onblur="fillValue(this, 'Name');"/>
+ </p>
+
+ <p>
+ <input type="text" name="email" value="E-mail" class="width75" onfocus="clearValue(this, 'E-mail');" onblur="fillValue(this, 'E-mail');"/>
+ </p>
+
+ <p>
+ <input type="text" name="subject" value="Subject" class="width75" onfocus="clearValue(this, 'Subject');" onblur="fillValue(this, 'Subject');"/>
+ </p>
+
+ <p>
+ <textarea name="message" rows="5" cols="80" onfocus="clearValue(this, 'Message');" onblur="fillValue(this, 'Message');">Message</textarea>
+ </p>
+
+ <p>
+ <input type="submit" value="SEND" class="button" />
+ <input type="reset" value="RESET" class="button" />
+ </p>
+
+ <p>&nbsp;
+
+ </p>
+
+
+
+ <h1>Example Form <span class="dark">Elements</span></h1>
+
+
+
+ <p>
+ <label>Input Heading</label>
+ <input type="text" name="input1" class="width50"/>
+ </p>
+
+ <p>
+ <label>Input Heading</label>
+ <input type="text" name="input2" class="width50"/>
+ </p>
+
+ <p>
+ <label>Textarea Heading</label>
+ <textarea name="textarea" rows="5" cols="60"></textarea>
+ </p>
+
+ <p>
+ <label>Select Heading</label>
+ <select name="select">
+ <option value="1">Value One</option>
+ <option value="2">Value Two</option>
+ </select>
+ </p>
+
+
+
+
+ <p>
+ <label>Radio Button Group</label>
+ <input type="radio" name="radioGroup" value="1" class="radio"/> One
+ <input type="radio" name="radioGroup" value="2" class="radio"/> Two
+ <input type="radio" name="radioGroup" value="3" class="radio"/> Three
+ <input type="radio" name="radioGroup" value="4" class="radio"/> Four
+ </p>
+
+
+ <p>
+ <label>Checkbox Group</label>
+ <input type="checkbox" name="checkGroup" value="1" class="radio"/> One
+ <input type="checkbox" name="checkGroup" value="2" class="radio"/> Two
+ <input type="checkbox" name="checkGroup" value="3" class="radio"/> Three
+ <input type="checkbox" name="checkGroup" value="4" class="radio"/> Four
+ </p>
+
+
+ <div class="lightBlueBg">
+
+ <h2>Different Style Checkbox Group</h2>
+ <p>
+ <input type="checkbox" name="checkGroup" value="1" class="radio"/> A Choice<br/>
+ <input type="checkbox" name="checkGroup" value="2" class="radio"/> Another Choice<br/>
+ <input type="checkbox" name="checkGroup" value="3" class="radio"/> Maybe this is a better choice<br/>
+ <input type="checkbox" name="checkGroup" value="4" class="radio"/> Other <input type="text" name="other" class="width33"/>
+ </p>
+
+ </div>
+
+
+
+ </form>
+
+
+
+
+ </div>
+ <!-- Right column end -->
+
+
+ </div>
+ <!-- #columns end -->
+
+ </div>
+ <!-- #page end -->
+
+</div>
+<!-- #content end -->
+
+
+
+
+<!-- #footer: holds the site footer (logo and links) -->
+<div id="footer">
+
+ <!-- #bg: applies the site width and footer background -->
+ <div id="bg" class="width">
+
+ <img src="images/logo.gif" alt="Your logo goes here"/>
+
+ <ul>
+ <li><a href="">Sitemap</a></li>
+ <li><a href="">Register</a></li>
+ <li><a href="http://www.opensourcetemplates.org">opensourcetemplates.org</a></li>
+ <li><a href="http://www.studio7designs.com" class="last">studio7designs.com</a></li>
+ </ul>
+
+ </div>
+ <!-- #bg end -->
+
+</div>
+<!-- #footer end -->
+
+</body>
+
+</html>
203 media/Nautica05b/css/html.css
@@ -0,0 +1,203 @@
+/**************************************************************
+ Visit studio7designs.com for more layouts and downloads for this template!
+ **************************************************************/
+
+/*********************************************************
+ HTML Elements
+ *********************************************************/
+
+html,
+body {
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ text-align: center;
+ background: url(../images/bg/light_body.gif) repeat-y top center;
+ font: 400 0.7em verdana, arial, sans-serif;
+ line-height: 170%;
+
+ color: #555;
+}
+
+
+/* Headers */
+h1, h2, h3, h4, h5, h6 {
+ margin: 0 0 10px 0;
+ padding: 0;
+}
+
+
+h1 {
+ padding-bottom: 0.2em;
+
+ font: 400 1.6em arial, sans-serif;
+ color: #536C71;
+ border-bottom: 12px solid #ddd;
+}
+
+h2 {
+ font-size: 1.2em;
+ color: #586B7A;
+}
+
+h3 {
+ text-transform: uppercase;
+ font-size: 0.9em;
+ color: #5D6F73;
+}
+
+h4 {
+ font-size: 0.85em;
+}
+
+h5 {
+ font-size: 0.8em;
+}
+
+
+/* Needed to horizontally pad in a coloured container */
+.horzPad h1,
+.horzPad h2,
+.horzPad h3,
+.horzPad h4,
+.horzPad h5,
+.horzPad p {
+ padding-left: 5px;
+ padding-right: 5px;