Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial Commit to github and start of open source distibution. This i…

…s not perfect!

Big changes to be less dependent of the settings.py
Major hack in Admin.py and the admin templates to make it work as a package.
  • Loading branch information...
commit 7b87020022dcbc5359d3d2f31d8998eac422adf0 1 parent 6464acf
@belug23 belug23 authored
Showing with 7,965 additions and 3 deletions.
  1. +14 −0 CHANGELOG
  2. +7 −0 MANIFEST.in
  3. +7 −3 README.md
  4. 0  geoprisma_config/__init__.py
  5. +321 −0 geoprisma_config/admin.py
  6. 0  geoprisma_config/conf/__init__.py
  7. +81 −0 geoprisma_config/conf/types.py
  8. +4 −0 geoprisma_config/context_processors.py
  9. +133 −0 geoprisma_config/forms.py
  10. 0  geoprisma_config/management/__init__.py
  11. 0  geoprisma_config/management/commands/__init__.py
  12. +689 −0 geoprisma_config/management/commands/import.py
  13. +269 −0 geoprisma_config/models.py
  14. +827 −0 geoprisma_config/static/admin/gc_css/base.css
  15. +252 −0 geoprisma_config/static/admin/gc_css/changelists.css
  16. +24 −0 geoprisma_config/static/admin/gc_css/dashboard.css
  17. +325 −0 geoprisma_config/static/admin/gc_css/forms.css
  18. +51 −0 geoprisma_config/static/admin/gc_css/ie.css
  19. +54 −0 geoprisma_config/static/admin/gc_css/login.css
  20. +206 −0 geoprisma_config/static/admin/gc_css/rtl.css
  21. +506 −0 geoprisma_config/static/admin/gc_css/widgets.css
  22. BIN  geoprisma_config/static/admin/gc_img/admin/arrow-down.gif
  23. BIN  geoprisma_config/static/admin/gc_img/admin/arrow-up.gif
  24. BIN  geoprisma_config/static/admin/gc_img/admin/changelist-bg.gif
  25. BIN  geoprisma_config/static/admin/gc_img/admin/changelist-bg_rtl.gif
  26. BIN  geoprisma_config/static/admin/gc_img/admin/chooser-bg.gif
  27. BIN  geoprisma_config/static/admin/gc_img/admin/chooser_stacked-bg.gif
  28. BIN  geoprisma_config/static/admin/gc_img/admin/default-bg-reverse.gif
  29. BIN  geoprisma_config/static/admin/gc_img/admin/default-bg.gif
  30. BIN  geoprisma_config/static/admin/gc_img/admin/deleted-overlay.gif
  31. BIN  geoprisma_config/static/admin/gc_img/admin/icon-no.gif
  32. BIN  geoprisma_config/static/admin/gc_img/admin/icon-unknown.gif
  33. BIN  geoprisma_config/static/admin/gc_img/admin/icon-yes.gif
  34. BIN  geoprisma_config/static/admin/gc_img/admin/icon_addlink.gif
  35. BIN  geoprisma_config/static/admin/gc_img/admin/icon_alert.gif
  36. BIN  geoprisma_config/static/admin/gc_img/admin/icon_calendar.gif
  37. BIN  geoprisma_config/static/admin/gc_img/admin/icon_changelink.gif
  38. BIN  geoprisma_config/static/admin/gc_img/admin/icon_clock.gif
  39. BIN  geoprisma_config/static/admin/gc_img/admin/icon_deletelink.gif
  40. BIN  geoprisma_config/static/admin/gc_img/admin/icon_error.gif
  41. BIN  geoprisma_config/static/admin/gc_img/admin/icon_searchbox.png
  42. BIN  geoprisma_config/static/admin/gc_img/admin/icon_success.gif
  43. BIN  geoprisma_config/static/admin/gc_img/admin/inline-delete-8bit.png
  44. BIN  geoprisma_config/static/admin/gc_img/admin/inline-delete.png
  45. BIN  geoprisma_config/static/admin/gc_img/admin/inline-restore-8bit.png
  46. BIN  geoprisma_config/static/admin/gc_img/admin/inline-restore.png
  47. BIN  geoprisma_config/static/admin/gc_img/admin/inline-splitter-bg.gif
  48. BIN  geoprisma_config/static/admin/gc_img/admin/nav-bg-grabber.gif
  49. BIN  geoprisma_config/static/admin/gc_img/admin/nav-bg-reverse.gif
  50. BIN  geoprisma_config/static/admin/gc_img/admin/nav-bg.gif
  51. BIN  geoprisma_config/static/admin/gc_img/admin/selector-add.gif
  52. BIN  geoprisma_config/static/admin/gc_img/admin/selector-addall.gif
  53. BIN  geoprisma_config/static/admin/gc_img/admin/selector-remove.gif
  54. BIN  geoprisma_config/static/admin/gc_img/admin/selector-removeall.gif
  55. BIN  geoprisma_config/static/admin/gc_img/admin/selector-search.gif
  56. BIN  geoprisma_config/static/admin/gc_img/admin/selector_stacked-add.gif
  57. BIN  geoprisma_config/static/admin/gc_img/admin/selector_stacked-remove.gif
  58. BIN  geoprisma_config/static/admin/gc_img/admin/tool-left.gif
  59. BIN  geoprisma_config/static/admin/gc_img/admin/tool-left_over.gif
  60. BIN  geoprisma_config/static/admin/gc_img/admin/tool-right.gif
  61. BIN  geoprisma_config/static/admin/gc_img/admin/tool-right_over.gif
  62. BIN  geoprisma_config/static/admin/gc_img/admin/tooltag-add.gif
  63. BIN  geoprisma_config/static/admin/gc_img/admin/tooltag-add_over.gif
  64. BIN  geoprisma_config/static/admin/gc_img/admin/tooltag-arrowright.gif
  65. BIN  geoprisma_config/static/admin/gc_img/admin/tooltag-arrowright_over.gif
  66. BIN  geoprisma_config/static/admin/gc_img/gis/move_vertex_off.png
  67. BIN  geoprisma_config/static/admin/gc_img/gis/move_vertex_on.png
  68. +111 −0 geoprisma_config/static/admin/gc_js/SelectBox.js
  69. +113 −0 geoprisma_config/static/admin/gc_js/SelectFilter2.js
  70. +39 −0 geoprisma_config/static/admin/gc_js/actions.js
  71. +85 −0 geoprisma_config/static/admin/gc_js/admin/CollapsedFieldsets.js
  72. +255 −0 geoprisma_config/static/admin/gc_js/admin/DateTimeShortcuts.js
  73. +96 −0 geoprisma_config/static/admin/gc_js/admin/RelatedObjectLookups.js
  74. +137 −0 geoprisma_config/static/admin/gc_js/admin/ordering.js
  75. +143 −0 geoprisma_config/static/admin/gc_js/calendar.js
  76. +176 −0 geoprisma_config/static/admin/gc_js/core.js
  77. +233 −0 geoprisma_config/static/admin/gc_js/dateparse.js
  78. +167 −0 geoprisma_config/static/admin/gc_js/getElementsBySelector.js
  79. +94 −0 geoprisma_config/static/admin/gc_js/timeparse.js
  80. +140 −0 geoprisma_config/static/admin/gc_js/urlify.js
  81. +6 −0 geoprisma_config/static/gc_js/admin-inline-fix.js
  82. +86 −0 geoprisma_config/static/gc_js/admin-list-reorder.js
  83. +67 −0 geoprisma_config/static/gc_js/admin-widgets.js
  84. +154 −0 geoprisma_config/static/gc_js/jquery-1.4.2.min.js
  85. +1,012 −0 geoprisma_config/static/gc_js/jquery-ui-1.8.2.custom.min.js
  86. +12 −0 geoprisma_config/templates/admin/404.html
  87. +12 −0 geoprisma_config/templates/admin/500.html
  88. +5 −0 geoprisma_config/templates/admin/actions.html
  89. +15 −0 geoprisma_config/templates/admin/app_index.html
  90. +101 −0 geoprisma_config/templates/admin/base.html
  91. +33 −0 geoprisma_config/templates/admin/base2.html
  92. +10 −0 geoprisma_config/templates/admin/base_site.html
  93. +124 −0 geoprisma_config/templates/admin/change.html
  94. +66 −0 geoprisma_config/templates/admin/change_form.html
  95. +86 −0 geoprisma_config/templates/admin/change_list.html
  96. +17 −0 geoprisma_config/templates/admin/change_list_results.html
  97. +10 −0 geoprisma_config/templates/admin/date_hierarchy.html
  98. +32 −0 geoprisma_config/templates/admin/delete_confirmation.html
  99. +40 −0 geoprisma_config/templates/admin/delete_selected_confirmation.html
  100. +28 −0 geoprisma_config/templates/admin/edit_inline/stacked.html
  101. +71 −0 geoprisma_config/templates/admin/edit_inline/tabular.html
  102. +8 −0 geoprisma_config/templates/admin/filter.html
  103. +19 −0 geoprisma_config/templates/admin/includes/fieldset.html
  104. +80 −0 geoprisma_config/templates/admin/index.html
  105. +8 −0 geoprisma_config/templates/admin/invalid_setup.html
  106. +35 −0 geoprisma_config/templates/admin/login.html
  107. +42 −0 geoprisma_config/templates/admin/object_history.html
  108. +12 −0 geoprisma_config/templates/admin/pagination.html
  109. +11 −0 geoprisma_config/templates/admin/prepopulated_fields_js.html
  110. +18 −0 geoprisma_config/templates/admin/search_form.html
  111. +11 −0 geoprisma_config/templates/admin/submit_line.html
  112. +32 −0 geoprisma_config/templates/admin/template_validator.html
  113. +23 −0 geoprisma_config/tests.py
  114. +40 −0 geoprisma_config/urls.py
  115. +5 −0 geoprisma_config/utils.py
  116. +36 −0 geoprisma_config/views.py
  117. +6 −0 geoprisma_config/widgets.py
  118. +33 −0 setup.py
