Skip to content
Browse files

first commit

  • Loading branch information...
0 parents commit 2aae1e2acea8ff4a556168c3dafa9d8959ef5205 @dbunskoek dbunskoek committed Apr 7, 2011
Showing with 11,402 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +17 −0 LICENSE.rst
  3. +2 −0 MANIFEST.in
  4. +281 −0 README.rst
  5. 0 fiber/__init__.py
  6. +130 −0 fiber/admin.py
  7. +13 −0 fiber/admin_urls.py
  8. +67 −0 fiber/admin_views.py
  9. 0 fiber/api/__init__.py
  10. +56 −0 fiber/api/authentication.py
  11. +41 −0 fiber/api/emitters.py
  12. +293 −0 fiber/api/handlers.py
  13. +32 −0 fiber/api/urls.py
  14. +14 −0 fiber/app_settings.py
  15. +97 −0 fiber/context_processors.py
  16. +21 −0 fiber/editor.py
  17. +13 −0 fiber/editor_definitions.py
  18. +193 −0 fiber/middleware.py
  19. +152 −0 fiber/migrations/0001_initial.py
  20. +85 −0 fiber/migrations/0002_auto__chg_field_image_image__chg_field_file_file.py
  21. +98 −0 fiber/migrations/0003_auto__chg_field_contentitem_protected__add_field_page_alias_page__chg_.py
  22. +86 −0 fiber/migrations/0004_auto__del_field_page_alias_page__add_field_page_redirect_page.py
  23. +79 −0 fiber/migrations/0005_auto__del_field_contentitem_html__add_field_contentitem_content_markup.py
  24. +83 −0 fiber/migrations/0006_urls2onefield.py
  25. +84 −0 fiber/migrations/0007_auto__del_field_page_relative_url__del_field_page_named_url.py
  26. 0 fiber/migrations/__init__.py
  27. +325 −0 fiber/models.py
  28. +5 −0 fiber/static/fiber/css/Aristo-jQuery-UI-Theme/README.rdoc
  29. +1 −0 fiber/static/fiber/css/Aristo-jQuery-UI-Theme/URL
  30. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/button_bg.png
  31. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/datepicker.gif
  32. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/icon_sprite.png
  33. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/progress_bar.gif
  34. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/slider_h_bg.gif
  35. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/slider_handles.png
  36. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/slider_v_bg.gif
  37. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/tab_bg.gif
  38. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/the_gradient.gif
  39. BIN ...atic/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_diagonals-thick_18_b81900_40x40.png
  40. BIN ...atic/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_diagonals-thick_20_666666_40x40.png
  41. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_flat_10_000000_40x100.png
  42. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_glass_100_f6f6f6_1x400.png
  43. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_glass_100_fdf5ce_1x400.png
  44. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_glass_65_ffffff_1x400.png
  45. BIN .../static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_gloss-wave_35_f6a828_500x100.png
  46. BIN ...atic/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_highlight-soft_100_eeeeee_1x100.png
  47. BIN ...tatic/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-bg_highlight-soft_75_ffe45c_1x100.png
  48. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-icons_222222_256x240.png
  49. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-icons_228ef1_256x240.png
  50. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-icons_ef8c08_256x240.png
  51. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-icons_ffd27a_256x240.png
  52. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/images/ui-icons_ffffff_256x240.png
  53. +644 −0 fiber/static/fiber/css/Aristo-jQuery-UI-Theme/css/Aristo/jquery-ui-1.8.5.custom.css
  54. +546 −0 fiber/static/fiber/css/Aristo-jQuery-UI-Theme/demo.html
  55. BIN fiber/static/fiber/css/Aristo-jQuery-UI-Theme/icons.psd
  56. +199 −0 fiber/static/fiber/css/admin-reset.css
  57. +533 −0 fiber/static/fiber/css/admin.css
  58. +27 −0 fiber/static/fiber/css/widgets.css
  59. BIN fiber/static/fiber/images/busy.gif
  60. BIN fiber/static/fiber/images/button-add.png
  61. BIN fiber/static/fiber/images/button-close.png
  62. BIN fiber/static/fiber/images/button-down.png
  63. BIN fiber/static/fiber/images/button-toggle-sidebar.gif
  64. BIN fiber/static/fiber/images/button-up.png
  65. BIN fiber/static/fiber/images/ckeditor/bg-editor-toolbar-buttons.png
  66. BIN fiber/static/fiber/images/ckeditor/icon-customlink.png
  67. BIN fiber/static/fiber/images/ckeditor/icon-filelink.png
  68. BIN fiber/static/fiber/images/ckeditor/icon-image.png
  69. BIN fiber/static/fiber/images/ckeditor/icon-imagelink.png
  70. BIN fiber/static/fiber/images/ckeditor/icon-page.png
  71. BIN fiber/static/fiber/images/ckeditor/icon-pagelink.png
  72. BIN fiber/static/fiber/images/ckeditor/icon-table.png
  73. BIN fiber/static/fiber/images/ckeditor/icon-unlink.png
  74. +2,016 −0 fiber/static/fiber/js/admin.js
  75. +24 −0 fiber/static/fiber/js/ckeditor/.htaccess
  76. +862 −0 fiber/static/fiber/js/ckeditor/CHANGES.html
  77. +92 −0 fiber/static/fiber/js/ckeditor/INSTALL.html
  78. +1,334 −0 fiber/static/fiber/js/ckeditor/LICENSE.html
  79. +6 −0 fiber/static/fiber/js/ckeditor/adapters/jquery.js
  80. +955 −0 fiber/static/fiber/js/ckeditor/ckeditor.asp
  81. +134 −0 fiber/static/fiber/js/ckeditor/ckeditor.js
  82. +211 −0 fiber/static/fiber/js/ckeditor/ckeditor.pack
  83. +29 −0 fiber/static/fiber/js/ckeditor/ckeditor.php
  84. +8 −0 fiber/static/fiber/js/ckeditor/ckeditor_basic.js
  85. +20 −0 fiber/static/fiber/js/ckeditor/ckeditor_basic_source.js
  86. +593 −0 fiber/static/fiber/js/ckeditor/ckeditor_php4.php
  87. +583 −0 fiber/static/fiber/js/ckeditor/ckeditor_php5.php
  88. +25 −0 fiber/static/fiber/js/ckeditor/ckeditor_source.js
  89. +11 −0 fiber/static/fiber/js/ckeditor/config.js
  90. +35 −0 fiber/static/fiber/js/ckeditor/contents.css
  91. BIN fiber/static/fiber/js/ckeditor/images/spacer.gif
  92. +6 −0 fiber/static/fiber/js/ckeditor/lang/_languages.js
  93. +60 −0 fiber/static/fiber/js/ckeditor/lang/_translationstatus.txt
  94. +6 −0 fiber/static/fiber/js/ckeditor/lang/af.js
  95. +6 −0 fiber/static/fiber/js/ckeditor/lang/ar.js
  96. +6 −0 fiber/static/fiber/js/ckeditor/lang/bg.js
  97. +6 −0 fiber/static/fiber/js/ckeditor/lang/bn.js
  98. +6 −0 fiber/static/fiber/js/ckeditor/lang/bs.js
  99. +6 −0 fiber/static/fiber/js/ckeditor/lang/ca.js
  100. +6 −0 fiber/static/fiber/js/ckeditor/lang/cs.js
  101. +6 −0 fiber/static/fiber/js/ckeditor/lang/cy.js
  102. +6 −0 fiber/static/fiber/js/ckeditor/lang/da.js
  103. +6 −0 fiber/static/fiber/js/ckeditor/lang/de.js
  104. +6 −0 fiber/static/fiber/js/ckeditor/lang/el.js
  105. +6 −0 fiber/static/fiber/js/ckeditor/lang/en-au.js
  106. +6 −0 fiber/static/fiber/js/ckeditor/lang/en-ca.js
  107. +6 −0 fiber/static/fiber/js/ckeditor/lang/en-gb.js
  108. +6 −0 fiber/static/fiber/js/ckeditor/lang/en.js
  109. +6 −0 fiber/static/fiber/js/ckeditor/lang/eo.js
  110. +6 −0 fiber/static/fiber/js/ckeditor/lang/es.js
  111. +6 −0 fiber/static/fiber/js/ckeditor/lang/et.js
  112. +6 −0 fiber/static/fiber/js/ckeditor/lang/eu.js
  113. +6 −0 fiber/static/fiber/js/ckeditor/lang/fa.js
  114. +6 −0 fiber/static/fiber/js/ckeditor/lang/fi.js
  115. +6 −0 fiber/static/fiber/js/ckeditor/lang/fo.js
  116. +6 −0 fiber/static/fiber/js/ckeditor/lang/fr-ca.js
  117. +6 −0 fiber/static/fiber/js/ckeditor/lang/fr.js
  118. +6 −0 fiber/static/fiber/js/ckeditor/lang/gl.js
  119. +6 −0 fiber/static/fiber/js/ckeditor/lang/gu.js
  120. +6 −0 fiber/static/fiber/js/ckeditor/lang/he.js
  121. +6 −0 fiber/static/fiber/js/ckeditor/lang/hi.js
  122. +6 −0 fiber/static/fiber/js/ckeditor/lang/hr.js
  123. +6 −0 fiber/static/fiber/js/ckeditor/lang/hu.js
