diff --git a/apps/app_registry/__init__.py b/apps/app_registry/__init__.py new file mode 100644 index 0000000000..acf978062e --- /dev/null +++ b/apps/app_registry/__init__.py @@ -0,0 +1,23 @@ +from __future__ import absolute_import + +import logging + +from django.conf import settings +from django.utils.importlib import import_module + +from .models import App + +logger = logging.getLogger(__name__) + + +def register_apps(): + for app_name in settings.INSTALLED_APPS: + logger.debug('registering: %s' % app_name) + App.register(app_name) + try: + post_init = import_module('%s.post_init' % app_name) + except ImportError: + logger.error('Unable to import post_init module from: %s' % app_name) + + +register_apps() diff --git a/apps/app_registry/models.py b/apps/app_registry/models.py new file mode 100644 index 0000000000..e78ca0ccce --- /dev/null +++ b/apps/app_registry/models.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import + +import logging +import imp +import sys + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.utils.importlib import import_module + +from project_setup.api import register_setup +from project_tools.api import register_tool +from navigation.api import register_top_menu +from bootstrap.classes import Cleanup, BootstrapModel + +logger = logging.getLogger(__name__) + + +class App(object): + @classmethod + def register(cls, app_name): + logger.debug('Trying to import: %s' % app_name) + try: + app_module = import_module(app_name) + except ImportError: + transaction.rollback + logger.error('Unable to import app: %s' % app_name) + else: + logger.debug('Trying to import registry from: %s' % app_name) + try: + registration = import_module('%s.registry' % app_name) + except ImportError: + logger.error('Unable to import registry for app: %s' % app_name) + else: + if not getattr(registration, 'disabled', False): + app = App() + app.name=app_name + # If there are not error go ahead with the stored app instance + app.label = getattr(registration, 'label', app_name) + app.description = getattr(registration, 'description', u'') + + for link in getattr(registration, 'setup_links', []): + logger.debug('setup link: %s' % link) + register_setup(link) + + for link in getattr(registration, 'tool_links', []): + logger.debug('tool link: %s' % link) + register_tool(link) + + for index, link in enumerate(getattr(registration, 'menu_links', [])): + logger.debug('menu_link: %s' % link) + register_top_menu(name='%s.%s' % (app_name, index), link=link) + + for cleanup_function in getattr(registration, 'cleanup_functions', []): + logger.debug('cleanup_function: %s' % cleanup_function) + Cleanup(cleanup_function) + + for bootstrap_model in getattr(registration, 'bootstrap_models', []): + logger.debug('bootstrap_model: %s' % bootstrap_model) + BootstrapModel(model_name=bootstrap_model.get('name'), app_name=app_name, sanitize=bootstrap_model.get('sanitize', True)) + + def __unicode__(self): + return unicode(self.label) diff --git a/apps/bootstrap/__init__.py b/apps/bootstrap/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/bootstrap/admin.py b/apps/bootstrap/admin.py new file mode 100644 index 0000000000..86013694b3 --- /dev/null +++ b/apps/bootstrap/admin.py @@ -0,0 +1,7 @@ +from __future__ import absolute_import + +from django.contrib import admin + +from .models import BootstrapSetup + +admin.site.register(BootstrapSetup) diff --git a/apps/bootstrap/classes.py b/apps/bootstrap/classes.py new file mode 100644 index 0000000000..ba0224ac24 --- /dev/null +++ b/apps/bootstrap/classes.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import + +import logging + +from django.db import models +from django.core import serializers +from django.utils.datastructures import SortedDict + +from .exceptions import ExistingData +from .literals import FIXTURE_TYPE_PK_NULLIFIER + +logger = logging.getLogger(__name__) + + +class Cleanup(object): + """ + Class to store all the registered cleanup functions in one place + """ + _registry = {} + + @classmethod + def execute_all(cls): + for cleanup in cls._registry.values(): + cleanup.function() + + def __init__(self, function): + self.function = function + self.__class__._registry[id(self)] = self + + +class BootstrapModel(object): + """ + Class used to keep track of all the models to be dumped to create a + bootstrap setup from the current setup in use + """ + _registry = SortedDict() + + @classmethod + def check_for_data(cls): + for model in cls.get_all(): + model_instance = models.get_model(model.app_name, model.model_name) + if model_instance.objects.all().count(): + raise ExistingData + + @classmethod + def get_all(cls): + return cls._registry.values() + + def get_fullname(self): + return '.'.join([self.app_name, self.model_name]) + + def get_model_instance(self): + return models.get_model(self.app_name, self.model_name) + + def __init__(self, model_name, app_name=None, sanitize=True): + app_name_splitted = None + if '.' in model_name: + app_name_splitted, model_name = model_name.split('.') + + self.app_name = app_name_splitted or app_name + if not self.app_name: + raise Exception('Pass either a dotted app plus model name or a model name and a separate app name') + self.model_name = model_name + self.__class__._registry[self.get_fullname()] = self + self.sanitize = sanitize + + def dump(self, serialization_format): + result = serializers.serialize(serialization_format, self.get_model_instance().objects.all(), indent=4, use_natural_keys=True) + logger.debug('result: %s' % result) + if self.sanitize: + # Remove primary key values + result = FIXTURE_TYPE_PK_NULLIFIER[serialization_format](result) + return result diff --git a/apps/bootstrap/exceptions.py b/apps/bootstrap/exceptions.py new file mode 100644 index 0000000000..933ee9c84a --- /dev/null +++ b/apps/bootstrap/exceptions.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + + +class ExistingData(Exception): + """ + Raised when an attempt to execute a bootstrap setup is made and there is + existing data that would be corrupted or damaged by the loading the + bootstrap's fixture + """ + pass diff --git a/apps/bootstrap/forms.py b/apps/bootstrap/forms.py new file mode 100644 index 0000000000..10a31300e8 --- /dev/null +++ b/apps/bootstrap/forms.py @@ -0,0 +1,28 @@ +from __future__ import absolute_import + +import logging + +from django import forms +from django.utils.translation import ugettext_lazy as _ + +from common.forms import DetailForm + +from .models import BootstrapSetup + +logger = logging.getLogger(__name__) + + +class BootstrapSetupForm(forms.ModelForm): + class Meta: + model = BootstrapSetup + + +class BootstrapSetupForm_view(DetailForm): + class Meta: + model = BootstrapSetup + + +class BootstrapSetupForm_dump(forms.ModelForm): + class Meta: + model = BootstrapSetup + exclude = ['fixture'] diff --git a/apps/bootstrap/links.py b/apps/bootstrap/links.py new file mode 100644 index 0000000000..953aa2f725 --- /dev/null +++ b/apps/bootstrap/links.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from .permissions import (PERMISSION_BOOTSTRAP_VIEW, PERMISSION_BOOTSTRAP_CREATE, + PERMISSION_BOOTSTRAP_EDIT, PERMISSION_BOOTSTRAP_DELETE, + PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_BOOTSTRAP_DUMP, + PERMISSION_NUKE_DATABASE) + +link_bootstrap_setup_tool = {'text': _(u'bootstrap'), 'view': 'bootstrap_setup_list', 'icon': 'lightning.png', 'permissions': [PERMISSION_BOOTSTRAP_VIEW]} +link_bootstrap_setup_list = {'text': _(u'bootstrap setup list'), 'view': 'bootstrap_setup_list', 'famfam': 'lightning', 'permissions': [PERMISSION_BOOTSTRAP_VIEW]} +link_bootstrap_setup_create = {'text': _(u'create new bootstrap setup'), 'view': 'bootstrap_setup_create', 'famfam': 'lightning_add', 'permissions': [PERMISSION_BOOTSTRAP_CREATE]} +link_bootstrap_setup_edit = {'text': _(u'edit'), 'view': 'bootstrap_setup_edit', 'args': 'object.pk', 'famfam': 'edit', 'permissions': [PERMISSION_BOOTSTRAP_EDIT]} +link_bootstrap_setup_delete = {'text': _(u'delete'), 'view': 'bootstrap_setup_delete', 'args': 'object.pk', 'famfam': 'lightning_delete', 'permissions': [PERMISSION_BOOTSTRAP_DELETE]} +link_bootstrap_setup_view = {'text': _(u'details'), 'view': 'bootstrap_setup_view', 'args': 'object.pk', 'famfam': 'lightning', 'permissions': [PERMISSION_BOOTSTRAP_VIEW]} +link_bootstrap_setup_execute = {'text': _(u'execute'), 'view': 'bootstrap_setup_execute', 'args': 'object.pk', 'famfam': 'lightning_go', 'permissions': [PERMISSION_BOOTSTRAP_EXECUTE]} +link_bootstrap_setup_dump = {'text': _(u'dump current setup'), 'view': 'bootstrap_setup_dump', 'famfam': 'arrow_down', 'permissions': [PERMISSION_BOOTSTRAP_DUMP]} +link_erase_database = {'text': _(u'erase database'), 'view': 'erase_database_view', 'icon': 'radioactivity.png', 'permissions': [PERMISSION_NUKE_DATABASE]} diff --git a/apps/bootstrap/literals.py b/apps/bootstrap/literals.py new file mode 100644 index 0000000000..1161ba4417 --- /dev/null +++ b/apps/bootstrap/literals.py @@ -0,0 +1,30 @@ +from __future__ import absolute_import + +import re + +from django.utils.translation import ugettext_lazy as _ + +FIXTURE_TYPE_JSON = 'json' +FIXTURE_TYPE_YAML = 'yaml' +FIXTURE_TYPE_XML = 'xml' + +FIXTURE_TYPES_CHOICES = ( + (FIXTURE_TYPE_JSON, _(u'JSON')), + (FIXTURE_TYPE_YAML, _(u'YAML')), + # Disabing XML until a way to specify a null pk is found + #(FIXTURE_TYPE_XML, _(u'XML')), +) + +FIXTURE_FILE_TYPE = { + FIXTURE_TYPE_JSON: 'json', + FIXTURE_TYPE_YAML: 'yaml', + FIXTURE_TYPE_XML: 'xml', +} + +FIXTURE_TYPE_PK_NULLIFIER = { + FIXTURE_TYPE_JSON: lambda x: re.sub('"pk": [0-9]{1,5}', '"pk": null', x), + FIXTURE_TYPE_YAML: lambda x: re.sub('pk: [0-9]{1,5}', 'pk: null', x), + FIXTURE_TYPE_XML: lambda x: re.sub('pk="[0-9]{1,5}"', 'pk=null', x), +} + +COMMAND_LOADDATA = 'loaddata' diff --git a/apps/bootstrap/managers.py b/apps/bootstrap/managers.py new file mode 100644 index 0000000000..f52e068df9 --- /dev/null +++ b/apps/bootstrap/managers.py @@ -0,0 +1,36 @@ +from __future__ import absolute_import + +import logging + +#try: +# from cStringIO import StringIO +#except ImportError: +# from StringIO import StringIO + +from django.db import models +from django.core import serializers + +from .classes import BootstrapModel + +logger = logging.getLogger(__name__) + + +class BootstrapSetupManager(models.Manager): + def explode(self, data): + """ + Gets a compressed and compacted bootstrap setup and creates a new + database BootstrapSetup instance + """ + pass + + def dump(self, serialization_format): + result = [] + #models = [instance.get_fullname() + for bootstrap_model in BootstrapModel.get_all(): + #logger.debug('models: %s' % models) + #options = dict(indent=4, format=format, use_natural_keys=True, interactive=False, verbosity=0, stdout=result) + #management.call_command(COMMAND_DUMPDATA, *models, **options) + #logger.debug('result: %s' % result) + result.append(bootstrap_model.dump(serialization_format)) + #return result.read() + return '\n'.join(result) diff --git a/apps/bootstrap/migrations/0001_initial.py b/apps/bootstrap/migrations/0001_initial.py new file mode 100644 index 0000000000..74c18d7e81 --- /dev/null +++ b/apps/bootstrap/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'BootstrapSetup' + db.create_table('bootstrap_bootstrapsetup', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)), + ('description', self.gf('django.db.models.fields.TextField')(blank=True)), + ('fixture', self.gf('django.db.models.fields.TextField')()), + ('type', self.gf('django.db.models.fields.CharField')(max_length=16)), + )) + db.send_create_signal('bootstrap', ['BootstrapSetup']) + + + def backwards(self, orm): + # Deleting model 'BootstrapSetup' + db.delete_table('bootstrap_bootstrapsetup') + + + models = { + 'bootstrap.bootstrapsetup': { + 'Meta': {'ordering': "['name']", 'object_name': 'BootstrapSetup'}, + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'fixture': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + } + } + + complete_apps = ['bootstrap'] \ No newline at end of file diff --git a/apps/bootstrap/migrations/__init__.py b/apps/bootstrap/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/bootstrap/models.py b/apps/bootstrap/models.py new file mode 100644 index 0000000000..d34404ef07 --- /dev/null +++ b/apps/bootstrap/models.py @@ -0,0 +1,59 @@ +from __future__ import absolute_import + +import os +import tempfile + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.core import management + +from .literals import (FIXTURE_TYPES_CHOICES, FIXTURE_FILE_TYPE, COMMAND_LOADDATA) +from .managers import BootstrapSetupManager +from .classes import BootstrapModel + + +class BootstrapSetup(models.Model): + """ + Model to store the fixture for a pre configured setup. + """ + name = models.CharField(max_length=128, verbose_name=_(u'name'), unique=True) + description = models.TextField(verbose_name=_(u'description'), blank=True) + fixture = models.TextField(verbose_name=_(u'fixture'), help_text=_(u'These are the actual database structure creation instructions.')) + type = models.CharField(max_length=16, verbose_name=_(u'type'), choices=FIXTURE_TYPES_CHOICES) + + objects = BootstrapSetupManager() + + def __unicode__(self): + return self.name + + def get_extension(self): + return FIXTURE_FILE_TYPE[self.type] + + def execute(self): + BootstrapModel.check_for_data() + handle, filepath = tempfile.mkstemp() + # Just need the filepath, close the file description + os.close(handle) + + filepath = os.path.extsep.join([filepath, self.get_extension()]) + + with open(filepath, 'w') as file_handle: + file_handle.write(self.fixture) + + management.call_command(COMMAND_LOADDATA, filepath, verbosity=0) + os.unlink(filepath) + + def compress(self): + """ + Return a compacted and compressed version of the BootstrapSetup + instance, meant for download. + """ + return '' + + def save(self, *args, **kwargs): + return super(BootstrapSetup, self).save(*args, **kwargs) + + class Meta: + verbose_name = _(u'bootstrap setup') + verbose_name_plural = _(u'bootstrap setups') + ordering = ['name'] diff --git a/apps/bootstrap/permissions.py b/apps/bootstrap/permissions.py new file mode 100644 index 0000000000..f954e31c67 --- /dev/null +++ b/apps/bootstrap/permissions.py @@ -0,0 +1,15 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import PermissionNamespace, Permission + +namespace = PermissionNamespace('bootstrap', _(u'Database bootstrap')) + +PERMISSION_BOOTSTRAP_VIEW = Permission.objects.register(namespace, 'bootstrap_view', _(u'View bootstrap setups')) +PERMISSION_BOOTSTRAP_CREATE = Permission.objects.register(namespace, 'bootstrap_create', _(u'Create bootstrap setups')) +PERMISSION_BOOTSTRAP_EDIT = Permission.objects.register(namespace, 'bootstrap_edit', _(u'Edit bootstrap setups')) +PERMISSION_BOOTSTRAP_DELETE = Permission.objects.register(namespace, 'bootstrap_delete', _(u'Delete bootstrap setups')) +PERMISSION_BOOTSTRAP_EXECUTE = Permission.objects.register(namespace, 'bootstrap_execute', _(u'Execute bootstrap setups')) +PERMISSION_BOOTSTRAP_DUMP = Permission.objects.register(namespace, 'bootstrap_dump', _(u'Dump the current project\s setup into a bootstrap setup')) +PERMISSION_NUKE_DATABASE = Permission.objects.register(namespace, 'nuke_database', _(u'Erase the entire database and document storage')) diff --git a/apps/bootstrap/post_init.py b/apps/bootstrap/post_init.py new file mode 100644 index 0000000000..7894667e88 --- /dev/null +++ b/apps/bootstrap/post_init.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +from navigation.api import register_links + +from .links import (link_bootstrap_setup_create, link_bootstrap_setup_execute, + link_bootstrap_setup_list, link_bootstrap_setup_edit, link_bootstrap_setup_delete, + link_bootstrap_setup_view, link_bootstrap_setup_dump) +from .models import BootstrapSetup + +register_links([BootstrapSetup], [link_bootstrap_setup_view, link_bootstrap_setup_edit, link_bootstrap_setup_delete, link_bootstrap_setup_execute]) +register_links([BootstrapSetup], [link_bootstrap_setup_list, link_bootstrap_setup_create, link_bootstrap_setup_dump], menu_name='secondary_menu') +register_links(['bootstrap_setup_list', 'bootstrap_setup_create', 'bootstrap_setup_dump'], [link_bootstrap_setup_list, link_bootstrap_setup_create, link_bootstrap_setup_dump], menu_name='secondary_menu') diff --git a/apps/bootstrap/registry.py b/apps/bootstrap/registry.py new file mode 100644 index 0000000000..22fcdc0242 --- /dev/null +++ b/apps/bootstrap/registry.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from .links import link_bootstrap_setup_tool, link_erase_database + +label = _(u'Bootstrap') +description = _(u'Provides pre configured setups for indexes, document types, tags, etc.') +dependencies = ['app_registry', 'icons', 'navigation', 'documents', 'indexing', 'metadata', 'tags'] +setup_links = [link_bootstrap_setup_tool, link_erase_database] diff --git a/apps/bootstrap/static/images/icons/lightning.png b/apps/bootstrap/static/images/icons/lightning.png new file mode 100644 index 0000000000..df8dffc658 Binary files /dev/null and b/apps/bootstrap/static/images/icons/lightning.png differ diff --git a/apps/bootstrap/static/images/icons/lightning_go.png b/apps/bootstrap/static/images/icons/lightning_go.png new file mode 100644 index 0000000000..33833b4cf0 Binary files /dev/null and b/apps/bootstrap/static/images/icons/lightning_go.png differ diff --git a/apps/bootstrap/static/images/icons/radioactivity.png b/apps/bootstrap/static/images/icons/radioactivity.png new file mode 100644 index 0000000000..ad5d5348ff Binary files /dev/null and b/apps/bootstrap/static/images/icons/radioactivity.png differ diff --git a/apps/bootstrap/urls.py b/apps/bootstrap/urls.py new file mode 100644 index 0000000000..8a91228f4f --- /dev/null +++ b/apps/bootstrap/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('bootstrap.views', + url(r'^setup/list/$', 'bootstrap_setup_list', (), 'bootstrap_setup_list'), + url(r'^setup/create/$', 'bootstrap_setup_create', (), 'bootstrap_setup_create'), + url(r'^setup/(?P\d+)/edit/$', 'bootstrap_setup_edit', (), 'bootstrap_setup_edit'), + url(r'^setup/(?P\d+)/delete/$', 'bootstrap_setup_delete', (), 'bootstrap_setup_delete'), + url(r'^setup/(?P\d+)/$', 'bootstrap_setup_view', (), 'bootstrap_setup_view'), + url(r'^setup/(?P\d+)/execute/$', 'bootstrap_setup_execute', (), 'bootstrap_setup_execute'), + url(r'^setup/dump/$', 'bootstrap_setup_dump', (), 'bootstrap_setup_dump'), + url(r'^nuke/$', 'erase_database_view', (), 'erase_database_view'), +) diff --git a/apps/bootstrap/views.py b/apps/bootstrap/views.py new file mode 100644 index 0000000000..85c6c78283 --- /dev/null +++ b/apps/bootstrap/views.py @@ -0,0 +1,235 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response, get_object_or_404 +from django.template import RequestContext +from django.contrib import messages +from django.core.urlresolvers import reverse + +from permissions.models import Permission + +from .models import BootstrapSetup +from .classes import Cleanup, BootstrapModel +from .permissions import (PERMISSION_BOOTSTRAP_VIEW, PERMISSION_BOOTSTRAP_CREATE, + PERMISSION_BOOTSTRAP_EDIT, PERMISSION_BOOTSTRAP_DELETE, + PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_NUKE_DATABASE, PERMISSION_BOOTSTRAP_DUMP) +from .forms import BootstrapSetupForm, BootstrapSetupForm_view, BootstrapSetupForm_dump +from .exceptions import ExistingData + + +def bootstrap_setup_list(request): + Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_VIEW]) + + context = { + 'object_list': BootstrapSetup.objects.all(), + 'title': _(u'bootstrap setups'), + 'hide_link': True, + 'extra_columns': [ + {'name': _(u'description'), 'attribute': 'description'}, + {'name': _(u'type'), 'attribute': 'get_type_display'}, + ], + } + + return render_to_response('generic_list.html', context, + context_instance=RequestContext(request)) + + +def bootstrap_setup_create(request): + Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_CREATE]) + + if request.method == 'POST': + form = BootstrapSetupForm(request.POST) + if form.is_valid(): + bootstrap = form.save() + messages.success(request, _(u'Bootstrap created successfully')) + return HttpResponseRedirect(reverse('bootstrap_setup_list')) + else: + messages.error(request, _(u'Error creating bootstrap setup.')) + else: + form = BootstrapSetupForm() + + return render_to_response('generic_form.html', { + 'title': _(u'create bootstrap'), + 'form': form, + }, + context_instance=RequestContext(request)) + + +def bootstrap_setup_edit(request, bootstrap_setup_pk): + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + + bootstrap = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk) + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_BOOTSTRAP_EDIT, request.user, bootstrap) + + if request.method == 'POST': + form = BootstrapSetupForm(instance=bootstrap, data=request.POST) + if form.is_valid(): + form.save() + messages.success(request, _(u'Bootstrap setup edited successfully')) + return HttpResponseRedirect(previous) + else: + messages.error(request, _(u'Error editing bootstrap setup.')) + else: + form = BootstrapSetupForm(instance=bootstrap) + + return render_to_response('generic_form.html', { + 'title': _(u'edit bootstrap setup: %s') % bootstrap, + 'form': form, + 'object': bootstrap, + 'previous': previous, + 'object_name': _(u'bootstrap setup'), + }, + context_instance=RequestContext(request)) + + +def bootstrap_setup_delete(request, bootstrap_setup_pk): + bootstrap = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk) + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_DELETE]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_BOOTSTRAP_DELETE, request.user, bootstrap) + + post_action_redirect = reverse('bootstrap_setup_list') + + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/'))) + + if request.method == 'POST': + try: + bootstrap.delete() + messages.success(request, _(u'Bootstrap setup: %s deleted successfully.') % bootstrap) + except Exception, e: + messages.error(request, _(u'Bootstrap setup: %(bootstrap)s delete error: %(error)s') % { + 'bootstrap': bootstrap, 'error': e}) + + return HttpResponseRedirect(reverse('bootstrap_setup_list')) + + context = { + 'object_name': _(u'bootstrap setup'), + 'delete_view': True, + 'previous': previous, + 'next': next, + 'object': bootstrap, + 'title': _(u'Are you sure you with to delete the bootstrap setup: %s?') % bootstrap, + 'form_icon': icon_bootstrap_setup_delete, + } + + return render_to_response('generic_confirm.html', context, + context_instance=RequestContext(request)) + + +def bootstrap_setup_view(request, bootstrap_setup_pk): + bootstrap = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk) + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_VIEW]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_BOOTSTRAP_VIEW, request.user, bootstrap) + + form = BootstrapSetupForm_view(instance=bootstrap) + context = { + 'form': form, + 'object': bootstrap, + 'object_name': _(u'bootstrap setup'), + } + + return render_to_response('generic_detail.html', context, + context_instance=RequestContext(request)) + + +def bootstrap_setup_execute(request, bootstrap_setup_pk): + Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_EXECUTE]) + bootstrap_setup = get_object_or_404(BootstrapSetup, pk=bootstrap_setup_pk) + + post_action_redirect = reverse('bootstrap_setup_list') + + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/'))) + + if request.method == 'POST': + try: + bootstrap_setup.execute() + except ExistingData: + messages.error(request, _(u'Cannot execute bootstrap setup, there is existing data. Erase database and try again.')) + except Exception, exc: + messages.error(request, _(u'Error executing bootstrap setup; %s') % exc) + else: + messages.success(request, _(u'Bootstrap setup "%s" executed successfully.') % bootstrap_setup) + return HttpResponseRedirect(next) + + context = { + 'object_name': _(u'bootstrap setup'), + 'delete_view': False, + 'previous': previous, + 'next': next, + 'form_icon': 'lightning_go.png', + 'object': bootstrap_setup, + } + + context['title'] = _(u'Are you sure you wish to execute the database bootstrap named: %s?') % bootstrap_setup + + return render_to_response('generic_confirm.html', context, + context_instance=RequestContext(request)) + + +def bootstrap_setup_dump(request): + Permission.objects.check_permissions(request.user, [PERMISSION_BOOTSTRAP_DUMP]) + + if request.method == 'POST': + form = BootstrapSetupForm_dump(request.POST) + if form.is_valid(): + bootstrap = form.save(commit=False) + try: + bootstrap.fixture = BootstrapSetup.objects.dump(serialization_format=bootstrap.type) + except Exception as exception: + messages.error(request, _(u'Error dumping bootstrap setup; %s') % exception) + raise + else: + bootstrap.save() + messages.success(request, _(u'Bootstrap created successfully.')) + return HttpResponseRedirect(reverse('bootstrap_setup_list')) + else: + form = BootstrapSetupForm_dump() + + return render_to_response('generic_form.html', { + 'title': _(u'dump current setup into a bootstrap setup'), + 'form': form, + }, + context_instance=RequestContext(request)) + + +def erase_database_view(request): + Permission.objects.check_permissions(request.user, [PERMISSION_NUKE_DATABASE]) + + post_action_redirect = None + + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/'))) + + if request.method == 'POST': + try: + Cleanup.execute_all() + except Exception, exc: + messages.error(request, _(u'Error erasing database; %s') % exc) + else: + messages.success(request, _(u'Database erased successfully.')) + return HttpResponseRedirect(next) + + context = { + 'delete_view': False, + 'previous': previous, + 'next': next, + 'form_icon': 'radioactivity.png', + } + + context['title'] = _(u'Are you sure you wish to erase the entire database and document storage?') + context['message'] = _(u'All documents, sources, metadata, metadata types, set, tags, indexes and logs will be lost irreversibly!') + + return render_to_response('generic_confirm.html', context, + context_instance=RequestContext(request)) diff --git a/settings.py b/settings.py index a7a3475949..50b02f3091 100644 --- a/settings.py +++ b/settings.py @@ -103,6 +103,7 @@ MIDDLEWARE_CLASSES = ( 'common.middleware.strip_spaces_widdleware.SpacelessMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.transaction.TransactionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -164,6 +165,7 @@ INSTALLED_APPS = ( 'installation', # Mayan EDMS 'storage', + 'app_registry', 'folders', 'tags', 'document_comments', @@ -179,7 +181,7 @@ INSTALLED_APPS = ( 'rest_api', 'document_signatures', 'checkouts', - + 'bootstrap', # Has to be last so the other apps can register it's signals 'signaler', ) diff --git a/urls.py b/urls.py index 79bc1fab54..b2d5ccd4f3 100644 --- a/urls.py +++ b/urls.py @@ -34,6 +34,7 @@ urlpatterns = patterns('', (r'^checkouts/', include('checkouts.urls')), (r'^installation/', include('installation.urls')), (r'^scheduler/', include('scheduler.urls')), + (r'^bootstrap/', include('bootstrap.urls')), )