View
14 CHANGELOG
@@ -0,0 +1,14 @@
+0.0.1
+=====
+
+Initial Commit to github and start of open source distibution. This is not perfect!
+Big changes to be less dependent of the settings.py
+Major hack in Admin.py and the admin templates to make it work as a package.
+The Admin part of the app is to be redone.
+It needs to be tester with django 1.4 and 1.5
+
+
+0.0.0
+=====
+
+The project was create by Alan Boudreault - Mapgears inc. for Solution Globale Informatique and Nippour on the Géoctopus project.
View
7 MANIFEST.in
@@ -0,0 +1,7 @@
+include README.md
+include LICENSE
+include CHANGELOG
+recursive-include geoprisma_config/management *
+recursive-include geoprisma_config/static *
+recursive-include geoprisma_config/templates *
+recursive-include geoprisma_config/conf *
View
10 README.md
@@ -12,8 +12,6 @@ Contrib Admin is required.
1. Add "geoprisma_config" to your INSTALLED_APPS setting like this::
- MEDIA_URL_geoprisma_config = '/media-geoprisma_config/'
- ....
INSTALLED_APPS = (
...
'geoprisma_config',
@@ -28,10 +26,16 @@ Contrib Admin is required.
4. Start the development server and visit http://127.0.0.1:8000/admin/
to manage your geoprisma configs (you'll need the Admin app enabled).
+Production Setup
+----------------
+
+Staticfiles is needed to work, so check https://docs.djangoproject.com/en/dev/howto/static-files/ for support.
+
Tested
------
-This app have been tested with Django 1.2 and Django 1.3, tests for django 1.4 and 1.5 are on the way.
+This app have been tested with Django 1.3, tests for django 1.4 and 1.5 are on the way.
+Django 1.2 might be ok if you add django-staticfiles app. Check https://docs.djangoproject.com/en/dev/howto/static-files/
BSD License
=======
View
0  geoprisma_config/__init__.py
No changes.
View
321 geoprisma_config/admin.py
@@ -0,0 +1,321 @@
+from django.conf import settings
+from django.contrib import admin
+from django.contrib.admin.util import unquote
+from django.contrib.admin import helpers
+from django.contrib.admin.options import IncorrectLookupParameters
+from django.core.exceptions import PermissionDenied
+from django.utils.translation import ugettext as _
+from django.contrib.admin.util import quote
+from django.utils.encoding import force_unicode
+from django.utils.safestring import mark_safe
+from django.http import Http404, HttpResponseRedirect
+from django.utils.html import escape
+from django import forms, template
+
+from geoprisma_config.models import *
+from geoprisma_config.forms import *
+from utils import join_url
+
+from django.conf.urls.defaults import patterns
+
+# Inlines
+
+class ServiceOptionInline(admin.TabularInline):
+ form = ServiceOptionForm
+ model = ServiceOption
+ extra = 2
+
+class DatastoreOptionInline(admin.TabularInline):
+ form = DatastoreOptionForm
+ model = DatastoreOption
+ extra = 2
+
+class ResourceFieldInline(admin.TabularInline):
+ model = ResourceField
+ extra = 5
+
+class ResourceOptionInline(admin.TabularInline):
+ form = ResourceOptionForm
+ model = ResourceOption
+ extra = 5
+
+class FieldOptionInline(admin.TabularInline):
+ form = FieldOptionForm
+ model = FieldOption
+ extra = 5
+
+class AccessFilterOptionInline(admin.TabularInline):
+ form = AccessFilterOptionForm
+ model = AccessFilterOption
+ extra = 5
+
+class WidgetOptionInline(admin.TabularInline):
+ form = WidgetOptionForm
+ model = WidgetOption
+ extra = 5
+
+class MapContextOptionInline(admin.TabularInline):
+ form = MapContextOptionForm
+ model = MapContextOption
+ extra = 5
+
+class MapContextResourceInline(admin.TabularInline):
+ model = MapContextResource
+ extra = 5
+
+class ApplicationWidgetInline(admin.TabularInline):
+ model = ApplicationWidget
+ extra = 5
+
+# Admin Model
+
+class BaseModelAdmin(admin.ModelAdmin):
+
+ change_form_template = 'admin/change.html'
+
+ def get_urls(self):
+ urls = super(BaseModelAdmin, self).get_urls()
+ my_urls = patterns('',
+ ('^(?P<object_id>\d+)/$', self.changelist_changeform_view),
+ )
+ return my_urls + urls
+
+ def changelist_changeform_view(self, request, extra_context=None, add=False, object_id=None):
+ "The 'change list' and 'change form' admin view for BaseModelAdmin."
+ from django.contrib.admin.views.main import ChangeList, ERROR_FLAG
+ opts = self.model._meta
+ app_label = opts.app_label
+ if not self.has_change_permission(request, None):
+ raise PermissionDenied
+
+ # Fix links of save, save_continue form buttons
+ if not add and not object_id:
+ request.path += 'add/'
+
+ if '_popup' in request.REQUEST.keys():
+ return super(BaseModelAdmin, self).add_view(request)
+
+ # the form sender is either the changelist or the changeform
+ formSender = None
+ if '_formSender' in request.POST.keys():
+ formSender = request.POST['_formSender']
+
+ if request.method == 'POST' and formSender == 'changeform' and request.POST.has_key('_cancelgoback'):
+ return HttpResponseRedirect('../')
+
+ # Check actions to see if any are available on this changelist
+ actions = self.get_actions(request)
+
+ # Remove action checkboxes if there aren't any actions available.
+ list_display = list(self.list_display)
+ if not actions:
+ try:
+ list_display.remove('action_checkbox')
+ except ValueError:
+ pass
+
+ try:
+ cl = ChangeList(request, self.model, list_display, self.list_display_links, self.list_filter,
+ self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_editable, self)
+
+ except IncorrectLookupParameters:
+ # Wacky lookup parameters were given, so redirect to the main
+ # changelist page, without parameters, and pass an 'invalid=1'
+ # parameter via the query string. If wacky parameters were given and
+ # the 'invalid=1' parameter was already in the query string, something
+ # is screwed up with the database, so display an error page.
+ if ERROR_FLAG in request.GET.keys():
+ return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
+ return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
+
+ # If the request was POSTed, this might be a bulk action or a bulk edit.
+ # Try to look up an action first, but if this isn't an action the POST
+ # will fall through to the bulk edit check, below.
+ if actions and request.method == 'POST' and formSender == 'changelist':
+ response = self.response_action(request, queryset=cl.get_query_set())
+ if response:
+ return response
+
+ # If we're allowing changelist editing, we need to construct a formset
+ # for the changelist given all the fields to be edited. Then we'll
+ # use the formset to validate/process POSTed data.
+ formset = cl.formset = None
+
+ # Handle POSTed bulk-edit data.
+ if request.method == "POST" and self.list_editable and formSender == 'changelist':
+ FormSet = self.get_changelist_formset(request)
+ formset = cl.formset = FormSet(request.POST, request.FILES, queryset=cl.result_list)
+ if formset.is_valid():
+ changecount = 0
+ for form in formset.forms:
+ if form.has_changed():
+ obj = self.save_form(request, form, change=True)
+ self.save_model(request, obj, form, change=True)
+ form.save_m2m()
+ change_msg = self.construct_change_message(request, form, None)
+ self.log_change(request, obj, change_msg)
+ changecount += 1
+
+ if changecount:
+ if changecount == 1:
+ name = force_unicode(opts.verbose_name)
+ else:
+ name = force_unicode(opts.verbose_name_plural)
+ msg = ungettext("%(count)s %(name)s was changed successfully.",
+ "%(count)s %(name)s were changed successfully.",
+ changecount) % {'count': changecount,
+ 'name': name,
+ 'obj': force_unicode(obj)}
+ self.message_user(request, msg)
+
+ return HttpResponseRedirect(request.get_full_path())
+
+ # Handle GET -- construct a formset for display.
+ elif self.list_editable:
+ FormSet = self.get_changelist_formset(request)
+ formset = cl.formset = FormSet(queryset=cl.result_list)
+
+ # Build the list of media to be used by the formset.
+ if formset:
+ media = self.media + formset.media
+ else:
+ media = self.media
+
+ # Build the action form and populate it with available actions.
+ if actions:
+ action_form = self.action_form(auto_id=None)
+ action_form.fields['action'].choices = self.get_action_choices(request)
+ else:
+ action_form = None
+
+ context = {
+ 'model_name': force_unicode(self.model._meta.verbose_name),
+ 'lc': {
+ 'title': cl.title,
+ 'cl': cl,
+ 'media': media,
+ 'root_path': self.admin_site.root_path,
+ 'app_label': app_label,
+ },
+ 'action_form': action_form,
+ 'actions_on_top': self.actions_on_top,
+ 'actions_on_bottom': self.actions_on_bottom,
+ 'is_popup': cl.is_popup,
+ 'has_add_permission': self.has_add_permission(request),
+ 'is_add': add,
+ 'object_id': object_id,
+ }
+ context.update(extra_context or {})
+
+ if object_id:
+ cl.url_for_result = lambda result, self=cl: "../%s/" % quote(getattr(result, self.pk_attname))
+ return super(BaseModelAdmin, self).change_view(request, object_id,
+ extra_context=context)
+ else:
+ if add:
+ cl.url_for_result = lambda result, self=cl: "../%s/" % quote(getattr(result, self.pk_attname))
+ else:
+ cl.url_for_result = lambda result, self=cl: "%s/" % quote(getattr(result, self.pk_attname))
+ return super(BaseModelAdmin, self).add_view(request, extra_context=context)
+
+
+ # Redirect standard views
+ def changelist_view(self, request, extra_context=None):
+ return self.changelist_changeform_view(request)
+
+ def change_view(self, request, object_id, extra_context=None):
+ return self.changelist_changeform_view(request)
+
+ def add_view(self, request, form_url='', extra_context=None):
+ return self.changelist_changeform_view(request, add=True)
+
+class ServiceAdmin(BaseModelAdmin):
+ form = ServiceForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ inlines = (ServiceOptionInline,)
+
+class DatastoreAdmin(BaseModelAdmin):
+ form = DatastoreForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ inlines = (DatastoreOptionInline,)
+
+
+class ResourceAdmin(BaseModelAdmin):
+ form = ResourceForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ inlines = (ResourceOptionInline, ResourceFieldInline)
+ filter_horizontal = ('datastores',)
+
+ class Media:
+ js = (
+ join_url(settings.STATIC_URL,'gc_js/jquery-ui-1.8.2.custom.min.js'),
+ join_url(settings.STATIC_URL,'gc_js/admin-list-reorder.js'),
+ )
+
+class FieldAdmin(BaseModelAdmin):
+ form = FieldForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ inlines = (FieldOptionInline,)
+
+class AccessFilterAdmin(BaseModelAdmin):
+ form = AccessFilterForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ inlines = (AccessFilterOptionInline,)
+
+class WidgetAdmin(BaseModelAdmin):
+ form = WidgetForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ inlines = (WidgetOptionInline,)
+
+ class Media:
+ js = (
+ join_url(settings.STATIC_URL,'gc_js/jquery-ui-1.8.2.custom.min.js'),
+ join_url(settings.STATIC_URL,'gc_js/admin-list-reorder.js'),
+ join_url(settings.STATIC_URL,'gc_js/admin-widgets.js'),
+ )
+
+class MapContextAdmin(BaseModelAdmin):
+ form = MapContextForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ inlines = (MapContextOptionInline, MapContextResourceInline)
+
+ class Media:
+ js = (
+ join_url(settings.STATIC_URL,'gc_js/jquery-ui-1.8.2.custom.min.js'),
+ join_url(settings.STATIC_URL,'gc_js/admin-list-reorder.js'),
+ )
+
+class ApplicationAdmin(BaseModelAdmin):
+ form = ApplicationForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ inlines = (ApplicationWidgetInline,)
+
+ class Media:
+ js = (
+ join_url(settings.STATIC_URL,'gc_js/jquery-ui-1.8.2.custom.min.js'),
+ join_url(settings.STATIC_URL,'gc_js/admin-list-reorder.js'),
+ )
+
+class SessionAdmin(BaseModelAdmin):
+ form = SessionForm
+ list_display = ('name',)
+ search_fields = ('name',)
+ pass # do not remove this class
+
+admin.site.register(Datastore, DatastoreAdmin)
+admin.site.register(Service, ServiceAdmin)
+admin.site.register(Field, FieldAdmin)
+admin.site.register(AccessFilter, AccessFilterAdmin)
+admin.site.register(Resource, ResourceAdmin)
+admin.site.register(Widget, WidgetAdmin)
+admin.site.register(MapContext, MapContextAdmin)
+admin.site.register(Application, ApplicationAdmin)
+admin.site.register(Session, SessionAdmin)
View
0  geoprisma_config/conf/__init__.py
No changes.
View
81 geoprisma_config/conf/types.py
@@ -0,0 +1,81 @@
+# GeoPrisma constants
+
+APPLICATION_TYPES = (
+ (1, 'ExtJS'),
+)
+
+WIDGET_TYPES = (
+ # 1 => 'quickzoom', // removed
+# (2, 'Map'), do not use this in django.
+ (3, 'MapFishLayerTree'),
+ (4, 'MapFishRecenter'),
+ (5, 'MeasureTool'),
+ # 6 => 'toolbar', // removed
+ (7, 'QueryonClick'),
+ # 8 => 'Popup', // removed
+ (9, 'ResulTextGrid'),
+ # 10 => 'removed',
+ (11, 'InitialView'),
+ (12, 'ZoomSlider'),
+ (13, 'MousePosition'),
+ (14, 'GetMousePosition'),
+ (15, 'Scale'),
+ (16, 'EditFeature_Create'),
+ (17, 'EditFeature_Update'),
+ (18, 'EditFeature_Delete'),
+ #(19, 'FeaturePanel_Form'), # will not be supported by db driver
+ (20, 'FeaturePanel_Selector'),
+ #(21, 'LegendPanel'), # removed
+ (22, 'GeoExtToolbar'),
+ #(23, 'Merge'), # removed
+ #(24, 'Split'), # removed
+ (25, 'Shortcut'),
+ #(26, 'GeoExtUx_GeoNamesSearchCombo'), # currently not supported by db driver
+ (27, 'GeoExtUx_Redliningpanel'),
+ (28, 'PdfPrint'),
+ (29, 'WMSLayerAdder'),
+ (30, 'GeoExtUx_ShortcutCombo'),
+ (31, 'GeoExtUx_ZoomTo'),
+ (32, 'HTWindow'),
+ (33, 'UnselectAll'),
+ (34, 'GeoExtPrintForm'),
+ (35, 'FileTreePanel'),
+ (36, 'GeoExtUx_LayerTreeBuilder'),
+ #(37, 'MapPanel'), # don't use it in django
+ #(38, 'Layer') # don't use it in django
+ (39, 'QueryByRect'),
+ (40, 'FeaturePanel_AttributeForm'),
+ (41, 'FeaturePanel_CustomForm'),
+ (42, 'GeoExtUx_WMSBrowser'),
+ (43, 'VectorLayer'),
+ (44, 'ResultVectorLayer'),
+ (45, 'QueryOnClickWizard'),
+ (46, 'GeoExtUx_PrintPreview'),
+ (47, 'WFSFilterBuilder'),
+ (48, 'TemplatePopup'),
+ (49, 'Toggle'),
+ (50, 'EditFeature_Split'),
+ (51, 'EditFeature_Drag'),
+ (52, 'EditFeature_Copy'),
+ (53, 'EditFeature_Confirm'),
+ (54, 'KeepActiveSession'),
+ # for user-defined widgets use numbers grater than 9999 ??
+ (10001, 'DXFImport'),
+ (10002, 'GeoctopusPrint'),
+ (10003, 'GeoctopusSplitter')
+ )
+
+
+SERVICE_TYPES = (
+ (1, 'WMS'),
+ (2, 'TileCache'),
+ (3, 'FeatureServer'),
+ (4, 'GYMO'),
+ #(5, 'Widget'), # deprecated
+ #(6, 'MapServer'), # deprecated
+ (7, 'MapFishPrint'),
+ (8, 'File'),
+ (9, 'WFS'),
+ (10, 'HttpRequest')
+ )
+
View
4 geoprisma_config/context_processors.py
@@ -0,0 +1,4 @@
+from django.conf import settings as _settings
+
+def settings(request):
+ return {'settings': _settings}
View
133 geoprisma_config/forms.py
@@ -0,0 +1,133 @@
+from django import forms
+from geoprisma_config.models import *
+from geoprisma_config.widgets import MyTextInput
+
+class ServiceForm(forms.ModelForm):
+ class Meta:
+ model = Service
+ widgets = {
+ 'name': MyTextInput(),
+ 'source': MyTextInput(),
+ }
+
+class ServiceOptionForm(forms.ModelForm):
+ class Meta:
+ model = ServiceOption
+ widgets = {
+ 'name': MyTextInput(),
+ 'value': MyTextInput(),
+ }
+
+class DatastoreForm(forms.ModelForm):
+ class Meta:
+ model = Datastore
+ widgets = {
+ 'name': MyTextInput(),
+ 'layers': MyTextInput(),
+ }
+
+class DatastoreOptionForm(forms.ModelForm):
+ class Meta:
+ model = DatastoreOption
+ widgets = {
+ 'name': MyTextInput(),
+ 'value': MyTextInput(),
+ }
+
+class FieldForm(forms.ModelForm):
+ class Meta:
+ model = Field
+ widgets = {
+ 'name': MyTextInput(),
+ 'title': MyTextInput(),
+ 'key': MyTextInput(),
+ 'domain': MyTextInput(),
+ }
+
+class FieldOptionForm(forms.ModelForm):
+ class Meta:
+ model = FieldOption
+ widgets = {
+ 'name': MyTextInput(),
+ 'value': MyTextInput(),
+ }
+
+class AccessFilterForm(forms.ModelForm):
+ class Meta:
+ model = AccessFilter
+ widgets = {
+ 'name': MyTextInput(),
+ }
+
+class AccessFilterOptionForm(forms.ModelForm):
+ class Meta:
+ model = AccessFilterOption
+ widgets = {
+ 'name': MyTextInput(),
+ 'value': MyTextInput(),
+ }
+
+class ResourceForm(forms.ModelForm):
+ class Meta:
+ model = Resource
+ widgets = {
+ 'name': MyTextInput(),
+ 'aclName': MyTextInput(),
+ 'key': MyTextInput(),
+ 'domain': MyTextInput(),
+ }
+
+class ResourceOptionForm(forms.ModelForm):
+ class Meta:
+ model = ResourceOption
+ widgets = {
+ 'name': MyTextInput(),
+ 'value': MyTextInput(),
+ 'key': MyTextInput(),
+ 'domain': MyTextInput(),
+ }
+
+class WidgetForm(forms.ModelForm):
+ class Meta:
+ model = Widget
+ widgets = {
+ 'name': MyTextInput(),
+ }
+
+class WidgetOptionForm(forms.ModelForm):
+ class Meta:
+ model = WidgetOption
+ widgets = {
+ 'name': MyTextInput(),
+ 'value': MyTextInput(),
+ }
+
+class MapContextForm(forms.ModelForm):
+ class Meta:
+ model = MapContext
+ widgets = {
+ 'name': MyTextInput(),
+ }
+
+class MapContextOptionForm(forms.ModelForm):
+ class Meta:
+ model = MapContextOption
+ widgets = {
+ 'name': MyTextInput(),
+ 'value': MyTextInput(),
+ }
+
+class ApplicationForm(forms.ModelForm):
+ class Meta:
+ model = Application
+ widgets = {
+ 'name': MyTextInput(),
+ 'template': MyTextInput(),
+ }
+
+class SessionForm(forms.ModelForm):
+ class Meta:
+ model = Session
+ widgets = {
+ 'name': MyTextInput(),
+ }
View
0  geoprisma_config/management/__init__.py
No changes.
View
0  geoprisma_config/management/commands/__init__.py
No changes.
View
689 geoprisma_config/management/commands/import.py
@@ -0,0 +1,689 @@
+import copy
+import simplejson as json
+
+from django.core.management.base import BaseCommand, CommandError
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
+from xml.etree.ElementTree import ElementTree
+from xml.parsers.expat import ExpatError
+
+from geoprisma_config.models import *
+from conf.types import *
+
+
+class Command(BaseCommand):
+ args = "<config.xml>"
+ help = 'Import geoprisma xml config file'
+
+
+ def handle(self, *args, **options):
+ if len(args) == 0:
+ raise CommandError('No xml config file provided')
+
+ filename = args[0]
+
+ self.rootElement = ElementTree()
+
+ # do some checks
+ try:
+ self.rootElement.parse(filename)
+ except IOError, e:
+ print e
+ raise CommandError('Unable to open %s' % filename)
+ except ExpatError, e:
+ print e
+ raise CommandError('Unable to parse %s' % filename)
+
+ if self.rootElement.getroot().tag != 'geoprisma':
+ raise CommandError('%s is not a geoprisma xml config file' % filename)
+
+ # registers
+ # Used when parsing workspaces
+ self.defaultMapContext = None
+ self.defaultApplication = None
+ # map contexts are read in parseWidgets(), but have to be added in the DB after resources
+ self.mapContexts = []
+ self.datastoreFields = {}
+
+ # parse the entire config
+ self.parseServices()
+ self.parseDatastores()
+ self.parseResources()
+ self.parseFields()
+ self.parseWidgets()
+ self.parseMapContexts()
+ self.parseApplications()
+ self.parseSessions()
+
+
+ def getNodeValue(self, element, tag):
+ 'Try to get a node value, return None if not found'
+
+ node = element.find(tag)
+ if node is None: return None
+
+ return node.text
+
+ def saveModel(self, model):
+ 'Clean model fields and save it in the database'
+
+ try:
+ model.full_clean() # validate the fields
+ model.save()
+ except ValidationError, e:
+ for key, errors in e.message_dict.items():
+ for error in errors:
+ print "Error: field '%s' => %s" % (key, error)
+ print "Error: Unable to save model '%s'" % model
+ return 1
+ except Exception, e:
+ print e
+ print "Error: Unable to save model '%s'" % model
+ return 2
+
+ return 0
+
+ def generateJSON(self, elements):
+ 'Parse nodes and generate json'
+
+ jsonDict = {}
+ for element in elements:
+ if len(element) is not 0:
+ jsonDict[element.tag] = self.generateArray(element)
+ else:
+ jsonDict[element.tag] = element.text
+
+ return jsonDict
+
+
+ def generateArray(self, elements):
+
+ array = []
+ for element in elements:
+ if len(element) is not 0:
+ array.append(self.generateJSON(element))
+
+ return array
+
+
+ def parseMapFishModel(self, values, elements):
+ 'Parse the <model> tag of the mapfish layer tree widget'
+
+ for element in elements:
+ newValues = copy.copy(values)
+ resourceName = element.find('resourcename')
+ if resourceName is not None:
+ try :
+ resourceModel = Resource.objects.get(name=resourceName.text)
+ if resourceModel.resourceoption_set.filter(name='layertreepath').exists():
+ print "Unable to add layertreepath option in the resource '%s': layertreepath already exists" % resourceName.text
+ continue
+ else:
+ resourceModel.resourceoption_set.create(name='layertreepath', value='/'.join(newValues))
+ if element.find('checked') is not None:
+ if resourceModel.resourceoption_set.filter(name='visibility').exists():
+ print "Unable to add visibility option in the resource '%s': visibility already exists" % resourceName.text
+ continue
+ else:
+ resourceModel.resourceoption_set.create(name='visibility', value=element.find('checked').text)
+ except ObjectDoesNotExist:
+ print "Unable to add layertreepath option in the resource '%s': invalid resource" % resourceName.text
+ continue
+ else:
+ newValues.append(element.find('textkey').text)
+ self.parseMapFishModel(newValues, element.find('children'))
+
+ def parseServices(self):
+ 'Parse services in the xml config and add them in the database'
+
+ services = self.rootElement.find('services')
+ if services is None or len(services.getchildren()) == 0:
+ print 'No services found'
+ return
+
+ count = 0
+ for service in services.getchildren():
+ # verify that the type is valid
+ type = None
+ for t in SERVICE_TYPES:
+ if t[1].lower() == service.tag:
+ type = t[0]
+ break
+
+ if type is None:
+ print "Skipping service '%s': invalid type" % service.tag
+ continue
+
+ # create the service model and save it
+ serviceModel = Service()
+ serviceModel.type = type
+ serviceModel.name = self.getNodeValue(service, 'name')
+ serviceModel.source = self.getNodeValue(service, 'source')
+ if self.saveModel(serviceModel) is 0:
+
+ # TODO parse service options, not currently supported in xml config (only provider ...)
+ provider = service.find('provider')
+ if provider is not None:
+ serviceModel.serviceoption_set.create(name=provider.tag, value=provider.text)
+
+ count += 1;
+
+
+ print "Imported %d service(s)" % count
+
+
+ def parseDatastores(self):
+ 'Parse datastores in the xml config and add them in the database'
+
+ datastores = self.rootElement.find('datastores')
+ if datastores is None or len(datastores.getchildren()) == 0:
+ print 'No datastores found'
+ return
+
+ count = 0
+ for datastore in datastores.findall('datastore'):
+
+ datastoreModel = Datastore()
+ datastoreModel.name = self.getNodeValue(datastore, 'name')
+ datastoreOptions = []
+
+ # Verify that the service exists
+ try:
+ serviceModel = Service.objects.get(name=self.getNodeValue(datastore, 'service'))
+ datastoreModel.service = serviceModel
+ except ObjectDoesNotExist:
+ print "Skipping datastore '%s': invalid service" % datastoreModel.name
+ continue
+
+ # iterate the params
+ params = datastore.find('params')
+ if params is not None:
+ for param in params.getchildren():
+ # if the param is a container... ignore it.
+ if len(param) is not 0 and param.tag != 'fields':
+ print "Ignoring param '%s': it's a container" % param.tag
+ elif param.tag == 'layers':
+ datastoreModel.layers = param.text
+ else:
+ # Add a datastore options
+ datastoreOption = DatastoreOption(name=param.tag,
+ value=param.text)
+ datastoreOptions.append(datastoreOption)
+
+ if self.saveModel(datastoreModel) is 0:
+ for datastoreOption in datastoreOptions:
+ datastoreOption.datastore = datastoreModel
+ self.saveModel(datastoreOption)
+
+ count += 1;
+
+ # parse fields
+ if params is not None:
+ fields = params.find('fields')
+ if fields is not None:
+ self.datastoreFields[datastoreModel] = fields
+
+ print "Imported %d datastore(s)" % count
+
+
+ def parseWidgets(self):
+ 'Parse widgets in the xml config and add them in the database'
+
+ widgets = self.rootElement.find('widgets')
+ if widgets is None or len(widgets.getchildren()) == 0:
+ print 'No widgets found'
+ return
+
+ count = 0
+ for widget in widgets.getchildren():
+
+ if widget.tag == 'map':
+ # register the map context, will be parsed after the resources
+ self.mapContexts.append(widget);
+ continue
+
+ # verify that the type is valid
+ type = None
+ for t in WIDGET_TYPES:
+ if t[1].lower() == widget.tag:
+ type = t[0]
+ break
+
+ if type is None:
+ print "Skipping widget '%s': invalid type" % widget.tag
+ continue
+
+ widgetModel = Widget()
+ widgetModel.type = type
+ widgetModel.name = self.getNodeValue(widget, 'name')
+
+ if self.saveModel(widgetModel) is 0:
+ # iterate the options
+ options = widget.find('options')
+ if options:
+ for option in options.getchildren():
+
+ if option.tag == 'model':
+ self.parseMapFishModel([], option)
+ else:
+ # if the option is a container.. check the suboptions
+ if len(option) is not 0:
+ jsonValue = []
+ for suboption in option:
+ if len(suboption) is not 0:
+ # another container ? generate json
+ jsonValue.append(self.generateJSON(suboption))
+ else:
+ widgetModel.widgetoption_set.create(name=suboption.tag, value=suboption.text)
+
+ if jsonValue:
+ widgetModel.widgetoption_set.create(name=option.tag, value=json.dumps(jsonValue))
+
+ else:
+ # Add a widget options
+ widgetModel.widgetoption_set.create(name=option.tag, value=option.text)
+
+ # fix all GeoExtToolbar "widget" options
+ geoExtToolbarWidgets = Widget.objects.filter(type=22)
+ for geoExtToolbarWidget in geoExtToolbarWidgets:
+ # get the widget options
+ widgetOptions = geoExtToolbarWidget.widgetoption_set.filter(name='widget')
+ for widgetOption in widgetOptions:
+ try:
+ widgetModel = Widget.objects.get(name=widgetOption.value)
+ widgetOption.value = widgetModel.id
+ self.saveModel(widgetOption)
+ except ObjectDoesNotExist:
+ continue
+
+ # fix all QueryOnClick "result" and "featurepanel" options
+ queryOnClickWidgets = Widget.objects.filter(type=7)
+ for queryOnClickWidget in queryOnClickWidgets:
+ # get the options
+ widgetOptions = queryOnClickWidget.widgetoption_set.filter(name__in=['result','featurepanel', 'RESULT', 'FEATUREPANEL'])
+ for widgetOption in widgetOptions:
+ try:
+ widgetModel = Widget.objects.get(name=widgetOption.value)
+ widgetOption.value = widgetModel.id
+ self.saveModel(widgetOption)
+ except ObjectDoesNotExist:
+ continue
+
+ count += 1;
+
+ print "Imported %d widget(s)" % count
+
+
+ def parseResources(self):
+ 'Parse resources in the xml config and add them in the database'
+
+ resources = self.rootElement.find('resources')
+ if resources is None or len(resources.getchildren()) == 0:
+ print 'No resources found'
+ return
+
+ count = 0
+ for resource in resources.findall('resource'):
+
+ resourceModel = Resource()
+ resourceModel.name = self.getNodeValue(resource, 'name')
+ resourceModel.aclName = resourceModel.name
+ resourceDatastores = []
+
+ datastores = resource.find('datastores')
+ if datastores is None or len(datastores.findall('datastore')) == 0:
+ print "Skipping resource '%s': no datastores associated" % resourceModel.name
+ continue
+
+ for datastore in datastores.findall('datastore'):
+ # Verify that the datastore exists
+ try:
+ datastoreModel = Datastore.objects.get(name=datastore.text)
+ resourceDatastores.append(datastoreModel)
+ except ObjectDoesNotExist:
+ print "Failed to link to datastore '%s': invalid datastore" % datastore.text
+ continue
+
+ # do not save it there is no valid datastores
+ if not resourceDatastores:
+ print "Skipping resource '%s': no datastores associated" % resourceModel.name
+ continue
+
+
+ if self.saveModel(resourceModel) is not 0:
+ continue
+
+ for resourceDatastore in resourceDatastores:
+ resourceModel.datastores.add(resourceDatastore)
+
+ # iterate the options
+ options = resource.find('options')
+ if options is not None:
+ for option in options.getchildren():
+ if option.tag == 'title':
+ resourceModel.resourceoption_set.create(name=option.tag,
+ value=option.text,
+ key=self.getNodeValue(option, 'key'),
+ domain=self.getNodeValue(option, 'domain'))
+ # if the option is a container... ignore it.
+ elif len(option) is not 0:
+ print "Ignoring option '%s': it's a container" % option.tag
+ else:
+ # Add a resource options
+ resourceModel.resourceoption_set.create(name=option.tag, value=option.text)
+
+ count += 1;
+
+ print "Imported %d resource(s)" % count
+
+
+ def parseFields(self):
+ 'Parse datastore fields and add them in the database'
+
+ if self.datastoreFields is {}:
+ print "No fields found"
+ return
+
+ for datastoreModel, fields in self.datastoreFields.items():
+ for field in fields.findall('field'):
+ fieldModel = Field()
+ fieldOptions = {}
+ for option in field.getchildren():
+ if option.tag == 'name':
+ fieldModel.name = option.text
+ elif option.tag == 'text':
+ fieldModel.title = option.text
+ else:
+ fieldOptions[option.tag] = option.text
+
+ # try to save the field if it doesn't exist
+ if Field.objects.filter(name=fieldModel.name).exists():
+ fieldModel = Field.objects.get(name=fieldModel.name)
+ elif self.saveModel(fieldModel) is not 0:
+ continue
+
+ # add the field options
+ for key,value in fieldOptions.items():
+ fieldOptionModel = FieldOption(name=key, value=value)
+ fieldModel.fieldoption_set.add(fieldOptionModel)
+
+ # Iterate the resources that are linked to that datastore
+ resourceSet = datastoreModel.resource_set.all()
+ for resource in resourceSet:
+ resource.resourcefield_set.create(field=fieldModel)
+
+
+ def parseMapContexts(self):
+ 'Parse map contexts in the xml config and add them in the database'
+
+ if not self.mapContexts:
+ print "No map contexts found"
+ return
+
+ count = 0
+ for mapContext in self.mapContexts:
+
+ mapContextModel = MapContext()
+ mapContextModel.name = self.getNodeValue(mapContext, 'name')
+
+ if self.saveModel(mapContextModel) is not 0:
+ continue # unable to save model...
+
+ # iterate the options
+ options = mapContext.find('options')
+ if options is not None:
+ for option in options.getchildren():
+ if len(option) is not 0:
+ print "Ignoring option '%s': it's a container" % option.tag
+ else:
+ # Add a map context options
+ mapContextModel.mapcontextoption_set.create(name=option.tag,
+ value=option.text)
+
+ # iterate the layers
+ layers = mapContext.find('layers')
+ if layers is not None:
+ for layer in layers.findall('layer'):
+
+ resources = layer.find('resourcenames')
+ if resources is None or len(resources.getchildren()) == 0:
+ continue # no resourcenames ?
+ else:
+ # first, read the layer options
+ layerOptions = []
+ options = layer.find('options')
+ if options is not None and len(options.getchildren()) != 0:
+ for option in options.getchildren():
+ if len(option) is not 0:
+ print "Ignoring option '%s': it's a container" % option.tag
+ else:
+ layerOptions.append(option)
+
+ for resource in resources.findall('resourcename'):
+ # verify that the resource exists
+ try:
+ resourceModel = Resource.objects.get(name=resource.text)
+ resourceModel.resource = resourceModel
+ # Write the layer options in the resource
+ for layerOption in layerOptions:
+ if not resourceModel.resourceoption_set.filter(name=layerOption.tag).exists():
+ resourceModel.resourceoption_set.create(name=layerOption.tag, value=layerOption.text)
+
+ # Add a map context resource
+ mapContextModel.mapcontextresource_set.create(resource=resourceModel)
+ except ObjectDoesNotExist:
+ print "Failed to link to resource '%s': invalid resource" % resource.text
+ continue
+
+ if count == 0:
+ self.defaultMapContext = mapContextModel
+ count += 1;
+
+ print "Imported %d map context(s)" % count
+
+ def parseApplications(self):
+ 'Parse applications in the xml config and add them in the database'
+
+ applications = self.rootElement.find('layouts')
+ if applications is None or len(applications.getchildren()) == 0:
+ print 'No applications found'
+ return
+
+ count = 0
+ for application in applications.findall('layout'):
+
+ drawMode = self.getNodeValue(application, 'drawmode')
+ # verify that the type is valid
+ type = None
+ for t in APPLICATION_TYPES:
+ if t[1].lower() == drawMode:
+ type = t[0]
+ break
+
+ if type is None:
+ print "Skipping application '%s': invalid type" % drawMode
+ continue
+
+ applicationModel = Application()
+ applicationModel.name = self.getNodeValue(application, 'name')
+ applicationModel.type = type
+ applicationModel.template = self.getNodeValue(application, 'file')
+
+ if self.saveModel(applicationModel) is 0:
+ if count == 0:
+ self.defaultApplication = applicationModel
+ count += 1
+
+ print "Imported %d application(s)" % count
+
+
+ def parseSessions(self):
+ 'Parse sessions in the xml config and add them in the database'
+
+ sessions = self.rootElement.find('workspaces')
+ if sessions is None or len(sessions.getchildren()) == 0:
+ print 'No sessions found'
+ return
+
+ count = 0
+ for session in sessions.findall('workspace'):
+
+ name = self.getNodeValue(session, 'name')
+ application = self.getNodeValue(session, 'layout')
+ if application is None and self.defaultApplication is not None:
+ application = self.defaultApplication.name
+
+ if application is None:
+ print "Skipping session '%s': invalid application or map context" % name
+ continue
+
+ sessionModel = Session()
+ sessionModel.name = name
+
+ mapContextModel = MapContext()
+ mapContextModel.name = name
+
+ if self.saveModel(mapContextModel) is not 0:
+ continue # unable to save model...
+
+ try:
+ sessionModel.application = Application.objects.get(name=application)
+ sessionModel.mapContext = mapContextModel
+ except ObjectDoesNotExist:
+ print "Failed to link to application '%s': invalid application" % application
+ continue
+
+ if self.saveModel(sessionModel) is 0:
+ count += 1
+
+
+ resources = session.find('resources')
+ for resource in resources.findall('resource'):
+ name = self.getNodeValue(resource, 'name')
+ if name is None:
+ print "Failed to link to resource '%s': invalid resource" % name
+ continue
+
+ try:
+ resourceModel = Resource.objects.get(name=name)
+ if not sessionModel.mapContext.mapcontextresource_set.filter(resource=resourceModel).exists():
+ sessionModel.mapContext.mapcontextresource_set.create(resource=resourceModel)
+
+ except ObjectDoesNotExist:
+ print "Failed to link to resource '%s': invalid resource" % name
+ continue
+
+ widgets = resource.find('widgets')
+ for widget in widgets.findall('widget'):
+ if len(widget.getchildren()) != 0:
+ name = self.getNodeValue(widget, 'name')
+ if name is None:
+ print "Failed to link to widget '%s': invalid widget" % name
+ continue
+
+ try:
+ widgetModel = Widget.objects.get(name=name)
+ if not sessionModel.application.applicationwidget_set.filter(widget=widgetModel).exists():
+ sessionModel.application.applicationwidget_set.create(widget=widgetModel)
+ except ObjectDoesNotExist:
+ print "Failed to link to widget '%s': invalid widget" % name
+ continue
+
+ else:
+ try:
+ widgetModel = Widget.objects.get(name=widget.text)
+ if not sessionModel.application.applicationwidget_set.filter(widget=widgetModel).exists():
+
+ sessionModel.application.applicationwidget_set.create(widget=widgetModel)
+ except ObjectDoesNotExist:
+ print "Failed to link to widget '%s': invalid widget" % widget.text
+ continue
+
+ print "Imported %d session(s)" % count
+
+
+ def parseSessionsOld(self):
+ 'Parse sessions in the xml config and add them in the database'
+
+ sessions = self.rootElement.find('workspaces')
+ if sessions is None or len(sessions.getchildren()) == 0:
+ print 'No sessions found'
+ return
+
+ count = 0
+ for session in sessions.findall('workspace'):
+
+ name = self.getNodeValue(session, 'name')
+ application = self.getNodeValue(session, 'layout')
+ if application is None and self.defaultApplication is not None:
+ application = self.defaultApplication.name
+
+ mapContext = self.getNodeValue(session, 'map')
+ if mapContext is None and self.defaultMapContext is not None:
+ mapContext = self.defaultMapContext.name
+
+ if application is None or mapContext is None:
+ print "Skipping session '%s': invalid application or map context" % name
+ continue
+
+ sessionModel = Session()
+ sessionModel.name = name
+
+ try:
+ sessionModel.application = Application.objects.get(name=application)
+ except ObjectDoesNotExist:
+ print "Failed to link to application '%s': invalid application" % application
+ continue
+
+ try:
+ sessionModel.mapContext = MapContext.objects.get(name=mapContext)
+ except ObjectDoesNotExist:
+ print "Failed to link to map context '%s': invalid map context" % mapContext
+ continue
+
+ if self.saveModel(sessionModel) is 0:
+ count += 1
+
+
+ resources = session.find('resources')
+ for resource in resources.findall('resource'):
+ name = self.getNodeValue(resource, 'name')
+ if name is None:
+ print "Failed to link to resource '%s': invalid resource" % name
+ continue
+
+ try:
+ resourceModel = Resource.objects.get(name=name)
+ if not sessionModel.mapContext.mapcontextresource_set.filter(resource=resourceModel).exists():
+ sessionModel.mapContext.mapcontextresource_set.create(resource=resourceModel)
+
+ except ObjectDoesNotExist:
+ print "Failed to link to resource '%s': invalid resource" % name
+ continue
+
+ widgets = resource.find('widgets')
+ for widget in widgets.findall('widget'):
+ if len(widget.getchildren()) != 0:
+ name = self.getNodeValue(widget, 'name')
+ if name is None:
+ print "Failed to link to widget '%s': invalid widget" % name
+ continue
+
+ try:
+ widgetModel = Widget.objects.get(name=name)
+ if not sessionModel.application.applicationwidget_set.filter(widget=widgetModel).exists():
+ sessionModel.application.applicationwidget_set.create(widget=widgetModel)
+ except ObjectDoesNotExist:
+ print "Failed to link to widget '%s': invalid widget" % name
+ continue
+
+ else:
+ try:
+ widgetModel = Widget.objects.get(name=widget.text)
+ if not sessionModel.application.applicationwidget_set.filter(widget=widgetModel).exists():
+
+ sessionModel.application.applicationwidget_set.create(widget=widgetModel)
+ except ObjectDoesNotExist:
+ print "Failed to link to widget '%s': invalid widget" % widget.text
+ continue
+
+ print "Imported %d session(s)" % count
View
269 geoprisma_config/models.py
@@ -0,0 +1,269 @@
+from django.db import models
+from conf.types import *# Create an abstract class for the new manager.
+
+class Service(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ source = models.TextField()
+ type = models.PositiveIntegerField(choices=SERVICE_TYPES)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class ServiceOption(models.Model):
+ service = models.ForeignKey(Service)
+ name = models.TextField()
+ value = models.TextField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class Datastore(models.Model):
+ service = models.ForeignKey(Service)
+ name = models.CharField(max_length=255, unique=True)
+ layers = models.TextField(null=True)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class DatastoreOption(models.Model):
+ datastore = models.ForeignKey(Datastore)
+ name = models.TextField()
+ value = models.TextField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class Field(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ title = models.TextField(blank=True)
+ key = models.TextField(null=True, blank=True)
+ domain = models.TextField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class FieldOption(models.Model):
+ field = models.ForeignKey(Field)
+ name = models.TextField()
+ value = models.TextField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class AccessFilter(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+
+
+ def __unicode__(self):
+ return self.name
+
+class AccessFilterOption(models.Model):
+ accessfilter = models.ForeignKey(AccessFilter)
+ name = models.TextField()
+ value = models.TextField(null=True, blank=True)
+
+
+ def __unicode__(self):
+ return self.name
+
+class Resource(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ aclName = models.TextField(null=True, blank=True, db_column='acl_name')
+ key = models.TextField(null=True, blank=True)
+ domain = models.TextField(null=True, blank=True)
+ datastores = models.ManyToManyField(Datastore)
+ accessfilters = models.ManyToManyField(AccessFilter, null=True, blank=True)
+ models.ManyToManyField(Field, through='ResourceField')
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class ResourceField(models.Model):
+ resource = models.ForeignKey(Resource)
+ field = models.ForeignKey(Field)
+ order = models.IntegerField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('order',)
+
+ def save(self, *args, **kwargs):
+ model = self.__class__
+
+ if self.order is None:
+ # Append
+ try:
+ last = model.objects.filter(resource=self.resource).order_by('-order')[0]
+ self.order = last.order + 1
+ except IndexError:
+ # First row
+ self.order = 1
+
+ return super(ResourceField, self).save(*args, **kwargs)
+
+
+ def __unicode__(self):
+ return self.field.name
+
+class ResourceOption(models.Model):
+ resource = models.ForeignKey(Resource)
+ name = models.TextField()
+ value = models.TextField(null=True, blank=True)
+ key = models.TextField(null=True, blank=True)
+ domain = models.TextField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class Widget(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ type = models.PositiveIntegerField(choices=WIDGET_TYPES)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class WidgetOption(models.Model):
+ widget = models.ForeignKey(Widget)
+ name = models.TextField()
+ value = models.TextField(null=True, blank=True)
+ order = models.IntegerField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('order',)
+
+ def save(self, *args, **kwargs):
+ model = self.__class__
+
+ if self.order is None:
+ # Append
+ try:
+ last = model.objects.filter(widget=self.widget).order_by('-order')[0]
+ self.order = last.order + 1
+ except IndexError:
+ # First row
+ self.order = 1
+
+ return super(WidgetOption, self).save(*args, **kwargs)
+
+ def __unicode__(self):
+ return self.name
+
+class MapContext(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ models.ManyToManyField(Resource, through='MapContextResource')
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class MapContextOption(models.Model):
+ mapContext = models.ForeignKey(MapContext, db_column='mapcontext_id')
+ name = models.TextField()
+ value = models.TextField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class MapContextResource(models.Model):
+ mapContext = models.ForeignKey(MapContext, db_column='mapcontext_id')
+ resource = models.ForeignKey(Resource)
+ order = models.IntegerField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('order',)
+
+ def save(self, *args, **kwargs):
+ model = self.__class__
+
+ if self.order is None:
+ # Append
+ try:
+ last = model.objects.filter(mapContext=self.mapContext).order_by('-order')[0]
+ self.order = last.order + 1
+ except IndexError:
+ # First row
+ self.order = 1
+
+ return super(MapContextResource, self).save(*args, **kwargs)
+
+ def __unicode__(self):
+ return self.resource.name
+
+class Application(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ template = models.TextField()
+ type = models.PositiveIntegerField(choices=APPLICATION_TYPES)
+ models.ManyToManyField(Widget, through='ApplicationWidget')
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class ApplicationWidget(models.Model):
+ application = models.ForeignKey(Application)
+ widget = models.ForeignKey(Widget)
+ order = models.IntegerField(null=True, blank=True)
+
+ class Meta:
+ ordering = ('order',)
+
+ def save(self, *args, **kwargs):
+ model = self.__class__
+
+ if self.order is None:
+ # Append
+ try:
+ last = model.objects.filter(application=self.application).order_by('-order')[0]
+ self.order = last.order + 1
+ except IndexError:
+ # First row
+ self.order = 1
+
+ return super(ApplicationWidget, self).save(*args, **kwargs)
+
+ def __unicode__(self):
+ return self.widget.name
+
+class Session(models.Model):
+ name = models.CharField(max_length=255, unique=True)
+ application = models.ForeignKey(Application)
+ mapContext = models.ForeignKey(MapContext, db_column='mapcontext_id')
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
View
827 geoprisma_config/static/admin/gc_css/base.css
@@ -0,0 +1,827 @@
+/*
+ DJANGO Admin styles
+*/
+
+body {
+ margin: 0;
+ padding: 0;
+ font-size: 12px;
+ font-family: "Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
+ color: #333;
+ background: #fff;
+}
+
+/* LINKS */
+
+a:link, a:visited {
+ color: #5b80b2;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #036;
+}
+
+a img {
+ border: none;
+}
+
+a.section:link, a.section:visited {
+ color: white;
+ text-decoration: none;
+}
+
+/* GLOBAL DEFAULTS */
+
+p, ol, ul, dl {
+ margin: .2em 0 .8em 0;
+}
+
+p {
+ padding: 0;
+ line-height: 140%;
+}
+
+h1,h2,h3,h4,h5 {
+ font-weight: bold;
+}
+
+h1 {
+ font-size: 18px;
+ color: #666;
+ padding: 0 6px 0 0;
+ margin: 0 0 .2em 0;
+}
+
+h2 {
+ font-size: 16px;
+ margin: 1em 0 .5em 0;
+}
+
+h2.subhead {
+ font-weight: normal;
+ margin-top: 0;
+}
+
+h3 {
+ font-size: 14px;
+ margin: .8em 0 .3em 0;
+ color: #666;
+ font-weight: bold;
+}
+
+h4 {
+ font-size: 12px;
+ margin: 1em 0 .8em 0;
+ padding-bottom: 3px;
+}
+
+h5 {
+ font-size: 10px;
+ margin: 1.5em 0 .5em 0;
+ color: #666;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+ul li {
+ list-style-type: square;
+ padding: 1px 0;
+}
+
+ul.plainlist {
+ margin-left: 0 !important;
+}
+
+ul.plainlist li {
+ list-style-type: none;
+}
+
+li ul {
+ margin-bottom: 0;
+}
+
+li, dt, dd {
+ font-size: 11px;
+ line-height: 14px;
+}
+
+dt {
+ font-weight: bold;
+ margin-top: 4px;
+}
+
+dd {
+ margin-left: 0;
+}
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+}
+
+blockquote {
+ font-size: 11px;
+ color: #777;
+ margin-left: 2px;
+ padding-left: 10px;
+ border-left: 5px solid #ddd;
+}
+
+code, pre {
+ font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
+ background: inherit;
+ color: #666;
+ font-size: 11px;
+}
+
+pre.literal-block {
+ margin: 10px;
+ background: #eee;
+ padding: 6px 8px;
+}
+
+code strong {
+ color: #930;
+}
+
+hr {
+ clear: both;
+ color: #eee;
+ background-color: #eee;
+ height: 1px;
+ border: none;
+ margin: 0;
+ padding: 0;
+ font-size: 1px;
+ line-height: 1px;
+}
+
+/* TEXT STYLES & MODIFIERS */
+
+.small {
+ font-size: 11px;
+}
+
+.tiny {
+ font-size: 10px;
+}
+
+p.tiny {
+ margin-top: -2px;
+}
+
+.mini {
+ font-size: 9px;
+}
+
+p.mini {
+ margin-top: -3px;
+}
+
+.help, p.help {
+ font-size: 10px !important;
+ color: #999;
+}
+
+p img, h1 img, h2 img, h3 img, h4 img, td img {
+ vertical-align: middle;
+}
+
+.quiet, a.quiet:link, a.quiet:visited {
+ color: #999 !important;
+ font-weight: normal !important;
+}
+
+.quiet strong {
+ font-weight: bold !important;
+}
+
+.float-right {
+ float: right;
+}
+
+.float-left {
+ float: left;
+}
+
+.clear {
+ clear: both;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-right {
+ text-align: right;
+}
+
+.example {
+ margin: 10px 0;
+ padding: 5px 10px;
+ background: #efefef;
+}
+
+.nowrap {
+ white-space: nowrap;
+}
+
+/* TABLES */
+
+table {
+ border-collapse: collapse;
+ border-color: #ccc;
+}
+
+td, th {
+ font-size: 11px;
+ line-height: 13px;
+ border-bottom: 1px solid #eee;
+ vertical-align: top;
+ padding: 5px;
+ font-family: "Lucida Grande", Verdana, Arial, sans-serif;
+}
+
+th {
+ text-align: left;
+ font-size: 12px;
+ font-weight: bold;
+}
+
+thead th,
+tfoot td {
+ color: #666;
+ padding: 2px 5px;
+ font-size: 11px;
+ background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;
+ border-left: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+}
+
+tfoot td {
+ border-bottom: none;
+ border-top: 1px solid #ddd;
+}
+
+thead th:first-child,
+tfoot td:first-child {
+ border-left: none !important;
+}
+
+thead th.optional {
+ font-weight: normal !important;
+}
+
+fieldset table {
+ border-right: 1px solid #eee;
+}
+
+tr.row-label td {
+ font-size: 9px;
+ padding-top: 2px;
+ padding-bottom: 0;
+ border-bottom: none;
+ color: #666;
+ margin-top: -1px;
+}
+
+tr.alt {
+ background: #f6f6f6;
+}
+
+.row1 {
+ background: #EDF3FE;
+}
+
+.row2 {
+ background: white;
+}
+
+/* SORTABLE TABLES */
+
+thead th a:link, thead th a:visited {
+ color: #666;
+ display: block;
+}
+
+table thead th.sorted {
+ background-position: bottom left !important;
+}
+
+table thead th.sorted a {
+ padding-right: 13px;
+}
+
+table thead th.ascending a {
+ background: url(../img/admin/arrow-down.gif) right .4em no-repeat;
+}
+
+table thead th.descending a {
+ background: url(../img/admin/arrow-up.gif) right .4em no-repeat;
+}
+
+/* ORDERABLE TABLES */
+
+table.orderable tbody tr td:hover {
+ cursor: move;
+}
+
+table.orderable tbody tr td:first-child {
+ padding-left: 14px;
+ background-image: url(../img/admin/nav-bg-grabber.gif);
+ background-repeat: repeat-y;
+}
+
+table.orderable-initalized .order-cell, body>tr>td.order-cell {
+ display: none;
+}
+
+/* FORM DEFAULTS */
+
+input, textarea, select {
+ margin: 2px 0;
+ padding: 2px 3px;
+ vertical-align: middle;
+ font-family: "Lucida Grande", Verdana, Arial, sans-serif;
+ font-weight: normal;
+ font-size: 11px;
+}
+
+textarea {
+ vertical-align: top !important;
+}
+
+input[type=text], input[type=password], textarea, select, .vTextField {
+ border: 1px solid #ccc;
+}
+
+/* FORM BUTTONS */
+
+.button, input[type=submit], input[type=button], .submit-row input {
+ background: white url(../img/admin/nav-bg.gif) bottom repeat-x;
+ padding: 3px;
+ color: black;
+ border: 1px solid #bbb;
+ border-color: #ddd #aaa #aaa #ddd;
+}
+
+.button:active, input[type=submit]:active, input[type=button]:active {
+ background-image: url(../img/admin/nav-bg-reverse.gif);
+ background-position: top;
+}
+
+.button.default, input[type=submit].default, .submit-row input.default {
+ border: 2px solid #5b80b2;
+ background: #7CA0C7 url(../img/admin/default-bg.gif) bottom repeat-x;
+ font-weight: bold;
+ color: white;
+ float: right;
+}
+
+.button.default:active, input[type=submit].default:active {
+ background-image: url(../img/admin/default-bg-reverse.gif);
+ background-position: top;
+}
+
+/* MODULES */
+
+.module {
+ border: 1px solid #ccc;
+ margin-bottom: 5px;
+ background: white;
+}
+
+.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+.module blockquote {
+ margin-left: 12px;
+}
+
+.module ul, .module ol {
+ margin-left: 1.5em;
+}
+
+.module h3 {
+ margin-top: .6em;
+}
+
+.module h2, .module caption, .inline-group h2 {
+ margin: 0;
+ padding: 2px 5px 3px 5px;
+ font-size: 11px;
+ text-align: left;
+ font-weight: bold;
+ background: #7CA0C7 url(../img/admin/default-bg.gif) top left repeat-x;
+ color: white;
+}
+
+.module table {
+ border-collapse: collapse;
+}
+
+/* MESSAGES & ERRORS */
+
+ul.messagelist {
+ padding: 0 0 5px 0;
+ margin: 0;
+}
+
+ul.messagelist li {
+ font-size: 12px;
+ display: block;
+ padding: 4px 5px 4px 25px;
+ margin: 0 0 3px 0;
+ border-bottom: 1px solid #ddd;
+ color: #666;
+ background: #ffc url(../img/admin/icon_success.gif) 5px .3em no-repeat;
+}
+
+.errornote {
+ font-size: 12px !important;
+ display: block;
+ padding: 4px 5px 4px 25px;
+ margin: 0 0 3px 0;
+ border: 1px solid red;
+ color: red;
+ background: #ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat;
+}
+
+ul.errorlist {
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+.errorlist li {
+ font-size: 12px !important;
+ display: block;
+ padding: 4px 5px 4px 25px;
+ margin: 0 0 3px 0;
+ border: 1px solid red;
+ color: white;
+ background: red url(../img/admin/icon_alert.gif) 5px .3em no-repeat;
+}
+
+td ul.errorlist {
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+td ul.errorlist li {
+ margin: 0 !important;
+}
+
+.errors {
+ background: #ffc;
+}
+
+.errors input, .errors select {
+ border: 1px solid red;
+}
+
+div.system-message {
+ background: #ffc;
+ margin: 10px;
+ padding: 6px 8px;
+ font-size: .8em;
+}
+
+div.system-message p.system-message-title {
+ padding: 4px 5px 4px 25px;
+ margin: 0;
+ color: red;
+ background: #ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat;
+}
+
+.description {
+ font-size: 12px;
+ padding: 5px 0 0 12px;
+}
+
+/* BREADCRUMBS */
+
+div.breadcrumbs {
+ background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
+ padding: 2px 8px 3px 8px;
+ font-size: 11px;
+ color: #999;
+ border-top: 1px solid white;
+ border-bottom: 1px solid #ccc;
+ text-align: left;
+}
+
+/* ACTION ICONS */
+
+.addlink {
+ padding-left: 12px;
+ background: url(../img/admin/icon_addlink.gif) 0 .2em no-repeat;
+}
+
+.changelink {
+ padding-left: 12px;
+ background: url(../img/admin/icon_changelink.gif) 0 .2em no-repeat;
+}
+
+.deletelink {
+ padding-left: 12px;
+ background: url(../img/admin/icon_deletelink.gif) 0 .25em no-repeat;
+}
+
+a.deletelink:link, a.deletelink:visited {
+ color: #CC3434;
+}
+
+a.deletelink:hover {
+ color: #993333;
+}
+
+/* OBJECT TOOLS */
+
+.object-tools {
+ font-size: 10px;
+ font-weight: bold;
+ font-family: Arial,Helvetica,sans-serif;
+ padding-left: 0;
+ float: right;
+ position: relative;
+ margin-top: -2.4em;
+ margin-bottom: -2em;
+}
+
+.form-row .object-tools {
+ margin-top: 5px;
+ margin-bottom: 5px;
+ float: none;
+ height: 2em;
+ padding-left: 3.5em;
+}
+
+.object-tools li {
+ display: block;
+ float: left;
+ background: url(../img/admin/tool-left.gif) 0 0 no-repeat;
+ padding: 0 0 0 8px;
+ margin-left: 2px;
+ height: 16px;
+}
+
+.object-tools li:hover {
+ background: url(../img/admin/tool-left_over.gif) 0 0 no-repeat;
+}
+
+.object-tools a:link, .object-tools a:visited {
+ display: block;
+ float: left;
+ color: white;
+ padding: .1em 14px .1em 8px;
+ height: 14px;
+ background: #999 url(../img/admin/tool-right.gif) 100% 0 no-repeat;
+}
+
+.object-tools a:hover, .object-tools li:hover a {
+ background: #417690 url(../img/admin/tool-right_over.gif) 100% 0 no-repeat;
+}
+
+.object-tools a.viewsitelink, .object-tools a.golink {
+ background: #999 url(../img/admin/tooltag-arrowright.gif) top right no-repeat;
+ padding-right: 28px;
+}
+
+.object-tools a.viewsitelink:hover, .object-tools a.golink:hover {
+ background: #5b80b2 url(../img/admin/tooltag-arrowright_over.gif) top right no-repeat;
+}
+
+.object-tools a.addlink {
+ background: #999 url(../img/admin/tooltag-add.gif) top right no-repeat;
+ padding-right: 28px;
+}
+
+.object-tools a.addlink:hover {
+ background: #417690 url(../img/admin/tooltag-add_over.gif) top right no-repeat;
+}
+
+/* OBJECT HISTORY */
+
+table#change-history {
+ width: 100%;
+}
+
+table#change-history tbody th {
+ width: 16em;
+}
+
+/* PAGE STRUCTURE */
+
+#container {
+ position: relative;
+ width: 100%;
+ min-width: 760px;
+ padding: 0;
+}
+
+#content {
+ margin: 10px 15px;
+}
+
+#header {
+ width: 100%;
+}
+
+#content-main {
+ float: left;
+ width: 100%;
+}
+
+#content-related {
+ float: right;
+ width: 18em;
+ position: relative;
+ margin-right: -19em;
+}
+
+#footer {
+ clear: both;
+ padding: 10px;
+}
+
+/* COLUMN TYPES */
+
+.colMS {
+ margin-right: 20em !important;
+}
+
+.colSM {
+ margin-left: 20em !important;
+}
+
+.colSM #content-related {
+ float: left;
+ margin-right: 0;
+ margin-left: -19em;
+}
+
+.colSM #content-main {
+ float: right;
+}
+
+.popup .colM {
+ width: 95%;
+}
+
+.subcol {
+ float: left;
+ width: 46%;
+ margin-right: 15px;
+}
+
+.dashboard #content {
+ width: 500px;
+}
+
+/* HEADER */
+
+#header {
+ background: #417690;
+ color: #ffc;
+ overflow: hidden;
+}
+
+#header a:link, #header a:visited {
+ color: white;
+}
+
+#header a:hover {
+ text-decoration: underline;
+}
+
+#branding h1 {
+ padding: 0 10px;
+ font-size: 18px;
+ margin: 8px 0;
+ font-weight: normal;
+ color: #f4f379;
+}
+
+#branding h2 {
+ padding: 0 10px;
+ font-size: 14px;
+ margin: -8px 0 8px 0;
+ font-weight: normal;
+ color: #ffc;
+}
+
+#user-tools {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 1.2em 10px;
+ font-size: 11px;
+ text-align: right;
+}
+
+/* SIDEBAR */
+
+#content-related h3 {
+ font-size: 12px;
+ color: #666;
+ margin-bottom: 3px;
+}
+
+#content-related h4 {
+ font-size: 11px;
+}
+
+#content-related .module h2 {
+ background: #eee url(../img/admin/nav-bg.gif) bottom left repeat-x;
+ color: #666;
+}
+
+/*************************************************************/
+/**********Menu principal*************************************/
+/*************************************************************/
+body.login div#main_menu{
+ display:none;
+}
+div#main_menu{
+ padding-top:10px;
+ background:#fff;
+ height:26px;
+ border-bottom:1px solid #CCC;
+}
+
+div#main_menu ul
+{
+ margin:0px;
+ padding:0px;
+ padding-left:10px;
+}
+
+div#main_menu ul li
+{
+ float:left;
+ margin-right:2px;
+ padding-bottom:0px;
+ list-style-type:none;
+}
+
+div#main_menu ul li a
+{
+ display:block;
+ padding:5px;
+ border:1px solid #CCC;
+ border-bottom:0px solid;
+ text-decoration:none;
+ background:white;
+}
+
+div#main_menu ul li.current a
+{
+ background: #417690;
+ color: #F4F379;
+ border-bottom:1px solid white;
+}
+
+div#main_menu ul li a:hover
+{
+ background:#f9f9f9;
+}
+
+div#main_menu ul li.current a:hover
+{
+ background: #417690;
+}
+
+div#left_content
+{
+ float:left;
+ width:26%;
+ margin-right:20px;
+ margin-left:10px;