Sorry, we could not display the entire diff because too many files (1,324) changed.
1 .gitignore
@@ -0,0 +1 @@
+*.pyc
17 LICENSE.rst
@@ -0,0 +1,17 @@
+=======
+LICENSE
+=======
+
+Copyright © 2011, Ride The Pony
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this software except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
2 MANIFEST.in
@@ -0,0 +1,2 @@
+recursive-include fiber/templates *
+recursive-include fiber/static *
281 README.rst
@@ -0,0 +1,281 @@
+=====
+Fiber
+=====
+
+Installation:
+=============
+
+::
+
+ $ pip install git+ssh://git@github.com/leukeleu/django-fiber.git#egg=fiber
+
+
+Requirements:
+=============
+
+::
+
+ $ pip install django-mptt==0.4.1
+ $ pip install hg+http://bitbucket.org/jespern/django-piston/#egg=django-piston
+ $ pip install django-staticfiles==0.3.4
+ $ pip install beautifulsoup==3.2.0
+ $ pip install django_compressor==0.5.3
+ $ pip install textile==2.1.4
+ $ pip install PIL==1.1.7
+
+
+Settings:
+=========
+
+settings.py
+-----------
+
+::
+
+ MIDDLEWARE_CLASSES = (
+ ...
+ 'fiber.middleware.ObfuscateEmailAddressMiddleware',
+ 'fiber.middleware.AdminPageMiddleware',
+ 'fiber.middleware.PageFallbackMiddleware',
+ ...
+ )
+
+ TEMPLATE_CONTEXT_PROCESSORS = (
+ ...
+ 'django.core.context_processors.request',
+ 'staticfiles.context_processors.static_url',
+ 'fiber.context_processors.page_info',
+ ...
+ )
+
+ INSTALLED_APPS = (
+ ...
+ 'mptt',
+ 'staticfiles',
+ 'compressor',
+ 'fiber',
+ ...
+ )
+
+ import os
+ STATIC_ROOT = os.path.join(MEDIA_ROOT, 'static')
+ STATIC_URL = MEDIA_URL + 'static/'
+ STATICFILES_MEDIA_DIRNAMES = (
+ 'static',
+ )
+
+ COMPRESS_JS_FILTERS = ()
+
+Optional settings:
+==================
+
+These settings are optional (default values are shown)::
+
+ FIBER_DEFAULT_TEMPLATE = 'base.html'
+ FIBER_EXCLUDE_URLS = (r'^admin/',)
+
+ FIBER_IMAGES_DIR = 'uploads/images'
+ FIBER_FILES_DIR = 'uploads/files'
+
+ COMPRESS = [the opposite of DEBUG]
+
+
+urls.py
+-------
+
+::
+
+ ...
+ (r'^api/v1/', include('fiber.api.urls')),
+ (r'^admin/fiber/', include('fiber.admin_urls')),
+ ...
+
+
+Post-installation:
+==================
+
+Create database tables::
+
+ $ python manage.py syncdb
+ $ python manage.py migrate fiber
+
+All static Fiber files need to be symlinked in (or copied to) your media folder.
+For Django < 1.3, execute the following admin command::
+
+ $ python manage.py build_static --link
+
+When Django 1.3 hits the stage, this will be handled by django.contrib.staticfiles::
+
+ $ python manage.py collectstatic
+
+
+Usage:
+======
+
+At the beginning of your template(s), load the Fiber template tags::
+
+ {% load fiber_tags %}
+
+Using the Fiber template tags, you can:
+
+- write out content items, that either
+
+ - have a specified name
+ - are linked to a specific location on the current page
+ - are linked to a specific location on another page
+
+- write out valid XHTML menu structures
+
+ - of pages below a named root page (this is the menu name),
+ - limited to a minimum and maximum level (depth),
+ - that mark the currently active page,
+ - optionally expanding all descendants of the currently active page,
+ - with all possible css hooks you could ever need
+
+
+Content items
+-------------
+
+You can write out content items with the 'show_content' and 'show_page_content' template tags::
+
+ {% show_content "content_item_name" %}
+ {% show_page_content "block_name" %}
+ {% show_page_content other_page "block_name" %}
+
+Examples
+........
+
+This shows content item named 'address'::
+
+ {% show_content "address" %}
+
+This shows content items that are linked to the location named 'content' on the current page::
+
+ {% show_page_content "content" %}
+
+This shows content items that are linked to the location named 'content' on another page 'other_page'::
+
+ {% show_page_content other_page "content" %}
+
+
+Menus
+-----
+
+You can write out menus with the 'show_menu' template tag::
+
+ {% show_menu "menu_name" min_level max_level ["all_descendants / all"] %}
+
+Examples
+........
+
+The examples below assume the pages are structured like this:
+
+- mainmenu
+
+ - Home
+ - About us
+
+ - Mission
+ - Our people
+
+ - Products
+
+ - Product A
+
+ - Testimonials
+ - Downloads
+
+ - Technical data sheet
+ - User manual
+
+ - Product B
+
+ - Downloads
+
+ - Product C
+
+ - Downloads
+
+ - Contact
+
+ - Newsletter
+ - Directions
+
+- generalmenu
+
+ - Disclaimer
+ - Privacy statement
+
+Main menu
+.........
+
+Show first and second level pages, below the root page named 'mainmenu'::
+
+ {% show_menu "mainmenu" 1 2 %}
+
+When the user is currently visiting the 'Home' page, this will show (current pages are bold):
+
+- **Home**
+- About us
+- Products
+- Contact
+
+When the user is currently visiting the 'Products' page, this will show:
+
+- Home
+- About us
+- **Products**
+
+ - Product A
+ - Product B
+ - Product C
+
+- Contact
+
+As you can see, the sub pages of the currently active 'Products' page are automatically expanded.
+
+When the user is currently visiting the 'Product A' page, this will show:
+
+- Home
+- About us
+- **Products**
+
+ - **Product A**
+ - Product B
+ - Product C
+
+- Contact
+
+The sub pages of the 'Product A' page are not shown, because they are outside of the specified minimum and maximum levels.
+
+Sub menu
+........
+
+Show pages from level 3 to 5, below the root page named 'mainmenu', and also show all descendants of the currently active page::
+
+ {% show_menu "mainmenu" 3 5 "all_descendants" %}
+
+When the user is currently visiting the 'Home' page, this will show an empty menu, since it cannot be determined what level 3 pages are currently active.
+
+However, when the user is currently visiting the 'Product A' page, this will show:
+
+- **Product A**
+
+ - Testimonials
+ - Downloads
+
+ - Technical data sheet
+ - User manual
+
+- Product B
+- Product C
+
+Notice that all pages below the currently active 'Product A' page are expanded because of the 'all_descendants' parameter.
+
+Sitemap
+.......
+
+Show all pages, with all pages expanded::
+
+ {% show_menu "mainmenu" 1 999 "all" %}
+ {% show_menu "generalmenu" 1 999 "all" %}
0 fiber/__init__.py
No changes.
130 fiber/admin.py
@@ -0,0 +1,130 @@
+from django import forms
+from django.contrib import admin
+
+from mptt.admin import MPTTModelAdmin
+from mptt.forms import TreeNodeChoiceField
+
+from fiber.editor import get_editor_field_name
+
+from models import Page, ContentItem, PageContentItem, Image, File
+from utils.urls import get_named_url_from_quoted_url, is_quoted_url
+
+class FiberAdminSite(admin.AdminSite):
+ pass
+
+fiber_admin_site = FiberAdminSite(name='fiber_admin')
+
+
+class PageForm(forms.ModelForm):
+ parent = TreeNodeChoiceField(queryset=Page.tree.all(), level_indicator=3*unichr(160), empty_label='---------', required=False)
+ url = forms.RegexField(label='URL', required=False, max_length=100, regex=r'^[-\w/\.\:"]+$',
+ help_text = 'Example: \'/section-1/products\' or \'products\' or \'"some_named_url"\'',
+ error_message = 'This value must contain only letters, numbers, underscores, dashes or slashes.')
+ redirect_page = TreeNodeChoiceField(queryset=Page.objects.filter(redirect_page__isnull=True), level_indicator=3*unichr(160), empty_label='---------', required=False)
+
+ class Meta:
+ model = Page
+
+ def clean_url(self):
+ if is_quoted_url(self.cleaned_data['url']) and not get_named_url_from_quoted_url(self.cleaned_data['url']):
+ raise forms.ValidationError('No reverse match found for the named url')
+ return self.cleaned_data['url']
+
+ def clean_redirect_page(self):
+ if self.cleaned_data['redirect_page']:
+ try:
+ if self.cleaned_data['url'] and is_quoted_url(self.cleaned_data['url']):
+ raise forms.ValidationError('A named url can\'t be combined with a redirect page')
+ except KeyError:
+ pass
+ return self.cleaned_data['redirect_page']
+
+
+class PageContentItemInline(admin.TabularInline):
+ model = PageContentItem
+ extra = 1
+
+
+class PageAdmin(MPTTModelAdmin):
+ form = PageForm
+ fieldsets = (
+ (None, {'fields': ('parent', 'title', 'url', 'redirect_page', 'template_name',)}),
+ ('Advanced options', {'classes': ('collapse',), 'fields': ('mark_current_regexes', 'show_in_menu', 'protected',)}),
+ )
+ inlines = (PageContentItemInline,)
+ list_display = ('title', 'url', 'redirect_page','get_absolute_url', 'move_links',)
+ list_per_page = 1000
+ search_fields = ('title', 'url', 'redirect_page')
+
+ def move_links(self, object):
+ move_up = u'\u2007'
+ move_down = u'\u2007'
+
+ # first child cannot be moved up
+ if (not object.is_first_child()):
+ move_up = u'<a href="%s/move_up">\u2191</a>' % object.pk
+
+ # last child cannot be moved down
+ if (not object.is_last_child()):
+ move_down = u'<a href="%s/move_down">\u2193</a>' % object.pk
+
+ return move_up + move_down
+
+ move_links.short_description = 'Move'
+ move_links.allow_tags = True
+
+
+admin.site.register(Page, PageAdmin)
+
+
+class FiberAdminPageAdmin(MPTTModelAdmin):
+ form = PageForm
+ fieldsets = (
+ (None, {'fields': ('title', 'url', 'redirect_page')}),
+ )
+
+ def save_model(self, request, obj, form, change):
+ if 'before_page_id' in request.POST:
+ before_page = Page.objects.get(pk=int(request.POST['before_page_id']))
+ obj.parent = before_page.parent
+ obj.insert_at(before_page, position='left', save=False)
+ elif 'below_page_id' in request.POST:
+ below_page = Page.objects.get(pk=int(request.POST['below_page_id']))
+ obj.parent = below_page
+ obj.insert_at(below_page, position='last-child', save=False)
+
+ super(FiberAdminPageAdmin, self).save_model(request, obj, form, change)
+
+
+fiber_admin_site.register(Page, FiberAdminPageAdmin)
+
+
+class ContentItemAdminForm(forms.ModelForm):
+ class Meta:
+ model = ContentItem
+
+
+class ContentItemAdmin(admin.ModelAdmin):
+ list_display = ('__unicode__',)
+ form = ContentItemAdminForm
+ fieldsets = (
+ (None, {'fields': ('name', get_editor_field_name('content_html'))}),
+ ('Advanced options', {'classes': ('collapse',), 'fields': ('protected',)}),
+ )
+
+admin.site.register(ContentItem, ContentItemAdmin)
+
+
+class FiberAdminContentItemAdmin(admin.ModelAdmin):
+ list_display = ('__unicode__',)
+ form = ContentItemAdminForm
+ fieldsets = (
+ (None, {'classes': ('hide-label',), 'fields': (get_editor_field_name('content_html'),)}),
+ )
+fiber_admin_site.register(ContentItem, FiberAdminContentItemAdmin)
+
+
+admin.site.register(Image)
+
+
+admin.site.register(File)
13 fiber/admin_urls.py
@@ -0,0 +1,13 @@
+from django.conf.urls.defaults import *
+
+from admin_views import *
+from admin import fiber_admin_site
+
+
+urlpatterns = patterns('',
+ url(r'^page/(?P<id>\d+)/move_up/$', page_move_up),
+ url(r'^page/(?P<id>\d+)/move_down/$', page_move_down),
+ url(r'^login/$', fiber_login),
+ url(r'^render_textile/$', render_textile),
+ (r'^fiber_admin/', include(fiber_admin_site.urls)),
+)
67 fiber/admin_views.py
@@ -0,0 +1,67 @@
+from django.contrib.auth import authenticate, login
+from django.http import HttpResponse, HttpResponseRedirect
+from django.contrib.admin.views.decorators import staff_member_required
+from django.utils import simplejson
+from django.utils.translation import ugettext as _
+from django.views.decorators.csrf import csrf_exempt
+
+from textile import textile
+
+from models import Page
+
+
+def fiber_login(request):
+ username = request.POST['username']
+ password = request.POST['password']
+ user = authenticate(username=username, password=password)
+
+ result = {}
+ if user is not None:
+ if user.is_active:
+ login(request, user)
+ result = {
+ 'status': 'success',
+ }
+ else:
+ result = {
+ 'status': 'inactive',
+ 'message': _('This account is inactive.'),
+ }
+ else:
+ result = {
+ 'status': 'failed',
+ 'message': _('Please enter a correct username and password. Note that both fields are case-sensitive.'),
+ }
+ json = simplejson.dumps(result)
+ return HttpResponse(json, mimetype='application/json')
+
+
+@staff_member_required
+def page_move_up(request, id):
+ page = Page.objects.get(pk=id)
+
+ if (page):
+ previous_sibling_page = page.get_previous_sibling()
+ if (previous_sibling_page):
+ page.move_to(previous_sibling_page, position='left')
+
+ return HttpResponseRedirect('../../')
+
+
+@staff_member_required
+def page_move_down(request, id):
+ page = Page.objects.get(pk=id)
+
+ if (page):
+ next_sibling_page = page.get_next_sibling()
+ if (next_sibling_page):
+ page.move_to(next_sibling_page, position='right')
+
+ return HttpResponseRedirect('../../')
+
+
+@csrf_exempt
+def render_textile(request):
+ return HttpResponse(
+ textile(request.POST['data'])
+ )
0 fiber/api/__init__.py
No changes.
56 fiber/api/authentication.py
@@ -0,0 +1,56 @@
+from django.http import HttpResponse
+from django.utils.importlib import import_module
+from django.conf import settings
+
+
+class DjangoStaffAuthentication(object):
+ """
+ Django staff authentication for Django Piston
+ """
+ def __init__(self):
+ pass
+
+ def is_authenticated(self, request):
+ """
+ This method calls the `is_authenticated` and `is_staff`
+ method of request.user.
+
+ `is_authenticated`: Will be called when checking for
+ authentication. It returns True if the user is authenticated
+ and is a staff member.
+ """
+ self.request = request
+ return request.user.is_authenticated and request.user.is_staff
+
+ def challenge(self):
+ """
+ `challenge`: In cases where `is_authenticated` returns
+ False, the result of this method will be returned.
+ This will usually be a `HttpResponse` object with
+ some kind of challenge headers and 401 code on it.
+ """
+ response = HttpResponse()
+ response.status_code = 401
+
+ return response
+
+
+def set_session(request, session_key):
+ """
+ Set the session from this session key. This is used for uploading, because Flash does not support cookies.
+ """
+ engine = import_module(settings.SESSION_ENGINE)
+ request.session = engine.SessionStore(session_key)
+
+
+class DjangoUploadAuthentication(DjangoStaffAuthentication):
+ """
+ Authentication for file uploads. Does the same as DjangoStaffAuthentication.
+
+ - Uses session id in POST for authentication instead of session cookie.
+ - The reason is that Flash uploads do not support cookies.
+ """
+ def is_authenticated(self, request):
+ set_session(request, request.POST.get('sessionid'))
+
+ return super(DjangoUploadAuthentication, self).is_authenticated(request)
41 fiber/api/emitters.py
@@ -0,0 +1,41 @@
+import math
+from django.utils import simplejson
+from django.core.serializers.json import DateTimeAwareJSONEncoder
+
+from piston.emitters import Emitter
+
+from fiber.models import File, Image
+
+
+class jqGridJSONEmitter(Emitter):
+ """
+ JSON emitter, understands timestamps, wraps result set in object literal
+ for jqGrid JS compatibility
+ """
+
+ def render(self, request):
+ callback = request.GET.get('callback')
+
+ row_data = self.construct()
+ fields = list(self.fields)
+ fields.remove('id')
+ rows = [{
+ 'id': row['id'],
+ 'cell': [row[field] for field in fields]} for row in row_data]
+
+ # todo: Is there a better way to determine this?
+ if fields[1] == 'image':
+ total = int(math.ceil(len(Image.objects.all())/50.0))
+ else:
+ total = int(math.ceil(len(File.objects.all())/50.0))
+
+ jqgrid_dict = {'page': int(request.GET['page']), 'total': total, 'records': len(rows), 'rows': rows}
+ json = simplejson.dumps(jqgrid_dict, cls=DateTimeAwareJSONEncoder, ensure_ascii=False, indent=4)
+
+ # callback
+ if callback:
+ return '%s(%s)' % (callback, json)
+
+ return json
+
+Emitter.register('jqgrid-json', jqGridJSONEmitter, 'application/json; charset=utf-8')
293 fiber/api/handlers.py
@@ -0,0 +1,293 @@
+import os
+
+from django.db.models import F, Max
+
+from piston.handler import BaseHandler
+from piston.utils import rc
+
+from fiber.utils.date import friendly_datetime
+
+from fiber.models import Page, PageContentItem, ContentItem, Image, File
+
+
+class PageHandler(BaseHandler):
+ allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
+ fields = ('data', 'children', 'show_in_menu')
+ model = Page
+
+ @classmethod
+ def data(cls, page):
+ return {
+ 'title': page.title,
+ 'attr': {
+ 'data-fiber-data': '{"type": "page", "id": %d}' % page.id,
+ 'href': page.get_absolute_url(),
+ }
+ }
+
+ @classmethod
+ def children(cls, page):
+ return page.get_children()
+
+ def read(self, request, id=None):
+ if id:
+ return self.read_page(id)
+ else:
+ return self.read_trees()
+
+ def read_trees(self):
+ return Page.objects.filter(level=0).order_by('tree_id')
+
+ def read_page(self, page_id):
+ page = Page.objects.get(id=page_id)
+
+ # Do not include the data of the child pages.
+ page.children = None
+ return page
+
+ def create(self, request):
+ """
+ Creates a new Page, either placed in the same level before or after a certain Page,
+ or as last child below a certain parent Page.
+ """
+ attrs = self.flatten_dict(request.POST)
+
+ try:
+ page_title = attrs['title']
+ page_relative_url = attrs['relative_url']
+ except KeyError:
+ return rc.BAD_REQUEST
+
+ page = Page(title=page_title, relative_url=page_relative_url)
+
+ if 'before_page_id' in attrs:
+ before_page = Page.objects.get(pk=int(attrs['before_page_id']))
+ page.parent = before_page.parent
+ page.insert_at(before_page, position='left', save=False)
+ elif 'below_page_id' in attrs:
+ below_page = Page.objects.get(pk=int(attrs['below_page_id']))
+ page.parent = below_page
+ page.insert_at(below_page, position='last-child', save=False)
+
+ page.save()
+ return rc.CREATED
+
+ def update(self, request, id):
+ data = request.data
+
+ if data.get('action') == 'move':
+ self._move(
+ int(id),
+ int(data['parent_id']),
+ int(data['left_id']),
+ )
+ else:
+ # TODO: check if this situation occurs
+ raise Exception('Unsupported action')
+
+ def delete(self, request, id):
+ page = Page.objects.get(pk=id)
+ page.delete()
+
+ return rc.DELETED
+
+ def _move(self, page_id, parent_id, left_id):
+ """
+ Moves the node. Parameters:
+ - page_id: the page to move
+ - parent_id: the new parent
+ - left_id: the node to the left (0 if it does not exist)
+ """
+ page = Page.objects.get(pk=page_id)
+ page.move_page(
+ parent_id,
+ left_id,
+ )
+
+
+class PageContentItemHandler(BaseHandler):
+ allowed_methods = ('POST', 'PUT', 'DELETE')
+ model = PageContentItem
+
+ def create(self, request):
+ """
+ Creates a new PageContentItem.
+ """
+ attrs = self.flatten_dict(request.POST)
+
+ content_item = ContentItem.objects.get(pk=int(attrs['content_item_id']))
+
+ if 'before_page_content_item_id' in attrs:
+ before_page_content_item = PageContentItem.objects.get(pk=int(attrs['before_page_content_item_id']))
+ page = Page.objects.get(pk=before_page_content_item.page.id)
+ block_name = before_page_content_item.block_name
+ sort = before_page_content_item.sort
+
+ # make room for new content item
+ PageContentItem.objects.filter(block_name=block_name).filter(sort__gte=sort).update(sort=F('sort')+1)
+ else:
+ page = Page.objects.get(pk=int(attrs['page_id']))
+ block_name = attrs['block_name']
+
+ all_page_content_items = PageContentItem.objects.filter(block_name=block_name).order_by('sort')
+ sort_max = all_page_content_items.aggregate(Max('sort'))['sort__max']
+ if sort_max != None:
+ sort = sort_max + 1
+ else:
+ sort = 0
+
+ page_content_item = PageContentItem(content_item=content_item, page=page, block_name=block_name, sort=sort)
+ page_content_item.save()
+ return rc.CREATED
+
+ def update(self, request, id):
+ page_content_item = PageContentItem.objects.get(pk=id)
+
+ data = request.data
+ if 'action' in data:
+ if data['action'] == 'move':
+ next = None
+ if 'before_page_content_item_id' in data:
+ next_id = int(data['before_page_content_item_id'])
+ if next_id:
+ next = PageContentItem.objects.get(pk=next_id)
+
+ block_name = data.get('block_name')
+
+ PageContentItem.objects.move(page_content_item, next, block_name=block_name)
+ page_content_item = PageContentItem.objects.get(pk=id)
+
+ return page_content_item
+
+ def delete(self, request, id):
+ page_content_item = PageContentItem.objects.get(pk=id)
+ page_content_item.delete()
+
+ return rc.DELETED
+
+
+class ImageHandler(BaseHandler):
+ allowed_methods = ('GET', )
+ fields = ('id', 'url', 'image', 'filename', 'size', 'updated')
+ exclude = () # un-exclude `id`
+ model = Image
+
+ @classmethod
+ def url(cls, image):
+ return image.image.url
+
+ @classmethod
+ def image(cls, image):
+ return image.image.url
+
+ @classmethod
+ def filename(cls, image):
+ return os.path.basename(image.image.name)
+
+ @classmethod
+ def size(cls, image):
+ return '%s x %d' % (image.width, image.height)
+
+ @classmethod
+ def updated(cls, image):
+ return friendly_datetime(image.updated)
+
+ def read(self, request):
+ rows = int(request.GET['rows'])
+ page = int(request.GET['page'])
+ if 'filename' in request.GET:
+ filename = request.GET['filename']
+ else:
+ filename = ''
+ limit = page*rows
+ offset = (page-1)*rows
+ order_by = request.GET['sidx']
+ order_reversed = (request.GET['sord'] == 'desc') #desc or asc
+ if order_by == 'updated':
+ order_clause = 'updated'
+ elif order_by == 'filename':
+ order_clause = 'image'
+ elif order_by == 'size':
+ order_clause = 'width'
+
+ if order_reversed:
+ order_clause = '-%s' % order_clause
+
+ images = Image.objects.filter(image__icontains=filename).order_by(order_clause)[offset:limit]
+ return images
+
+
+class FileHandler(BaseHandler):
+ allowed_methods = ('GET', )
+ fields = ('id', 'url', 'filename', 'updated')
+ exclude = () # un-exclude `id`
+ model = File
+
+ @classmethod
+ def url(cls, file):
+ return file.file.url
+
+ @classmethod
+ def filename(cls, file):
+ return os.path.basename(file.file.name)
+
+ @classmethod
+ def updated(cls, file):
+ return friendly_datetime(file.updated)
+
+ def read(self, request):
+ rows = int(request.GET['rows'])
+ page = int(request.GET['page'])
+ if 'filename' in request.GET:
+ filename = request.GET['filename']
+ else:
+ filename = ''
+ limit = page*rows
+ offset = (page-1)*rows
+ order_by = request.GET['sidx']
+ order_reversed = (request.GET['sord'] == 'desc') #desc or asc
+ if order_by == 'updated':
+ order_clause = 'updated'
+ elif order_by == 'filename':
+ order_clause = 'file'
+
+ if order_reversed:
+ order_clause = '-%s' % order_clause
+
+ files = File.objects.filter(file__icontains=filename).order_by(order_clause)[offset:limit]
+
+ return files
+
+ def create(self, request):
+ File.objects.create(
+ file=request.FILES['file'],
+ title='uploaded', # TODO: empty title
+ )
+ return rc.CREATED
+
+
+class FileUploadHandler(BaseHandler):
+ allowed_methods = ('POST',)
+
+ def create(self, request):
+ File.objects.create(
+ file=request.FILES['file'],
+ title='uploaded', # TODO: empty title
+ )
+ return rc.CREATED
+
+
+class ImageUploadHandler(BaseHandler):
+ allowed_methods = ('POST',)
+
+ def create(self, request):
+ Image.objects.create(
+ image=request.FILES['file'],
+ title='uploaded', # TODO: empty title
+ )
+ return rc.CREATED
+
+
+class ContentItemHandler(BaseHandler):
+ allowed_methods = ('DELETE',)
+ model = ContentItem
32 fiber/api/urls.py
@@ -0,0 +1,32 @@
+from django.conf.urls.defaults import *
+
+from piston.resource import Resource
+
+from authentication import DjangoStaffAuthentication, DjangoUploadAuthentication
+from handlers import PageHandler, PageContentItemHandler, ImageHandler, FileHandler, FileUploadHandler, ImageUploadHandler, ContentItemHandler
+from emitters import jqGridJSONEmitter
+
+auth = DjangoStaffAuthentication()
+upload_auth = DjangoUploadAuthentication()
+
+page_handler = Resource(PageHandler, authentication=auth)
+page_content_item_handler = Resource(PageContentItemHandler, authentication=auth)
+image_handler = Resource(ImageHandler, authentication=auth)
+file_handler = Resource(FileHandler, authentication=auth)
+file_upload_handler = Resource(FileUploadHandler, authentication=upload_auth)
+image_upload_handler = Resource(ImageUploadHandler, authentication=upload_auth)
+content_item_handler = Resource(ContentItemHandler, authentication=auth)
+
+urlpatterns = patterns('',
+ url(r'^pages/$', page_handler),
+ url(r'^pages\.(?P<emitter_format>.+)$', page_handler),
+ url(r'^page/(?P<id>\d+)/$', page_handler),
+ url(r'^page_content_items/$', page_content_item_handler),
+ url(r'^page_content_items\.(?P<emitter_format>.+)$', page_content_item_handler),
+ url(r'^page_content_item/(?P<id>\d+)/$', page_content_item_handler),
+ url(r'^images\.(?P<emitter_format>.+)$', image_handler),
+ url(r'^files\.(?P<emitter_format>.+)$', file_handler),
+ url(r'^files/$', file_upload_handler),
+ url(r'^images/$', image_upload_handler),
+ url(r'^content_item/(?P<id>\d+)/$', content_item_handler),
+)
14 fiber/app_settings.py
@@ -0,0 +1,14 @@
+from django.conf import settings
+
+
+DEFAULT_TEMPLATE = getattr(settings, 'FIBER_DEFAULT_TEMPLATE', 'base.html')
+EXCLUDE_URLS = getattr(settings, 'FIBER_EXCLUDE_URLS', [])
+
+FILES_DIR = getattr(settings, 'FIBER_FILES_DIR', 'uploads/files')
+IMAGES_DIR = getattr(settings, 'FIBER_IMAGES_DIR', 'uploads/images')
+
+# MPTT_ADMIN_LEVEL_INDENT defaults to 30
+if not hasattr(settings, 'MPTT_ADMIN_LEVEL_INDENT'):
+ settings.MPTT_ADMIN_LEVEL_INDENT = 30
+
+EDITOR = getattr(settings, 'FIBER_EDITOR', 'fiber.editor_definitions.CKEditor')
97 fiber/context_processors.py
@@ -0,0 +1,97 @@
+import re
+
+from models import Page
+from utils.urls import get_named_url_from_quoted_url, is_quoted_url
+
+
+def page_info(request):
+ context = {}
+ page = None
+ current_pages = []
+
+ url = request.path_info
+
+ """
+ Find Page that matches the requested URL.
+
+ First check if there is a Page whose `url` matches the requested URL.
+ """
+ try:
+ page = Page.objects.get(url__exact=url)
+ except Page.DoesNotExist:
+ pass
+
+ """
+ If no Page has been found, check a subset of Pages (whose `url` or
+ `relative_url` contain the rightmost part of the requested URL), to see
+ if their `get_absolute_url()` matches the requested URL entirely.
+ """
+ if not page:
+ last_url_part = url.rstrip('/').rsplit('/', 1)[-1]
+ if last_url_part:
+ page_candidates = Page.objects.exclude(url__exact='', ) \
+ .filter(url__icontains=last_url_part)
+ if page_candidates:
+ for page_candidate in page_candidates:
+ if page_candidate.get_absolute_url() == url:
+ page = page_candidate
+ break
+
+ """
+ If no Page has been found, try to find a Page by matching the
+ requested URL with reversed `named_url`s.
+ """
+ if not page:
+ page_candidates = Page.objects.exclude(url__exact='')
+ if page_candidates:
+ for page_candidate in page_candidates:
+ if is_quoted_url(page_candidate.url):
+ if get_named_url_from_quoted_url(page_candidate.url) == url:
+ page = page_candidate
+ break
+
+ """
+ Block access to pages that the current user isn't supposed to see.
+ """
+ if request.user.is_authenticated():
+ if page:
+ if page not in Page.objects.visible_pages_for_user(request.user):
+ page = None
+
+ """
+ Find pages that should be marked as current in menus.
+ """
+ if page:
+ """
+ The current page should be marked as current, obviously,
+ as well as all its ancestors.
+ """
+ current_pages.append(page)
+ current_pages.extend(page.get_ancestors())
+
+ """
+ For all pages that are not already current_pages,
+ check if one of the `mark_current_regexes` matches the requested URL.
+ If so, add the page and all its ancestors to the current_pages list.
+ """
+ current_page_candidates = Page.objects.exclude(mark_current_regexes__exact='')
+ for current_page_candidate in list(set(current_page_candidates) - set(current_pages)):
+ for mark_current_regex in current_page_candidate.mark_current_regexes.strip().splitlines():
+ if re.match(mark_current_regex, url):
+ current_pages.append(current_page_candidate)
+ current_pages.extend(current_page_candidate.get_ancestors())
+ break
+
+ """
+ Order current_pages for use with tree_info template tag,
+ and remove the root node in the process.
+ """
+ current_pages = sorted(current_pages, key=lambda current_page: current_page.lft)[1:]
+
+ if page:
+ context['fiber_page'] = page
+
+ if current_pages:
+ context['fiber_current_pages'] = current_pages
+
+ return context
21 fiber/editor.py
@@ -0,0 +1,21 @@
+from fiber.utils.import_util import import_element
+from fiber.app_settings import EDITOR
+
+
+editor = import_element(EDITOR)
+renderer = editor.get('renderer', None)
+
+
+def get_editor_field_name(html_field_name):
+ """
+ Returns markup or html field name, depending on editor.
+ Input is html field_name:
+
+ get_editor_field_name('content_html')
+
+ returns: 'content_html' or 'content_markup'
+ """
+ if renderer:
+ return html_field_name.replace('_html', '_markup')
+ else:
+ return html_field_name
13 fiber/editor_definitions.py
@@ -0,0 +1,13 @@
+from textile import textile
+
+
+CKEditor = {
+ 'template_js': 'fiber/ckeditor_js.html'
+}
+
+Markitup = {
+ 'template_js': 'fiber/markitup_js.html',
+ 'template_css': 'fiber/markitup_css.html',
+ 'renderer': textile,
+ 'rename_url_expressions': (r':%s', r':%s')
+}
193 fiber/middleware.py
@@ -0,0 +1,193 @@
+import re
+import random
+
+from django.conf import settings
+from django.http import Http404, HttpResponseRedirect
+from django.template import loader, RequestContext
+from django.utils.encoding import smart_unicode
+from django.utils import simplejson
+
+from utils.import_util import import_element
+
+from app_settings import EXCLUDE_URLS, EDITOR
+from views import page
+from models import Page, ContentItem
+
+
+class PageFallbackMiddleware(object):
+
+ def process_response(self, request, response):
+ if response.status_code != 404:
+ return response # No need to check for a page for non-404 responses.
+ try:
+ return page(request, request.path_info)
+ # Return the original response if any errors happened. Because this
+ # is a middleware, we can't assume the errors will be caught elsewhere.
+ except Http404:
+ return response
+ except:
+ if settings.DEBUG:
+ raise
+ return response
+
+
+class AdminPageMiddleware(object):
+ body_re = re.compile(
+ r"<head>(?P<IN_HEAD>.*)</head>(?P<AFTER_HEAD>.*)<body(?P<IN_BODY_TAG>.*?)>(?P<BODY_CONTENTS>.*)</body>",
+ re.DOTALL,
+ )
+
+ def __init__(self):
+ self.editor_settings = self.get_editor_settings()
+
+ def process_response(self, request, response):
+ if self.set_login_session(request, response):
+ request.session['show_fiber_admin'] = True
+ url_without_fiber = request.path.replace('@fiber', '')
+ querystring_without_fiber = request.META['QUERY_STRING'].replace('@fiber', '')
+ if(querystring_without_fiber != ''):
+ querystring = '?%s' % querystring_without_fiber
+ else:
+ querystring = ''
+
+ return HttpResponseRedirect('%s%s' % (url_without_fiber, querystring))
+ else:
+ fiber_data = {}
+ admin_html = ''
+
+ is_login = self.show_login(request, response)
+ if is_login or self.show_admin(request, response):
+ if is_login:
+ # Only show the login window once
+ request.session['show_fiber_admin'] = False
+
+ fiber_data['show_login'] = True
+ else:
+ if self.is_django_admin(request):
+ fiber_data['backend'] = True
+ else:
+ t = loader.get_template('fiber/admin.html')
+ c = RequestContext(request, {
+ 'menus': Page.tree.root_nodes(),
+ 'content_groups': ContentItem.objects.get_content_groups(),
+ 'logout_url': self.get_logout_url(request),
+ })
+ admin_html = t.render(c)
+
+ fiber_data['frontend'] = True
+ if 'fiber_page' in c:
+ fiber_data['page_id'] = c['fiber_page'].id
+
+ # Inject admin html in body.
+ # Inject header html in head.
+ # Add fiber-data attribute to body tag.
+ response.content = self.body_re.sub(
+ r"<head>\g<IN_HEAD>%s</head>\g<AFTER_HEAD><body data-fiber-data='%s'\g<IN_BODY_TAG>>\g<BODY_CONTENTS>%s</body>" % (
+ self.get_header_html(request),
+ simplejson.dumps(fiber_data),
+ admin_html,
+ ),
+ smart_unicode(response.content)
+ )
+ return response
+
+ def set_login_session(self, request, response):
+ """
+ Only set the fiber show_login session when the request
+ - has @fiber behind its request-url
+ """
+ if response['Content-Type'].split(';')[0] not in ('text/html', 'application/xhtml+xml'):
+ return False
+ if not (request.path.endswith('@fiber') or request.META['QUERY_STRING'].endswith('@fiber')):
+ return False
+ else:
+ return True
+
+ def show_login(self, request, response):
+ """
+ Only show the Fiber login interface when the request
+ - is not performed by an admin user
+ - has session key show_fiber_admin = True
+ - has a response which is either 'text/html' or 'application/xhtml+xml'
+ """
+ if response['Content-Type'].split(';')[0] not in ('text/html', 'application/xhtml+xml'):
+ return False
+ try:
+ if request.user.is_staff:
+ return False
+ except AttributeError:
+ pass
+ try:
+ if not request.session['show_fiber_admin'] == True:
+ return False
+ except AttributeError:
+ return False
+ except KeyError:
+ return False
+ else:
+ return True
+
+ def show_admin(self, request, response):
+ """
+ Only show the Fiber admin interface when the request
+ - has a response status code of 200
+ - is performed by an admin user
+ - has a response which is either 'text/html' or 'application/xhtml+xml'
+ - is not an AJAX request
+ - does not match EXCLUDE_URLS (empty by default)
+ """
+ if response.status_code != 200:
+ return False
+ if not hasattr(request, 'user'):
+ return False
+ if not request.user.is_staff:
+ return False
+ if response['Content-Type'].split(';')[0] not in ('text/html', 'application/xhtml+xml'):
+ return False
+ if request.is_ajax():
+ return False
+ if EXCLUDE_URLS:
+ for exclude_url in EXCLUDE_URLS:
+ if re.search(exclude_url, request.path.lstrip('/')):
+ return False
+ return True
+
+ def is_django_admin(self, request):
+ return re.search(r'^admin/', request.path.lstrip('/'))
+
+ def get_header_html(self, request):
+ t = loader.get_template('fiber/header.html')
+ c = RequestContext(
+ request,
+ {
+ 'editor_template_js': self.editor_settings.get('template_js'),
+ 'editor_template_css': self.editor_settings.get('template_css')
+ }
+ )
+ return t.render(c)
+
+ def get_logout_url(self, request):
+ if request.META['QUERY_STRING']:
+ return '/admin/logout/?next=%s?%s' % (request.path, request.META['QUERY_STRING'])
+ else:
+ return '/admin/logout/?next=%s' % request.path
+
+ def get_editor_settings(self):
+ return import_element(EDITOR)
+
+
+class ObfuscateEmailAddressMiddleware(object):
+
+ def process_response(self, request, response):
+ # http://www.lampdocs.com/blog/2008/10/regular-expression-to-extract-all-e-mail-addresses-from-a-file-with-php/
+ email_pattern = re.compile(r'(mailto:)?[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))')
+ if response['Content-Type'].split(';')[0] in ('text/html', 'application/xhtml+xml'):
+ response.content = email_pattern.sub(self.encode_string_repl, response.content)
+ return response
+
+ def encode_string_repl(self, email_pattern_match):
+ encoded_char_list = []
+ for char in email_pattern_match.group(0):
+ encoded_char_list.append(random.choice(['&#%d;' % ord(char), '&#x%x;' % ord(char)]))
+
+ return ''.join(encoded_char_list)
152 fiber/migrations/0001_initial.py
@@ -0,0 +1,152 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'ContentItem'
+ db.create_table('fiber_contentitem', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+ ('html', self.gf('django.db.models.fields.TextField')()),
+ ('protected', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ))
+ db.send_create_signal('fiber', ['ContentItem'])
+
+ # Adding model 'Page'
+ db.create_table('fiber_page', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+ ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='subpages', null=True, to=orm['fiber.Page'])),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+ ('url', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+ ('relative_url', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+ ('named_url', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+ ('mark_current_regexes', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('template_name', self.gf('django.db.models.fields.CharField')(max_length=70, blank=True)),
+ ('show_in_menu', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('protected', 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('fiber', ['Page'])
+
+ # Adding model 'PageContentItem'
+ db.create_table('fiber_pagecontentitem', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('content_item', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['fiber.ContentItem'])),
+ ('page', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['fiber.Page'])),
+ ('block_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('sort', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('fiber', ['PageContentItem'])
+
+ # Adding model 'Image'
+ db.create_table('fiber_image', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+ ('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('width', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('height', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('fiber', ['Image'])
+
+ # Adding model 'File'
+ db.create_table('fiber_file', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+ ('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('fiber', ['File'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'ContentItem'
+ db.delete_table('fiber_contentitem')
+
+ # Deleting model 'Page'
+ db.delete_table('fiber_page')
+
+ # Deleting model 'PageContentItem'
+ db.delete_table('fiber_pagecontentitem')
+
+ # Deleting model 'Image'
+ db.delete_table('fiber_image')
+
+ # Deleting model 'File'
+ db.delete_table('fiber_file')
+
+
+ models = {
+ 'fiber.contentitem': {
+ 'Meta': {'object_name': 'ContentItem'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'html': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.file': {
+ 'Meta': {'ordering': "('file',)", 'object_name': 'File'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.image': {
+ 'Meta': {'ordering': "('image',)", 'object_name': 'Image'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'fiber.page': {
+ 'Meta': {'ordering': "('tree_id', 'lft')", 'object_name': 'Page'},
+ 'content_items': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['fiber.ContentItem']", 'through': "orm['fiber.PageContentItem']", 'symmetrical': 'False'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'mark_current_regexes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'named_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subpages'", 'null': 'True', 'to': "orm['fiber.Page']"}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'relative_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'show_in_menu': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'template_name': ('django.db.models.fields.CharField', [], {'max_length': '70', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ 'fiber.pagecontentitem': {
+ 'Meta': {'object_name': 'PageContentItem'},
+ 'block_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'content_item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['fiber.ContentItem']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['fiber.Page']"}),
+ 'sort': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['fiber']
85 fiber/migrations/0002_auto__chg_field_image_image__chg_field_file_file.py
@@ -0,0 +1,85 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'Image.image'
+ db.alter_column('fiber_image', 'image', self.gf('django.db.models.fields.files.ImageField')(max_length=255))
+
+ # Changing field 'File.file'
+ db.alter_column('fiber_file', 'file', self.gf('django.db.models.fields.files.FileField')(max_length=255))
+
+
+ def backwards(self, orm):
+
+ # Changing field 'Image.image'
+ db.alter_column('fiber_image', 'image', self.gf('django.db.models.fields.files.ImageField')(max_length=100))
+
+ # Changing field 'File.file'
+ db.alter_column('fiber_file', 'file', self.gf('django.db.models.fields.files.FileField')(max_length=100))
+
+
+ models = {
+ 'fiber.contentitem': {
+ 'Meta': {'object_name': 'ContentItem'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'html': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.file': {
+ 'Meta': {'ordering': "('file',)", 'object_name': 'File'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.image': {
+ 'Meta': {'ordering': "('image',)", 'object_name': 'Image'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'fiber.page': {
+ 'Meta': {'ordering': "('tree_id', 'lft')", 'object_name': 'Page'},
+ 'content_items': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['fiber.ContentItem']", 'through': "orm['fiber.PageContentItem']", 'symmetrical': 'False'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'mark_current_regexes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'named_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subpages'", 'null': 'True', 'to': "orm['fiber.Page']"}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'relative_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'show_in_menu': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'template_name': ('django.db.models.fields.CharField', [], {'max_length': '70', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ 'fiber.pagecontentitem': {
+ 'Meta': {'object_name': 'PageContentItem'},
+ 'block_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'content_item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['fiber.ContentItem']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['fiber.Page']"}),
+ 'sort': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['fiber']
98 ...migrations/0003_auto__chg_field_contentitem_protected__add_field_page_alias_page__chg_.py
@@ -0,0 +1,98 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'ContentItem.protected'
+ db.alter_column('fiber_contentitem', 'protected', self.gf('django.db.models.fields.BooleanField')(blank=True))
+
+ # Adding field 'Page.alias_page'
+ db.add_column('fiber_page', 'alias_page', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='aliasing_pages', null=True, to=orm['fiber.Page']), keep_default=False)
+
+ # Changing field 'Page.show_in_menu'
+ db.alter_column('fiber_page', 'show_in_menu', self.gf('django.db.models.fields.BooleanField')(blank=True))
+
+ # Changing field 'Page.protected'
+ db.alter_column('fiber_page', 'protected', self.gf('django.db.models.fields.BooleanField')(blank=True))
+
+
+ def backwards(self, orm):
+
+ # Changing field 'ContentItem.protected'
+ db.alter_column('fiber_contentitem', 'protected', self.gf('django.db.models.fields.BooleanField')())
+
+ # Deleting field 'Page.alias_page'
+ db.delete_column('fiber_page', 'alias_page_id')
+
+ # Changing field 'Page.show_in_menu'
+ db.alter_column('fiber_page', 'show_in_menu', self.gf('django.db.models.fields.BooleanField')())
+
+ # Changing field 'Page.protected'
+ db.alter_column('fiber_page', 'protected', self.gf('django.db.models.fields.BooleanField')())
+
+
+ models = {
+ 'fiber.contentitem': {
+ 'Meta': {'object_name': 'ContentItem'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'html': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.file': {
+ 'Meta': {'object_name': 'File'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.image': {
+ 'Meta': {'object_name': 'Image'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'fiber.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'alias_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'aliasing_pages'", 'null': 'True', 'to': "orm['fiber.Page']"}),
+ 'content_items': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['fiber.ContentItem']", 'through': "orm['fiber.PageContentItem']", 'symmetrical': 'False'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'mark_current_regexes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'named_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subpages'", 'null': 'True', 'to': "orm['fiber.Page']"}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'relative_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'show_in_menu': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'template_name': ('django.db.models.fields.CharField', [], {'max_length': '70', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ 'fiber.pagecontentitem': {
+ 'Meta': {'object_name': 'PageContentItem'},
+ 'block_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'content_item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['fiber.ContentItem']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'page_content_items'", 'to': "orm['fiber.Page']"}),
+ 'sort': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['fiber']
86 fiber/migrations/0004_auto__del_field_page_alias_page__add_field_page_redirect_page.py
@@ -0,0 +1,86 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Deleting field 'Page.alias_page'
+ db.delete_column('fiber_page', 'alias_page_id')
+
+ # Adding field 'Page.redirect_page'
+ db.add_column('fiber_page', 'redirect_page', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='redirected_pages', null=True, to=orm['fiber.Page']), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Adding field 'Page.alias_page'
+ db.add_column('fiber_page', 'alias_page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='aliasing_pages', null=True, to=orm['fiber.Page'], blank=True), keep_default=False)
+
+ # Deleting field 'Page.redirect_page'
+ db.delete_column('fiber_page', 'redirect_page_id')
+
+
+ models = {
+ 'fiber.contentitem': {
+ 'Meta': {'object_name': 'ContentItem'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'html': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.file': {
+ 'Meta': {'object_name': 'File'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.image': {
+ 'Meta': {'object_name': 'Image'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'fiber.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'content_items': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['fiber.ContentItem']", 'through': "orm['fiber.PageContentItem']", 'symmetrical': 'False'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'mark_current_regexes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'named_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subpages'", 'null': 'True', 'to': "orm['fiber.Page']"}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'redirect_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'redirected_pages'", 'null': 'True', 'to': "orm['fiber.Page']"}),
+ 'relative_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'show_in_menu': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'template_name': ('django.db.models.fields.CharField', [], {'max_length': '70', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ 'fiber.pagecontentitem': {
+ 'Meta': {'object_name': 'PageContentItem'},
+ 'block_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'content_item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['fiber.ContentItem']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'page_content_items'", 'to': "orm['fiber.Page']"}),
+ 'sort': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['fiber']
79 ...migrations/0005_auto__del_field_contentitem_html__add_field_contentitem_content_markup.py
@@ -0,0 +1,79 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.add_column('fiber_contentitem', 'content_markup', self.gf('fiber.utils.fields.FiberMarkupField')(default='-'), keep_default=False)
+ db.rename_column('fiber_contentitem', 'html', 'content_html')
+
+
+ def backwards(self, orm):
+ db.rename_column('fiber_contentitem', 'content_html', 'html')
+ db.delete_column('fiber_contentitem', 'content_markup')
+
+
+ models = {
+ 'fiber.contentitem': {
+ 'Meta': {'object_name': 'ContentItem'},
+ 'content_html': ('fiber.utils.fields.FiberHTMLField', [], {}),
+ 'content_markup': ('fiber.utils.fields.FiberMarkupField', [], {}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.file': {
+ 'Meta': {'ordering': "('file',)", 'object_name': 'File'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'fiber.image': {
+ 'Meta': {'ordering': "('image',)", 'object_name': 'Image'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'fiber.page': {
+ 'Meta': {'ordering': "('tree_id', 'lft')", 'object_name': 'Page'},
+ 'content_items': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['fiber.ContentItem']", 'through': "orm['fiber.PageContentItem']", 'symmetrical': 'False'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'mark_current_regexes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'named_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subpages'", 'null': 'True', 'to': "orm['fiber.Page']"}),
+ 'protected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'redirect_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'redirected_pages'", 'null': 'True', 'to': "orm['fiber.Page']"}),
+ 'relative_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'show_in_menu': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'template_name': ('django.db.models.fields.CharField', [], {'max_length': '70', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ 'fiber.pagecontentitem': {
+ 'Meta': {'object_name': 'PageContentItem'},
+ 'block_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'content_item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['fiber.ContentItem']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'page_content_items'", 'to': "orm['fiber.Page']"}),
+ 'sort': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['fiber']
83 fiber/migrations/0006_urls2onefield.py
@@ -0,0 +1,83 @@
+# encoding: 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 page in orm.Page.objects.all():
+ if page.url == '' and page.relative_url != '':
+ #absolute url is not given, relative url is
+ #hence migrate the relative url tot the url field
+ page.url = page.relative_url
+ page.save()
+ elif page.url == '' and page.named_url != '':
+ page.url = '"%s"' % page.named_url
+ page.save()
+
+ def backwards(self, orm):
+ raise RuntimeError("Cannot reverse this migration.")
+
+
+ models = {
+ 'fiber.contentitem': {
+ 'Meta': {'object_name': 'ContentItem'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank'