Merge branch 'feature/icon_server' into development
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from navigation.api import bind_links, register_multi_item_links
|
||||
from project_setup.api import register_setup
|
||||
|
||||
from .classes import (AccessHolder, AccessObjectClass, ClassAccessHolder,
|
||||
AccessObject)
|
||||
@@ -17,4 +16,3 @@ bind_links(['acl_setup_valid_classes', 'acl_class_acl_list', 'acl_class_new_hold
|
||||
bind_links([ClassAccessHolder], [acl_class_acl_detail])
|
||||
bind_links([AccessObjectClass], [acl_class_acl_list, acl_class_new_holder_for])
|
||||
register_multi_item_links(['acl_class_acl_detail'], [acl_class_grant, acl_class_revoke])
|
||||
register_setup(acl_setup_valid_classes)
|
||||
|
||||
11
apps/acls/icons.py
Normal file
11
apps/acls/icons.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from icons.literals import KEY, KEY_GO, KEY_ADD, KEY_DELETE, LOCK, USER
|
||||
from icons import Icon
|
||||
|
||||
icon_acls = Icon(KEY)
|
||||
icon_acl_detail = Icon(KEY_GO)
|
||||
icon_acl_grant = Icon(KEY_ADD)
|
||||
icon_acl_revoke = Icon(KEY_DELETE)
|
||||
icon_acl_holder_new = Icon(USER)
|
||||
icon_acl_app = Icon(LOCK)
|
||||
@@ -6,14 +6,15 @@ from navigation.api import Link
|
||||
|
||||
from .permissions import (ACLS_EDIT_ACL, ACLS_VIEW_ACL,
|
||||
ACLS_CLASS_EDIT_ACL, ACLS_CLASS_VIEW_ACL)
|
||||
from .icons import icon_acls, icon_acl_detail, icon_acl_grant, icon_acl_revoke, icon_acl_holder_new
|
||||
|
||||
acl_list = Link(text=_(u'ACLs'), view='acl_list', sprite='lock', permissions=[ACLS_VIEW_ACL])
|
||||
acl_detail = Link(text=_(u'details'), view='acl_detail', args=['access_object.gid', 'object.gid'], sprite='key_go', permissions=[ACLS_VIEW_ACL])
|
||||
acl_grant = Link(text=_(u'grant'), view='acl_multiple_grant', sprite='key_add', permissions=[ACLS_EDIT_ACL])
|
||||
acl_revoke = Link(text=_(u'revoke'), view='acl_multiple_revoke', sprite='key_delete', permissions=[ACLS_EDIT_ACL])
|
||||
acl_holder_new = Link(text=_(u'New holder'), view='acl_holder_new', args='access_object.gid', sprite='user', permissions=[ACLS_EDIT_ACL])
|
||||
acl_list = Link(text=_(u'ACLs'), view='acl_list', icon=icon_acls, permissions=[ACLS_VIEW_ACL])
|
||||
acl_detail = Link(text=_(u'details'), view='acl_detail', args=['access_object.gid', 'object.gid'], icon=icon_acl_detail, permissions=[ACLS_VIEW_ACL])
|
||||
acl_grant = Link(text=_(u'grant'), view='acl_multiple_grant', icon=icon_acl_grant, permissions=[ACLS_EDIT_ACL])
|
||||
acl_revoke = Link(text=_(u'revoke'), view='acl_multiple_revoke', icon=icon_acl_revoke, permissions=[ACLS_EDIT_ACL])
|
||||
acl_holder_new = Link(text=_(u'New holder'), view='acl_holder_new', args='access_object.gid', icon=icon_acl_holder_new, permissions=[ACLS_EDIT_ACL])
|
||||
|
||||
acl_setup_valid_classes = Link(text=_(u'Default ACLs'), view='acl_setup_valid_classes', icon='lock.png', permissions=[ACLS_CLASS_VIEW_ACL]) # 'children_view_regex=[r'^acl_class', r'^acl_setup']}
|
||||
acl_setup_valid_classes = Link(text=_(u'Default ACLs'), view='acl_setup_valid_classes', icon=icon_acls, permissions=[ACLS_CLASS_VIEW_ACL]) # 'children_view_regex=[r'^acl_class', r'^acl_setup']}
|
||||
acl_class_list = Link(text=_(u'List of classes'), view='acl_setup_valid_classes', sprite='package', permissions=[ACLS_CLASS_VIEW_ACL])
|
||||
acl_class_acl_list = Link(text=_(u'ACLs for class'), view='acl_class_acl_list', args='object.gid', sprite='lock_go', permissions=[ACLS_CLASS_VIEW_ACL])
|
||||
acl_class_acl_detail = Link(text=_(u'details'), view='acl_class_acl_detail', args=['access_object_class.gid', 'object.gid'], sprite='key_go', permissions=[ACLS_CLASS_VIEW_ACL])
|
||||
|
||||
13
apps/acls/registry.py
Normal file
13
apps/acls/registry.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .icons import icon_acl_app
|
||||
from .links import acl_setup_valid_classes
|
||||
|
||||
name = 'acls'
|
||||
label = _(u'ACL')
|
||||
description = _(u'Handles object level access control.')
|
||||
icon = icon_acl_app
|
||||
setup_links = [acl_setup_valid_classes]
|
||||
dependencies = ['app_registry', 'permissions', 'navigation']
|
||||
@@ -1,54 +1,32 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import inspect
|
||||
#import runpy
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction, DatabaseError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from common.utils import encapsulate
|
||||
from icons.literals import APP, BACKUPS
|
||||
from job_processor.exceptions import JobQueuePushError
|
||||
from job_processor.models import JobQueue, JobType
|
||||
from project_tools.api import register_tool
|
||||
from project_setup.api import register_setup
|
||||
from navigation.api import bind_links, register_model_list_columns
|
||||
from .models import App
|
||||
|
||||
from .classes import AppBackup, ModelBackup
|
||||
from .links import (app_registry_tool_link, app_list, backup_tool_link,
|
||||
restore_tool_link, backup_job_list, backup_job_create, backup_job_edit,
|
||||
backup_job_test)
|
||||
from .literals import BACKUP_JOB_QUEUE_NAME
|
||||
from .models import App, BackupJob
|
||||
#from navigation.api import bind_links, register_model_list_columns
|
||||
|
||||
#from .links import (app_registry_tool_link, app_list, backup_tool_link,
|
||||
# restore_tool_link, backup_job_list, backup_job_create, backup_job_edit,
|
||||
# backup_job_test)
|
||||
|
||||
@transaction.commit_on_success
|
||||
def create_backups_job_queue():
|
||||
global backups_job_queue
|
||||
#bind_links(['app_list'], [app_list], menu_name='secondary_menu')
|
||||
###app.set_backup([ModelBackup()])
|
||||
|
||||
for app_name in settings.INSTALLED_APPS:
|
||||
App.register(app_name)
|
||||
try:
|
||||
backups_job_queue, created = JobQueue.objects.get_or_create(name=BACKUP_JOB_QUEUE_NAME, defaults={'label': _('Backups'), 'unique_jobs': True})
|
||||
except DatabaseError:
|
||||
transaction.rollback()
|
||||
|
||||
|
||||
register_tool(app_registry_tool_link)
|
||||
bind_links(['app_list'], [app_list], menu_name='secondary_menu')
|
||||
|
||||
create_backups_job_queue()
|
||||
#backup_job_type = JobType('remote_backup', _(u'Remove backup'), do_backup)
|
||||
|
||||
register_setup(backup_tool_link)
|
||||
register_tool(restore_tool_link)
|
||||
bind_links([BackupJob, 'backup_job_list', 'backup_job_create'], [backup_job_list], menu_name='secondary_menu')
|
||||
bind_links([BackupJob, 'backup_job_list', 'backup_job_create'], [backup_job_create], menu_name='sidebar')
|
||||
bind_links([BackupJob], [backup_job_edit, backup_job_test])
|
||||
|
||||
register_model_list_columns(BackupJob, [
|
||||
{'name':_(u'begin date time'), 'attribute': 'begin_datetime'},
|
||||
{'name':_(u'storage module'), 'attribute': 'storage_module.label'},
|
||||
{'name':_(u'apps'), 'attribute': encapsulate(lambda x: u', '.join([unicode(app) for app in x.apps.all()]))},
|
||||
])
|
||||
|
||||
try:
|
||||
app = App.register('app_registry', label=_(u'App registry'), icon=APP, description=_(u'Holds the app registry and backups functions.'))
|
||||
except App.UnableToRegister:
|
||||
pass
|
||||
else:
|
||||
app.set_backup([ModelBackup()])
|
||||
post_init = import_module('%s.post_init' % app_name)
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
if post_init:
|
||||
for name, value in inspect.getmembers(post_init):
|
||||
if hasattr(value, '__call__') and name.startswith('init'):
|
||||
value()
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django import forms
|
||||
|
||||
from common.widgets import ScrollableCheckboxSelectMultiple
|
||||
|
||||
from .classes import AppBackup
|
||||
from .models import App, BackupJob
|
||||
|
||||
|
||||
def valid_app_choices():
|
||||
# Return app that exist in the app registry and that have been registered for backup
|
||||
return App.live.filter(pk__in=[appbackup.app.pk for appbackup in AppBackup.get_all()])
|
||||
|
||||
|
||||
class BackupJobForm(forms.ModelForm):
|
||||
apps = forms.ModelMultipleChoiceField(queryset=valid_app_choices(), widget=ScrollableCheckboxSelectMultiple())
|
||||
|
||||
class Meta:
|
||||
model = BackupJob
|
||||
6
apps/app_registry/icons.py
Normal file
6
apps/app_registry/icons.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from icons.literals import PLUGIN
|
||||
from icons import Icon
|
||||
|
||||
icon_app = Icon(PLUGIN)
|
||||
@@ -3,19 +3,8 @@ from __future__ import absolute_import
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation.api import Link
|
||||
from icons.api import get_icon_name, get_sprite_name
|
||||
from icons.literals import APP
|
||||
|
||||
from .permissions import PERMISSION_BACKUP_JOB_VIEW, PERMISSION_BACKUP_JOB_CREATE, PERMISSION_BACKUP_JOB_EDIT, PERMISSION_BACKUP_JOB_DELETE
|
||||
from .icons import icon_app
|
||||
|
||||
app_registry_tool_link = Link(text=_(u'Apps'), view='app_list', icon=get_icon_name(APP))#, permissions=[PERMISSION_BACKUP_JOB_VIEW])
|
||||
app_list = Link(text=_(u'app list'), view='app_list', sprite=get_sprite_name(APP))#, permissions=[PERMISSION_BACKUP_JOB_VIEW])
|
||||
|
||||
backup_tool_link = Link(text=_(u'backups'), view='backup_job_list', icon='cd_burn.png', permissions=[PERMISSION_BACKUP_JOB_VIEW])
|
||||
backup_job_list = Link(text=_(u'backup job list'), view='backup_job_list', sprite='cd_burn', permissions=[PERMISSION_BACKUP_JOB_VIEW])
|
||||
backup_job_create = Link(text=_(u'create'), view='backup_job_create', sprite='cd_add', permissions=[PERMISSION_BACKUP_JOB_CREATE])
|
||||
backup_job_edit = Link(text=_(u'edit'), view='backup_job_edit', args='object.pk', sprite='cd_edit', permissions=[PERMISSION_BACKUP_JOB_EDIT])
|
||||
backup_job_test = Link(text=_(u'test'), view='backup_job_test', args='object.pk', sprite='cd_go')#, permissions=[PERMISSION_BACKUP_JOB_TEST])
|
||||
backup_job_delete = Link(text=_(u'delete'), view='backup_job_delete', args='object.pk', sprite='cd_delete', permissions=[PERMISSION_BACKUP_JOB_DELETE])
|
||||
|
||||
restore_tool_link = Link(text=_(u'restore'), view='restore_view', icon='cd_eject.png')#, permissions=[])
|
||||
app_registry_tool_link = Link(text=_(u'Apps'), view='app_list', icon=icon_app)#, permissions=[PERMISSION_BACKUP_JOB_VIEW])
|
||||
app_list = Link(text=_(u'app list'), view='app_list', sprite=icon_app)#, permissions=[PERMISSION_BACKUP_JOB_VIEW])
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Deleting model 'BackupJob'
|
||||
db.delete_table('app_registry_backupjob')
|
||||
|
||||
# Removing M2M table for field apps on 'BackupJob'
|
||||
db.delete_table('app_registry_backupjob_apps')
|
||||
|
||||
# Deleting field 'App.icon'
|
||||
db.delete_column('app_registry_app', 'icon')
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Adding model 'BackupJob'
|
||||
db.create_table('app_registry_backupjob', (
|
||||
('storage_arguments_json', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=64)),
|
||||
('storage_module_name', self.gf('django.db.models.fields.CharField')(max_length=32)),
|
||||
('begin_datetime', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2012, 8, 18, 0, 0))),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
))
|
||||
db.send_create_signal('app_registry', ['BackupJob'])
|
||||
|
||||
# Adding M2M table for field apps on 'BackupJob'
|
||||
db.create_table('app_registry_backupjob_apps', (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('backupjob', models.ForeignKey(orm['app_registry.backupjob'], null=False)),
|
||||
('app', models.ForeignKey(orm['app_registry.app'], null=False))
|
||||
))
|
||||
db.create_unique('app_registry_backupjob_apps', ['backupjob_id', 'app_id'])
|
||||
|
||||
# Adding field 'App.icon'
|
||||
db.add_column('app_registry_app', 'icon',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=64, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
models = {
|
||||
'app_registry.app': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'App'},
|
||||
'dependencies': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['app_registry.App']", 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['app_registry']
|
||||
@@ -2,6 +2,8 @@ from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import imp
|
||||
import sys
|
||||
|
||||
from django.db import models
|
||||
from django.db import DatabaseError, transaction
|
||||
@@ -9,22 +11,26 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from common.models import TranslatableLabelMixin, LiveObjectMixin
|
||||
from smart_settings import SettingsNamespace
|
||||
from project_setup.api import register_setup
|
||||
from project_tools.api import register_tool
|
||||
from statistics.api import register_statistics
|
||||
|
||||
from .classes import AppBackup, StorageModuleBase
|
||||
#from .classes import AppBackup, StorageModuleBase, Setting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class App(TranslatableLabelMixin, LiveObjectMixin, models.Model):
|
||||
translatables = ['label', 'description']
|
||||
translatables = ['label', 'description', 'icon']
|
||||
|
||||
class UnableToRegister(Exception):
|
||||
pass
|
||||
#class UnableToRegister(Exception):
|
||||
# pass
|
||||
|
||||
name = models.CharField(max_length=64, verbose_name=_(u'name'), unique=True)
|
||||
icon = models.CharField(max_length=64, verbose_name=_(u'icon'), blank=True)
|
||||
dependencies = models.ManyToManyField('self', verbose_name=_(u'dependencies'), symmetrical=False, blank=True, null=True)
|
||||
#version
|
||||
#top_urls
|
||||
@@ -32,30 +38,64 @@ class App(TranslatableLabelMixin, LiveObjectMixin, models.Model):
|
||||
|
||||
@classmethod
|
||||
@transaction.commit_on_success
|
||||
def register(cls, name, label, icon=None, description=None):
|
||||
def register(cls, app_name):
|
||||
logger.debug('Trying to import: %s' % app_name)
|
||||
try:
|
||||
app, created = App.objects.get_or_create(name=name)
|
||||
except DatabaseError:
|
||||
transaction.rollback()
|
||||
raise cls.UnableToRegister
|
||||
app_module = import_module(app_name)
|
||||
except ImportError:
|
||||
transaction.rollback
|
||||
logger.debug('import failed')
|
||||
else:
|
||||
app.label = label
|
||||
if icon:
|
||||
app.icon = icon
|
||||
if description:
|
||||
app.description = description
|
||||
app.dependencies.clear()
|
||||
app.save()
|
||||
return app
|
||||
|
||||
def set_dependencies(self, app_names):
|
||||
for app_name in app_names:
|
||||
app = App.objects.get(name=app_name)
|
||||
self.dependencies.add(app)
|
||||
|
||||
def set_backup(self, *args, **kwargs):
|
||||
return AppBackup(self, *args, **kwargs)
|
||||
|
||||
logger.debug('Trying to import app\'s registry')
|
||||
try:
|
||||
registration = import_module('%s.registry' % app_name)
|
||||
except ImportError as exception:
|
||||
transaction.rollback
|
||||
logger.debug('import failed; %s' % exception)
|
||||
else:
|
||||
if not getattr(registration, 'disabled', False):
|
||||
try:
|
||||
app, created = App.objects.get_or_create(name=app_name)
|
||||
except DatabaseError:
|
||||
transaction.rollback()
|
||||
raise cls.UnableToRegister
|
||||
else:
|
||||
app.label = getattr(registration, 'label', app_name)
|
||||
app.description = getattr(registration, 'description', u'')
|
||||
app.dependencies.clear()
|
||||
app.save()
|
||||
app.icon = getattr(registration, 'icon', None)
|
||||
|
||||
for dependency_name in getattr(registration, 'dependencies', []):
|
||||
dependency = App.objects.get(name=dependency_name)
|
||||
app.dependencies.add(dependency)
|
||||
|
||||
settings = getattr(registration, 'settings', None)
|
||||
|
||||
if settings:
|
||||
logger.debug('settings: %s' % settings)
|
||||
settings_module = imp.new_module('settings')
|
||||
setattr(app_module, 'settings', settings_module)
|
||||
sys.modules['%s.settings' % app_name] = settings_module
|
||||
settings_namespace = SettingsNamespace(app_name, app.label, '%s.settings' % app_name)
|
||||
for setting in settings:
|
||||
settings_namespace.add_setting(**setting)
|
||||
|
||||
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 statistic in getattr(registration, 'statistics', []):
|
||||
logger.debug('stattistic: %s' % statistic)
|
||||
register_statistics(statistic)
|
||||
|
||||
#def set_backup(self, *args, **kwargs):
|
||||
# return AppBackup(self, *args, **kwargs)
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.label)
|
||||
|
||||
@@ -65,55 +105,3 @@ class App(TranslatableLabelMixin, LiveObjectMixin, models.Model):
|
||||
verbose_name_plural = _(u'apps')
|
||||
|
||||
|
||||
class BackupJob(models.Model):
|
||||
name = models.CharField(max_length=64, verbose_name=_(u'name'))
|
||||
enabled = models.BooleanField(default=True, verbose_name=_(u'enabled'))
|
||||
apps = models.ManyToManyField(App)
|
||||
begin_datetime = models.DateTimeField(verbose_name=_(u'begin date and time'), default=lambda: datetime.datetime.now())
|
||||
|
||||
# * repetition =
|
||||
# day - 1 days
|
||||
# weekly - days of week checkbox
|
||||
# month - day of month, day of week
|
||||
# * repetition option field
|
||||
# * ends
|
||||
# - never
|
||||
# - After # ocurrences
|
||||
# - On date
|
||||
# * end option field
|
||||
# * type
|
||||
# - Full
|
||||
# - Incremental
|
||||
storage_module_name = models.CharField(max_length=32, choices=StorageModuleBase.get_as_choices(), verbose_name=_(u'storage module'))
|
||||
storage_arguments_json = models.TextField(verbose_name=_(u'storage module arguments (in JSON)'), blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def storage_module(self):
|
||||
return StorageModuleBase.get(self.storage_module_name)
|
||||
|
||||
def backup(self, dry_run=False):
|
||||
logger.debug('starting: %s', self)
|
||||
logger.debug('dry_run: %s' % dry_run)
|
||||
storage_module = self.storage_module
|
||||
#TODO: loads
|
||||
for app in self.apps.all():
|
||||
app_backup = AppBackup.get(app)
|
||||
app_backup.backup(storage_module(backup_path='/tmp'), dry_run=dry_run)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
#dump
|
||||
super(BackupJob, self).save(*args, **kwargs)
|
||||
|
||||
@models.permalink
|
||||
def get_absolute_url(self):
|
||||
return ('checkout_info', [self.document.pk])
|
||||
|
||||
class Meta:
|
||||
verbose_name = _(u'document checkout')
|
||||
verbose_name_plural = _(u'document checkouts')
|
||||
|
||||
|
||||
#class BackupJobLog
|
||||
|
||||
13
apps/app_registry/registry.py
Normal file
13
apps/app_registry/registry.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .icons import icon_app
|
||||
from .links import app_registry_tool_link
|
||||
|
||||
name = 'app_registry'
|
||||
label = _(u'App registry')
|
||||
description = _(u'Handles the registration of apps in a project.')
|
||||
icon = icon_app
|
||||
tool_links = [app_registry_tool_link]
|
||||
dependencies = ['navigation']
|
||||
@@ -2,9 +2,4 @@ from django.conf.urls.defaults import patterns, url
|
||||
|
||||
urlpatterns = patterns('app_registry.views',
|
||||
url(r'^list/$', 'app_list', (), 'app_list'),
|
||||
url(r'^jobs/list/$', 'backup_job_list', (), 'backup_job_list'),
|
||||
url(r'^jobs/create/$', 'backup_job_create', (), 'backup_job_create'),
|
||||
url(r'^jobs/(?P<backup_job_pk>\d+)/edit/$', 'backup_job_edit', (), 'backup_job_edit'),
|
||||
url(r'^jobs/(?P<backup_job_pk>\d+)/test/$', 'backup_job_test', (), 'backup_job_test'),
|
||||
#url(r'^jobs/(?P<backup_job_pk>\d+)/delete/$', 'backup_job_delete', (), 'backup_job_delete'),
|
||||
)
|
||||
|
||||
@@ -9,14 +9,10 @@ from django.template import RequestContext
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from common.utils import encapsulate
|
||||
from icons.widgets import icon_widget
|
||||
from icons.literals import APP
|
||||
from permissions.models import Permission
|
||||
|
||||
from .classes import AppBackup
|
||||
from .forms import BackupJobForm
|
||||
from .models import App, BackupJob
|
||||
from .permissions import PERMISSION_BACKUP_JOB_VIEW, PERMISSION_BACKUP_JOB_CREATE, PERMISSION_BACKUP_JOB_EDIT
|
||||
from .models import App
|
||||
from .icons import icon_app
|
||||
|
||||
|
||||
def app_list(request):
|
||||
@@ -25,120 +21,11 @@ def app_list(request):
|
||||
return render_to_response('generic_list.html', {
|
||||
'object_list' : App.live.all(),
|
||||
'hide_object': True,
|
||||
'title': _(u'registered apps'),
|
||||
'extra_columns': [
|
||||
{'name': _(u'icon'), 'attribute': 'icon'},
|
||||
{'name':_(u'icon'), 'attribute': encapsulate(lambda x: icon_widget(x.icon or APP))},
|
||||
{'name': _(u'label'), 'attribute': 'label'},
|
||||
{'name':_(u'icon'), 'attribute': encapsulate(lambda x: (getattr(x, 'icon') or icon_app).display_big())},
|
||||
{'name':_(u'description'), 'attribute': 'description'},
|
||||
{'name':_(u'dependencies'), 'attribute': encapsulate(lambda x: u', '.join([unicode(dependency) for dependency in x.dependencies.all()]))},
|
||||
],
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def backup_job_list(request):
|
||||
pre_object_list = BackupJob.objects.all()
|
||||
|
||||
try:
|
||||
Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_VIEW])
|
||||
except PermissionDenied:
|
||||
# If user doesn't have global permission, get a list of backup jobs
|
||||
# for which he/she does have access use it to filter the
|
||||
# provided object_list
|
||||
final_object_list = AccessEntry.objects.filter_objects_by_access(PERMISSION_BACKUP_JOB_VIEW, request.user, pre_object_list)
|
||||
else:
|
||||
final_object_list = pre_object_list
|
||||
|
||||
context = {
|
||||
'object_list': final_object_list,
|
||||
'title': _(u'backup jobs'),
|
||||
'hide_link': True,
|
||||
#'extra_columns': [
|
||||
# {'name': _(u'info'), 'attribute': 'info'},
|
||||
#],
|
||||
}
|
||||
return render_to_response('generic_list.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def backup_job_create(request):
|
||||
Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_CREATE])
|
||||
|
||||
if request.method == 'POST':
|
||||
form = BackupJobForm(data=request.POST)
|
||||
if form.is_valid():
|
||||
try:
|
||||
backup_job = form.save()
|
||||
except Exception, exc:
|
||||
messages.error(request, _(u'Error creating backup job; %s') % exc)
|
||||
else:
|
||||
messages.success(request, _(u'Backup job "%s" created successfully.') % backup_job)
|
||||
return HttpResponseRedirect(reverse('backup_job_list'))
|
||||
else:
|
||||
form = BackupJobForm()
|
||||
|
||||
return render_to_response('generic_form.html', {
|
||||
'form': form,
|
||||
'title': _(u'Create backup job')
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def backup_job_edit(request, backup_job_pk):
|
||||
backup_job = get_object_or_404(BackupJob, pk=backup_job_pk)
|
||||
try:
|
||||
Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_EDIT])
|
||||
except PermissionDenied:
|
||||
AccessEntry.objects.check_access(PERMISSION_BACKUP_JOB_EDIT, request.user, backup_job)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = BackupJobForm(data=request.POST, instance=backup_job)
|
||||
if form.is_valid():
|
||||
try:
|
||||
backup_job = form.save()
|
||||
except Exception, exc:
|
||||
messages.error(request, _(u'Error editing backup job; %s') % exc)
|
||||
else:
|
||||
messages.success(request, _(u'Backup job "%s" edited successfully.') % backup_job)
|
||||
return HttpResponseRedirect(reverse('backup_job_list'))
|
||||
else:
|
||||
form = BackupJobForm(instance=backup_job)
|
||||
|
||||
return render_to_response('generic_form.html', {
|
||||
'form': form,
|
||||
'object': backup_job,
|
||||
'title': _(u'Edit backup job: %s') % backup_job
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def backup_job_test(request, backup_job_pk):
|
||||
backup_job = get_object_or_404(BackupJob, pk=backup_job_pk)
|
||||
#try:
|
||||
# Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_EDIT])
|
||||
#except PermissionDenied:
|
||||
# AccessEntry.objects.check_access(PERMISSION_BACKUP_JOB_EDIT, request.user, backup_job)
|
||||
|
||||
try:
|
||||
backup_job.backup(dry_run=True)
|
||||
except Exception, exc:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
else:
|
||||
messages.error(request, _(u'Error testing backup job; %s') % exc)
|
||||
return HttpResponseRedirect(reverse('backup_job_list'))
|
||||
else:
|
||||
messages.success(request, _(u'Test for backup job "%s" finished successfully.') % backup_job)
|
||||
return HttpResponseRedirect(reverse('backup_job_list'))
|
||||
|
||||
|
||||
def backup_view(request):
|
||||
#Permission.objects.check_permissions(request.user, [])
|
||||
|
||||
context = {
|
||||
'object_list': AppBackup.get_all(),
|
||||
'title': _(u'registered apps for backup'),
|
||||
'hide_link': True,
|
||||
'extra_columns': [
|
||||
{'name': _(u'info'), 'attribute': 'info'},
|
||||
],
|
||||
}
|
||||
return render_to_response('generic_list.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
63
apps/backups/__init__.py
Normal file
63
apps/backups/__init__.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import inspect
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction, DatabaseError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
#from common.utils import encapsulate
|
||||
#from job_processor.exceptions import JobQueuePushError
|
||||
#from job_processor.models import JobQueue, JobType
|
||||
#from project_tools.api import register_tool
|
||||
#from project_setup.api import register_setup
|
||||
#from navigation.api import bind_links, register_model_list_columns
|
||||
|
||||
#from .classes import AppBackup, ModelBackup
|
||||
|
||||
#from .links import (app_registry_tool_link, app_list, backup_tool_link,
|
||||
# restore_tool_link, backup_job_list, backup_job_create, backup_job_edit,
|
||||
# backup_job_test)
|
||||
#from .literals import BACKUP_JOB_QUEUE_NAME
|
||||
#from .models import App
|
||||
#from . import models
|
||||
|
||||
#class UnableToRegister(Exception):
|
||||
# pass
|
||||
|
||||
#apipkg.initpkg(__name__, {
|
||||
# #'App': _App,
|
||||
## 'App': 'app_registry.models:App',
|
||||
# #'App': models.App
|
||||
#})
|
||||
#pp = 1
|
||||
#from .models import App#as _App#, BackupJob as _BackupJob
|
||||
|
||||
#@transaction.commit_on_success
|
||||
#def create_backups_job_queue():
|
||||
# global backups_job_queue
|
||||
# try:
|
||||
# backups_job_queue, created = JobQueue.objects.get_or_create(name=BACKUP_JOB_QUEUE_NAME, defaults={'label': _('Backups'), 'unique_jobs': True})
|
||||
# except DatabaseError:
|
||||
# transaction.rollback()
|
||||
|
||||
|
||||
#bind_links(['app_list'], [app_list], menu_name='secondary_menu')
|
||||
|
||||
#create_backups_job_queue()
|
||||
###backup_job_type = JobType('remote_backup', _(u'Remove backup'), do_backup)
|
||||
|
||||
#register_setup(backup_tool_link)
|
||||
#register_tool(restore_tool_link)
|
||||
#bind_links([BackupJob, 'backup_job_list', 'backup_job_create'], [backup_job_list], menu_name='secondary_menu')
|
||||
#bind_links([BackupJob, 'backup_job_list', 'backup_job_create'], [backup_job_create], menu_name='sidebar')
|
||||
#bind_links([BackupJob], [backup_job_edit, backup_job_test])
|
||||
|
||||
#register_model_list_columns(BackupJob, [
|
||||
# {'name':_(u'begin date time'), 'attribute': 'begin_datetime'},
|
||||
# {'name':_(u'storage module'), 'attribute': 'storage_module.label'},
|
||||
# {'name':_(u'apps'), 'attribute': encapsulate(lambda x: u', '.join([unicode(app) for app in x.apps.all()]))},
|
||||
#])
|
||||
|
||||
###app.set_backup([ModelBackup()])
|
||||
@@ -4,9 +4,11 @@ import os
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.core.management.commands.dumpdata import Command
|
||||
from django.conf import settings
|
||||
from django.db import router, DEFAULT_DB_ALIAS
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -156,145 +158,4 @@ class AppBackup(object):
|
||||
results.append(u'%s - %s' % (manager, manager.info() or _(u'Nothing')))
|
||||
return u', '.join(results)
|
||||
|
||||
def backup(self, storage_module, dry_run=False):
|
||||
logger.debug('starting')
|
||||
|
||||
self.state = self.__class__.STATE_BACKING_UP
|
||||
for manager in self.backup_managers:
|
||||
result = manager.backup()
|
||||
storage_module.backup(result, dry_run=dry_run)
|
||||
self.state = self.__class__.STATE_IDLE
|
||||
|
||||
def restore(self, storage_module=None):
|
||||
logger.debug('starting')
|
||||
self.state = self.__class__.STATE_RESTORING
|
||||
for manager in self.backup_managers:
|
||||
manager.restore(storage_module.restore())
|
||||
self.state = self.__class__.STATE_IDLE
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.app)
|
||||
|
||||
|
||||
#Storage
|
||||
class StorageModuleBase(object):
|
||||
_registry = {}
|
||||
|
||||
# Local modules depend on hardware on a node and execute in the Scheduler
|
||||
# of a particular node
|
||||
REALM_LOCAL = 'local'
|
||||
|
||||
# Remote modules can be execute by any node in a cluster and are placed
|
||||
# in the JobQueue
|
||||
REALM_REMOTE = 'remote'
|
||||
|
||||
REALM_CHOICES = (
|
||||
(REALM_LOCAL, _(u'local')),
|
||||
(REALM_REMOTE, _(u'remote')),
|
||||
)
|
||||
|
||||
class UnknownStorageModule(Exception):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def register(cls, klass):
|
||||
"""
|
||||
Register a subclass of StorageModuleBase to make it available to the
|
||||
UI
|
||||
"""
|
||||
cls._registry[klass.name] = klass
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
return cls._registry.values()
|
||||
|
||||
@classmethod
|
||||
def get(cls, name):
|
||||
try:
|
||||
return cls._registry[name]
|
||||
except KeyError:
|
||||
raise cls.UnknownStorageModule
|
||||
|
||||
@classmethod
|
||||
def get_as_choices(cls):
|
||||
return [(name, unicode(klass.label)) for name, klass in cls._registry.items()]
|
||||
|
||||
def get_arguments(self):
|
||||
return []
|
||||
|
||||
def is_local_realm(self):
|
||||
return self.realm == REALM_LOCAL
|
||||
|
||||
def is_remote_realm(self):
|
||||
return self.realm == REALM_REMOTE
|
||||
|
||||
def backup(self, data, dry_run):
|
||||
raise NotImplemented
|
||||
|
||||
def restore(self):
|
||||
"""
|
||||
Must return data or a file like object
|
||||
"""
|
||||
raise NotImplemented
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.label)
|
||||
|
||||
|
||||
class TestStorage(StorageModuleBase):
|
||||
name = 'test_storage'
|
||||
label = _(u'Test storage module')
|
||||
realm = StorageModuleBase.REALM_LOCAL
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.backup_path = kwargs.pop('backup_path', None)
|
||||
self.restore_path = kwargs.pop('restore_path', None)
|
||||
|
||||
def get_arguments(self):
|
||||
return ['backup_path', 'restore_path']
|
||||
|
||||
def backup(self, elements, dry_run):
|
||||
logger.debug('self.backup_path: %s' % self.backup_path)
|
||||
|
||||
for element in elements:
|
||||
content_file = element.save()
|
||||
logger.debug('element.filename: %s' % element.filename)
|
||||
logger.debug('element.content: %s' % element.content)
|
||||
|
||||
def restore(self):
|
||||
print 'restore from path: %s' % self.restore_path
|
||||
return 'sample_data'
|
||||
|
||||
|
||||
class LocalFileSystemStorage(FileSystemStorage):
|
||||
"""
|
||||
Simple wrapper for the stock Django FileSystemStorage class
|
||||
"""
|
||||
name = 'local_filesystem_storage'
|
||||
label = _(u'Local filesystem')
|
||||
realm = StorageModuleBase.REALM_LOCAL
|
||||
|
||||
separator = os.path.sep
|
||||
|
||||
def get_arguments(self):
|
||||
return ['backup_path', 'restore_path']
|
||||
|
||||
def backup(self, elements, dry_run):
|
||||
logger.debug('self.backup_path: %s' % self.backup_path)
|
||||
for element in elements:
|
||||
content_file = element.save()
|
||||
path = self.storage.save(content_file.name, content_file)
|
||||
logger.debug('element.filename: %s' % element.filename)
|
||||
logger.debug('element.content: %s' % element.content)
|
||||
|
||||
def restore(self):
|
||||
print 'restore from path: %s' % self.restore_path
|
||||
return 'sample_data'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.backup_path = kwargs.pop('backup_path', None)
|
||||
self.storage = FileSystemStorage(location=self.backup_path)
|
||||
|
||||
|
||||
StorageModuleBase.register(LocalFileSystemStorage)
|
||||
StorageModuleBase.register(TestStorage)
|
||||
20
apps/backups/forms.py
Normal file
20
apps/backups/forms.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django import forms
|
||||
|
||||
from common.widgets import ScrollableCheckboxSelectMultiple
|
||||
|
||||
#from .classes import AppBackup
|
||||
#from .models import App, BackupJob
|
||||
|
||||
|
||||
#def valid_app_choices():
|
||||
# # Return app that exist in the app registry and that have been registered for backup
|
||||
# return App.live.filter(pk__in=[appbackup.app.pk for appbackup in AppBackup.get_all()])
|
||||
|
||||
|
||||
#class BackupJobForm(forms.ModelForm):
|
||||
# apps = forms.ModelMultipleChoiceField(queryset=valid_app_choices(), widget=ScrollableCheckboxSelectMultiple())
|
||||
#
|
||||
# class Meta:
|
||||
# model = BackupJob
|
||||
17
apps/backups/links.py
Normal file
17
apps/backups/links.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation.api import Link
|
||||
|
||||
from .icons import icon_app
|
||||
from .permissions import PERMISSION_BACKUP_JOB_VIEW, PERMISSION_BACKUP_JOB_CREATE, PERMISSION_BACKUP_JOB_EDIT, PERMISSION_BACKUP_JOB_DELETE
|
||||
|
||||
backup_tool_link = Link(text=_(u'backups'), view='backup_job_list', icon='cd_burn.png', permissions=[PERMISSION_BACKUP_JOB_VIEW])
|
||||
backup_job_list = Link(text=_(u'backup job list'), view='backup_job_list', sprite='cd_burn', permissions=[PERMISSION_BACKUP_JOB_VIEW])
|
||||
backup_job_create = Link(text=_(u'create'), view='backup_job_create', sprite='cd_add', permissions=[PERMISSION_BACKUP_JOB_CREATE])
|
||||
backup_job_edit = Link(text=_(u'edit'), view='backup_job_edit', args='object.pk', sprite='cd_edit', permissions=[PERMISSION_BACKUP_JOB_EDIT])
|
||||
backup_job_test = Link(text=_(u'test'), view='backup_job_test', args='object.pk', sprite='cd_go')#, permissions=[PERMISSION_BACKUP_JOB_TEST])
|
||||
backup_job_delete = Link(text=_(u'delete'), view='backup_job_delete', args='object.pk', sprite='cd_delete', permissions=[PERMISSION_BACKUP_JOB_DELETE])
|
||||
|
||||
restore_tool_link = Link(text=_(u'restore'), view='restore_view', icon='cd_eject.png')#, permissions=[])
|
||||
58
apps/backups/models.py
Normal file
58
apps/backups/models.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from django.db import models
|
||||
|
||||
from app_registry.models import App
|
||||
|
||||
"""
|
||||
class BackupJob(models.Model):
|
||||
name = models.CharField(max_length=64, verbose_name=_(u'name'))
|
||||
enabled = models.BooleanField(default=True, verbose_name=_(u'enabled'))
|
||||
apps = models.ManyToManyField(App)
|
||||
begin_datetime = models.DateTimeField(verbose_name=_(u'begin date and time'), default=lambda: datetime.datetime.now())
|
||||
|
||||
# * repetition =
|
||||
# day - 1 days
|
||||
# weekly - days of week checkbox
|
||||
# month - day of month, day of week
|
||||
# * repetition option field
|
||||
# * ends
|
||||
# - never
|
||||
# - After # ocurrences
|
||||
# - On date
|
||||
# * end option field
|
||||
# * type
|
||||
# - Full
|
||||
# - Incremental
|
||||
storage_module_name = models.CharField(max_length=32, choices=StorageModuleBase.get_as_choices(), verbose_name=_(u'storage module'))
|
||||
storage_arguments_json = models.TextField(verbose_name=_(u'storage module arguments (in JSON)'), blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def storage_module(self):
|
||||
return StorageModuleBase.get(self.storage_module_name)
|
||||
|
||||
def backup(self, dry_run=False):
|
||||
logger.debug('starting: %s', self)
|
||||
logger.debug('dry_run: %s' % dry_run)
|
||||
storage_module = self.storage_module
|
||||
#TODO: loads
|
||||
for app in self.apps.all():
|
||||
app_backup = AppBackup.get(app)
|
||||
app_backup.backup(storage_module(backup_path='/tmp'), dry_run=dry_run)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
#dump
|
||||
super(BackupJob, self).save(*args, **kwargs)
|
||||
|
||||
@models.permalink
|
||||
def get_absolute_url(self):
|
||||
return ('checkout_info', [self.document.pk])
|
||||
|
||||
class Meta:
|
||||
verbose_name = _(u'document checkout')
|
||||
verbose_name_plural = _(u'document checkouts')
|
||||
|
||||
|
||||
#class BackupJobLog
|
||||
"""
|
||||
9
apps/backups/urls.py
Normal file
9
apps/backups/urls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
urlpatterns = patterns('backups.views',
|
||||
#url(r'^jobs/list/$', 'backup_job_list', (), 'backup_job_list'),
|
||||
#url(r'^jobs/create/$', 'backup_job_create', (), 'backup_job_create'),
|
||||
#url(r'^jobs/(?P<backup_job_pk>\d+)/edit/$', 'backup_job_edit', (), 'backup_job_edit'),
|
||||
#url(r'^jobs/(?P<backup_job_pk>\d+)/test/$', 'backup_job_test', (), 'backup_job_test'),
|
||||
#url(r'^jobs/(?P<backup_job_pk>\d+)/delete/$', 'backup_job_delete', (), 'backup_job_delete'),
|
||||
)
|
||||
126
apps/backups/views.py
Normal file
126
apps/backups/views.py
Normal file
@@ -0,0 +1,126 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from common.utils import encapsulate
|
||||
from permissions.models import Permission
|
||||
|
||||
#from .classes import AppBackup
|
||||
#from .forms import BackupJobForm
|
||||
#from .models import App#, BackupJob
|
||||
from .permissions import PERMISSION_BACKUP_JOB_VIEW, PERMISSION_BACKUP_JOB_CREATE, PERMISSION_BACKUP_JOB_EDIT
|
||||
|
||||
|
||||
def backup_job_list(request):
|
||||
pre_object_list = BackupJob.objects.all()
|
||||
|
||||
try:
|
||||
Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_VIEW])
|
||||
except PermissionDenied:
|
||||
# If user doesn't have global permission, get a list of backup jobs
|
||||
# for which he/she does have access use it to filter the
|
||||
# provided object_list
|
||||
final_object_list = AccessEntry.objects.filter_objects_by_access(PERMISSION_BACKUP_JOB_VIEW, request.user, pre_object_list)
|
||||
else:
|
||||
final_object_list = pre_object_list
|
||||
|
||||
context = {
|
||||
'object_list': final_object_list,
|
||||
'title': _(u'backup jobs'),
|
||||
'hide_link': True,
|
||||
#'extra_columns': [
|
||||
# {'name': _(u'info'), 'attribute': 'info'},
|
||||
#],
|
||||
}
|
||||
return render_to_response('generic_list.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def backup_job_create(request):
|
||||
Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_CREATE])
|
||||
|
||||
if request.method == 'POST':
|
||||
form = BackupJobForm(data=request.POST)
|
||||
if form.is_valid():
|
||||
try:
|
||||
backup_job = form.save()
|
||||
except Exception, exc:
|
||||
messages.error(request, _(u'Error creating backup job; %s') % exc)
|
||||
else:
|
||||
messages.success(request, _(u'Backup job "%s" created successfully.') % backup_job)
|
||||
return HttpResponseRedirect(reverse('backup_job_list'))
|
||||
else:
|
||||
form = BackupJobForm()
|
||||
|
||||
return render_to_response('generic_form.html', {
|
||||
'form': form,
|
||||
'title': _(u'Create backup job')
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def backup_job_edit(request, backup_job_pk):
|
||||
backup_job = get_object_or_404(BackupJob, pk=backup_job_pk)
|
||||
try:
|
||||
Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_EDIT])
|
||||
except PermissionDenied:
|
||||
AccessEntry.objects.check_access(PERMISSION_BACKUP_JOB_EDIT, request.user, backup_job)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = BackupJobForm(data=request.POST, instance=backup_job)
|
||||
if form.is_valid():
|
||||
try:
|
||||
backup_job = form.save()
|
||||
except Exception, exc:
|
||||
messages.error(request, _(u'Error editing backup job; %s') % exc)
|
||||
else:
|
||||
messages.success(request, _(u'Backup job "%s" edited successfully.') % backup_job)
|
||||
return HttpResponseRedirect(reverse('backup_job_list'))
|
||||
else:
|
||||
form = BackupJobForm(instance=backup_job)
|
||||
|
||||
return render_to_response('generic_form.html', {
|
||||
'form': form,
|
||||
'object': backup_job,
|
||||
'title': _(u'Edit backup job: %s') % backup_job
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def backup_job_test(request, backup_job_pk):
|
||||
backup_job = get_object_or_404(BackupJob, pk=backup_job_pk)
|
||||
#try:
|
||||
# Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_EDIT])
|
||||
#except PermissionDenied:
|
||||
# AccessEntry.objects.check_access(PERMISSION_BACKUP_JOB_EDIT, request.user, backup_job)
|
||||
|
||||
try:
|
||||
backup_job.backup(dry_run=True)
|
||||
except Exception, exc:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
else:
|
||||
messages.error(request, _(u'Error testing backup job; %s') % exc)
|
||||
return HttpResponseRedirect(reverse('backup_job_list'))
|
||||
else:
|
||||
messages.success(request, _(u'Test for backup job "%s" finished successfully.') % backup_job)
|
||||
return HttpResponseRedirect(reverse('backup_job_list'))
|
||||
|
||||
|
||||
def backup_view(request):
|
||||
#Permission.objects.check_permissions(request.user, [])
|
||||
|
||||
context = {
|
||||
'object_list': AppBackup.get_all(),
|
||||
'title': _(u'registered apps for backup'),
|
||||
'hide_link': True,
|
||||
'extra_columns': [
|
||||
{'name': _(u'info'), 'attribute': 'info'},
|
||||
],
|
||||
}
|
||||
return render_to_response('generic_list.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
@@ -3,20 +3,20 @@ from __future__ import absolute_import
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation.api import bind_links
|
||||
from project_setup.api import register_setup
|
||||
from app_registry.models import App
|
||||
#from project_setup.api import register_setup
|
||||
|
||||
from .links import database_bootstrap, bootstrap_execute, erase_database_link
|
||||
from .api import BootstrapSimple, BootstrapPermit
|
||||
|
||||
register_setup(database_bootstrap)
|
||||
register_setup(erase_database_link)
|
||||
#register_setup(database_bootstrap)
|
||||
#register_setup(erase_database_link)
|
||||
bind_links([BootstrapSimple], [bootstrap_execute])
|
||||
bind_links([BootstrapPermit], [bootstrap_execute])
|
||||
|
||||
"""
|
||||
try:
|
||||
app = App.register('bootstrap', _(u'Database bootstrap'))
|
||||
except App.UnableToRegister:
|
||||
pass
|
||||
else:
|
||||
app.set_dependencies(['app_registry'])
|
||||
"""
|
||||
|
||||
@@ -3,7 +3,6 @@ from __future__ import absolute_import
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from acls.api import class_permissions
|
||||
from app_registry.models import App
|
||||
from documents.models import Document
|
||||
from navigation.api import bind_links, register_top_menu
|
||||
from scheduler.api import LocalScheduler
|
||||
@@ -39,12 +38,4 @@ checkouts_scheduler = LocalScheduler('checkouts', _(u'Document checkouts'))
|
||||
checkouts_scheduler.add_interval_job('task_check_expired_check_outs', _(u'Check expired check out documents and checks them in.'), task_check_expired_check_outs, seconds=CHECK_EXPIRED_CHECK_OUTS_INTERVAL)
|
||||
checkouts_scheduler.start()
|
||||
|
||||
initialize_document_checkout_extra_methods()
|
||||
|
||||
try:
|
||||
app = App.register('checkouts', _(u'Checkouts'))
|
||||
except App.UnableToRegister:
|
||||
pass
|
||||
else:
|
||||
app.set_dependencies(['app_registry'])
|
||||
# AppBackup(app, [ModelBackup()])
|
||||
initialize_document_checkout_extra_methods()
|
||||
|
||||
@@ -10,28 +10,27 @@ from django.contrib.auth.models import User
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
from django.db.models.signals import post_save
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from navigation.api import bind_links, register_top_menu, Link
|
||||
from project_setup.api import register_setup
|
||||
from project_tools.api import register_tool
|
||||
#from project_tools.api import register_tool
|
||||
|
||||
from .conf.settings import (AUTO_CREATE_ADMIN, AUTO_ADMIN_USERNAME,
|
||||
from .settings import (AUTO_CREATE_ADMIN, AUTO_ADMIN_USERNAME,
|
||||
AUTO_ADMIN_PASSWORD, TEMPORARY_DIRECTORY)
|
||||
from .conf import settings as common_settings
|
||||
from .utils import validate_path
|
||||
from .links import (password_change_view, current_user_details,
|
||||
current_user_edit, about_view, license_view, admin_site, sentry)
|
||||
from .links import (link_password_change, link_current_user_details,
|
||||
link_current_user_edit, link_about, link_license, link_admin_site)
|
||||
from .models import AutoAdminSingleton
|
||||
from .debug import insert_pdb_exception_hook
|
||||
|
||||
from .literals import PAGE_SIZE_LETTER, PAGE_ORIENTATION_PORTRAIT
|
||||
if getattr(settings, 'DEBUG_ON_EXCEPTION', False):
|
||||
insert_pdb_exception_hook()
|
||||
|
||||
bind_links(['about_view', 'license_view'], [about_view, license_view], menu_name='secondary_menu')
|
||||
bind_links(['current_user_details', 'current_user_edit', 'password_change_view'], [current_user_details, current_user_edit, password_change_view], menu_name='secondary_menu')
|
||||
|
||||
register_top_menu('about', link=Link(text=_(u'about'), view='about_view', sprite='information'), position=-1)
|
||||
bind_links(['about_view', 'license_view'], [link_about, link_license], menu_name='secondary_menu')
|
||||
bind_links(['current_user_details', 'current_user_edit', 'password_change_view'], [link_current_user_details, link_current_user_edit, link_password_change], menu_name='secondary_menu')
|
||||
|
||||
register_top_menu('about', link=link_about, position=-1)
|
||||
|
||||
@receiver(post_migrate, dispatch_uid='create_superuser')
|
||||
def create_superuser(sender, **kwargs):
|
||||
@@ -63,7 +62,7 @@ def create_superuser(sender, **kwargs):
|
||||
auto_admin_properties.save()
|
||||
else:
|
||||
print 'Super admin user already exists. -- login: %s' % AUTO_ADMIN_USERNAME
|
||||
|
||||
|
||||
|
||||
@receiver(post_save, dispatch_uid='auto_admin_account_passwd_change', sender=User)
|
||||
def auto_admin_account_passwd_change(sender, instance, **kwargs):
|
||||
@@ -72,12 +71,11 @@ def auto_admin_account_passwd_change(sender, instance, **kwargs):
|
||||
# Only delete the auto admin properties when the password has been changed
|
||||
auto_admin_properties.delete(force=True)
|
||||
|
||||
# TODO: Fix
|
||||
#if (validate_path(TEMPORARY_DIRECTORY) == False) or (not TEMPORARY_DIRECTORY):
|
||||
# setattr(common_settings, 'TEMPORARY_DIRECTORY', tempfile.mkdtemp())
|
||||
|
||||
if (validate_path(TEMPORARY_DIRECTORY) == False) or (not TEMPORARY_DIRECTORY):
|
||||
setattr(common_settings, 'TEMPORARY_DIRECTORY', tempfile.mkdtemp())
|
||||
#if 'django.contrib.admin' in settings.INSTALLED_APPS:
|
||||
# register_setup(admin_site)
|
||||
|
||||
if 'django.contrib.admin' in settings.INSTALLED_APPS:
|
||||
register_setup(admin_site)
|
||||
|
||||
if 'sentry' in settings.INSTALLED_APPS:
|
||||
register_tool(sentry)
|
||||
#app.set_backup([ModelBackup()])
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
"""Configuration options for the common app"""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from smart_settings.api import Setting, SettingNamespace
|
||||
|
||||
from common.literals import PAGE_SIZE_LETTER, PAGE_ORIENTATION_PORTRAIT
|
||||
|
||||
namespace = SettingNamespace('common', _(u'Common'), module=u'common.conf.settings')
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='TEMPORARY_DIRECTORY',
|
||||
global_name='COMMON_TEMPORARY_DIRECTORY',
|
||||
default=u'/tmp',
|
||||
description=_(u'Temporary directory used site wide to store thumbnails, previews and temporary files. If none is specified, one will be created using tempfile.mkdtemp().'),
|
||||
exists=True
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name=u'DEFAULT_PAPER_SIZE',
|
||||
global_name=u'COMMON_DEFAULT_PAPER_SIZE',
|
||||
default=PAGE_SIZE_LETTER,
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name=u'DEFAULT_PAGE_ORIENTATION',
|
||||
global_name=u'COMMON_DEFAULT_PAGE_ORIENTATION',
|
||||
default=PAGE_ORIENTATION_PORTRAIT,
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name=u'AUTO_CREATE_ADMIN',
|
||||
global_name=u'COMMON_AUTO_CREATE_ADMIN',
|
||||
default=True,
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name=u'AUTO_ADMIN_USERNAME',
|
||||
global_name=u'COMMON_AUTO_ADMIN_USERNAME',
|
||||
default=u'admin',
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name=u'AUTO_ADMIN_PASSWORD',
|
||||
global_name=u'COMMON_AUTO_ADMIN_PASSWORD',
|
||||
default=User.objects.make_random_password(),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name=u'LOGIN_METHOD',
|
||||
global_name=u'COMMON_LOGIN_METHOD',
|
||||
default=u'username',
|
||||
description=_(u'Controls the mechanism used to authenticated user. Options are: username, email'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name=u'ALLOW_ANONYMOUS_ACCESS',
|
||||
global_name=u'COMMON_ALLOW_ANONYMOUS_ACCESS',
|
||||
default=False,
|
||||
description=_(u'Allow non authenticated users, access to all views'),
|
||||
)
|
||||
15
apps/common/icons.py
Normal file
15
apps/common/icons.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from icons.literals import (CROSS, TICK, COMPUTER_KEY, VCARD, VCARD_EDIT,
|
||||
INFORMATION, SCRIPT, KEYBOARD)
|
||||
from icons import Icon
|
||||
|
||||
icon_cross = Icon(CROSS)
|
||||
icon_tick = Icon(TICK)
|
||||
|
||||
icon_password_change = Icon(COMPUTER_KEY)
|
||||
icon_current_user_details = Icon(VCARD)
|
||||
icon_current_user_edit = Icon(VCARD_EDIT)
|
||||
icon_about = Icon(INFORMATION)
|
||||
icon_license = Icon(SCRIPT)
|
||||
icon_admin_site = Icon(KEYBOARD)
|
||||
@@ -2,6 +2,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation.api import Link
|
||||
|
||||
from .icons import (icon_password_change, icon_current_user_details, icon_current_user_edit,
|
||||
icon_about, icon_license, icon_admin_site)
|
||||
|
||||
def has_usable_password(context):
|
||||
return context['request'].user.has_usable_password
|
||||
@@ -11,10 +13,9 @@ def is_superuser(context):
|
||||
return context['request'].user.is_staff or context['request'].user.is_superuser
|
||||
|
||||
|
||||
password_change_view = Link(text=_(u'change password'), view='password_change_view', sprite='computer_key', condition=has_usable_password)
|
||||
current_user_details = Link(text=_(u'user details'), view='current_user_details', sprite='vcard')
|
||||
current_user_edit = Link(text=_(u'edit details'), view='current_user_edit', sprite='vcard_edit')
|
||||
about_view = Link(text=_('about'), view='about_view', sprite='information')
|
||||
license_view = Link(text=_('license'), view='license_view', sprite='script')
|
||||
sentry = Link(text=_(u'sentry'), view='sentry', sprite='bug', icon='bug.png', condition=is_superuser)
|
||||
admin_site = Link(text=_(u'admin site'), view='admin:index', sprite='keyboard', icon='keyboard.png', condition=is_superuser)
|
||||
link_password_change = Link(text=_(u'change password'), view='password_change_view', icon=icon_password_change, condition=has_usable_password)
|
||||
link_current_user_details = Link(text=_(u'user details'), view='current_user_details', icon=icon_current_user_details)
|
||||
link_current_user_edit = Link(text=_(u'edit details'), view='current_user_edit', icon=icon_current_user_edit)
|
||||
link_about = Link(text=_('about'), view='about_view', icon=icon_about)
|
||||
link_license = Link(text=_('license'), view='license_view', icon=icon_license)
|
||||
link_admin_site = Link(text=_(u'admin site'), view='admin:index', icon=icon_admin_site, condition=is_superuser)
|
||||
|
||||
@@ -38,3 +38,6 @@ PAGE_ORIENTATION_CHOICES = (
|
||||
(PAGE_ORIENTATION_PORTRAIT, _(u'Portrait')),
|
||||
(PAGE_ORIENTATION_LANDSCAPE, _(u'Landscape')),
|
||||
)
|
||||
|
||||
DEFAULT_PAGE_SIZE = PAGE_SIZE_LETTER
|
||||
DEFAULT_PAGE_ORIENTATION = PAGE_ORIENTATION_PORTRAIT
|
||||
|
||||
@@ -5,7 +5,8 @@ import re
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.conf import settings
|
||||
|
||||
from ..conf.settings import ALLOW_ANONYMOUS_ACCESS
|
||||
#from ..conf.settings import ALLOW_ANONYMOUS_ACCESS
|
||||
from .. import ALLOW_ANONYMOUS_ACCESS
|
||||
|
||||
EXEMPT_URLS = [re.compile(settings.LOGIN_URL.lstrip('/'))]
|
||||
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
|
||||
|
||||
11
apps/common/registry.py
Normal file
11
apps/common/registry.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .icons import icon_tick
|
||||
|
||||
name = 'common'
|
||||
label = _(u'Common')
|
||||
description = _(u'Contains many commonly used models, views and utilities.')
|
||||
dependencies = ['app_registry']
|
||||
icon = icon_tick
|
||||
65
apps/common/settings.py
Normal file
65
apps/common/settings.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
Configuration options for the common app
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from smart_settings import SettingsNamespace, LocalScope
|
||||
|
||||
from .literals import DEFAULT_PAGE_SIZE, DEFAULT_PAGE_ORIENTATION
|
||||
|
||||
namespace = SettingsNamespace(name='common', label=_(u'common'), module='common.settings')
|
||||
|
||||
namespace.add_setting(
|
||||
name='TEMPORARY_DIRECTORY',
|
||||
default=u'/tmp',
|
||||
description=_(u'Temporary directory used site wide to store thumbnails, previews and temporary files. If none is specified, one will be created using tempfile.mkdtemp().'),
|
||||
exists=True,
|
||||
scopes=[LocalScope()]
|
||||
)
|
||||
|
||||
namespace.add_setting(
|
||||
name=u'DEFAULT_PAPER_SIZE',
|
||||
default=DEFAULT_PAGE_SIZE,
|
||||
scopes=[LocalScope()]
|
||||
)
|
||||
|
||||
namespace.add_setting(
|
||||
name=u'DEFAULT_PAGE_ORIENTATION',
|
||||
default=DEFAULT_PAGE_ORIENTATION,
|
||||
scopes=[LocalScope()]
|
||||
)
|
||||
|
||||
namespace.add_setting(
|
||||
name=u'AUTO_CREATE_ADMIN',
|
||||
default=True,
|
||||
scopes=[LocalScope()]
|
||||
)
|
||||
|
||||
namespace.add_setting(
|
||||
name=u'AUTO_ADMIN_USERNAME',
|
||||
default=u'admin',
|
||||
scopes=[LocalScope()]
|
||||
)
|
||||
|
||||
namespace.add_setting(
|
||||
name=u'AUTO_ADMIN_PASSWORD',
|
||||
default=User.objects.make_random_password(),
|
||||
scopes=[LocalScope()]
|
||||
)
|
||||
|
||||
namespace.add_setting(
|
||||
name=u'LOGIN_METHOD',
|
||||
default=u'username',
|
||||
description=_(u'Controls the mechanism used to authenticated user. Options are: username, email'),
|
||||
scopes=[LocalScope()]
|
||||
)
|
||||
|
||||
namespace.add_setting(
|
||||
name=u'ALLOW_ANONYMOUS_ACCESS',
|
||||
default=False,
|
||||
description=_(u'Allow non authenticated users, access to all views'),
|
||||
scopes=[LocalScope()]
|
||||
)
|
||||
@@ -29,7 +29,8 @@
|
||||
{% endif %}
|
||||
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
<img style="margin-top: 12px;" src="{{ STATIC_URL }}images/icons/{{ form_icon|default:'question.png' }}" alt="{% trans 'form icon' %}" />
|
||||
{{ form_icon.display_big }}
|
||||
{#<img style="margin-top: 12px;" src="{{ STATIC_URL }}images/icons/{{ form_icon|default:'question.png' }}" alt="{% trans 'form icon' %}" />#}
|
||||
</div>
|
||||
<div style="float: left; width: 90%;">
|
||||
{% if title %}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
{% for object_reference, object_links in multi_item_links.items %}
|
||||
{% for link in object_links %}
|
||||
<button class="button" type="submit" name="action" value="{{ link.url }}">
|
||||
{% if link.sprite and not disable_icons %}<span class="famfam active famfam-{{ link.sprite|default:'link' }}"></span>{% endif %}{{ link.text|capfirst }}
|
||||
{{ link.icon.display_small }}{{ link.text|capfirst }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
@@ -175,7 +175,7 @@
|
||||
{% for object_reference, object_links in multi_item_links.items %}
|
||||
{% for link in object_links %}
|
||||
<button class="button" type="submit" name="action" value="{{ link.url }}">
|
||||
{% if link.sprite and not disable_icons %}<span class="famfam active famfam-{{ link.sprite|default:'link' }}"></span>{% endif %}{{ link.text|capfirst }}
|
||||
{{ link.icon.display_small }}{{ link.text|capfirst }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -17,7 +17,7 @@ from django.conf import settings
|
||||
|
||||
from .forms import (ChoiceForm, UserForm, UserForm_view, LicenseForm,
|
||||
EmailAuthenticationForm)
|
||||
from .conf.settings import LOGIN_METHOD
|
||||
from .settings import LOGIN_METHOD
|
||||
|
||||
|
||||
def multi_object_action_view(request):
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
from itertools import chain
|
||||
|
||||
@@ -8,6 +10,8 @@ from django.forms.util import flatatt
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.encoding import force_unicode
|
||||
|
||||
from .icons import icon_cross, icon_tick
|
||||
|
||||
|
||||
class PlainWidget(forms.widgets.Widget):
|
||||
"""
|
||||
@@ -66,11 +70,11 @@ def exists_with_famfam(path):
|
||||
return exc
|
||||
|
||||
|
||||
def two_state_template(state, famfam_ok_icon=u'tick', famfam_fail_icon=u'cross'):
|
||||
def two_state_template(state, ok_icon=icon_tick, fail_icon=icon_cross):
|
||||
if state:
|
||||
return mark_safe(u'<span class="famfam active famfam-%s"></span>' % famfam_ok_icon)
|
||||
return ok_icon
|
||||
else:
|
||||
return mark_safe(u'<span class="famfam active famfam-%s"></span>' % famfam_fail_icon)
|
||||
return fail_icon
|
||||
|
||||
|
||||
class TextAreaDiv(forms.widgets.Widget):
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from navigation.api import register_sidebar_template
|
||||
from project_tools.api import register_tool
|
||||
|
||||
from .utils import load_backend
|
||||
from .links import formats_list
|
||||
from .conf.settings import GRAPHICS_BACKEND
|
||||
from .exceptions import (UnknownFileFormat, IdentifyError, UnkownConvertError,
|
||||
OfficeConversionError, OfficeBackendError)
|
||||
|
||||
register_sidebar_template(['formats_list'], 'converter_file_formats_help.html')
|
||||
|
||||
try:
|
||||
backend = load_backend().ConverterClass()
|
||||
except ImproperlyConfigured:
|
||||
raise ImproperlyConfigured(u'Missing or incorrect converter backend: %s' % GRAPHICS_BACKEND)
|
||||
|
||||
register_tool(formats_list)
|
||||
|
||||
@@ -6,13 +6,12 @@ import logging
|
||||
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
from common.conf.settings import TEMPORARY_DIRECTORY
|
||||
from common.settings import TEMPORARY_DIRECTORY
|
||||
from common.textparser import TextParser, TEXT_PARSER_MIMETYPES
|
||||
from mimetype.api import get_mimetype
|
||||
|
||||
from .literals import (DEFAULT_PAGE_NUMBER,
|
||||
DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION, DEFAULT_FILE_FORMAT)
|
||||
from . import backend
|
||||
from .literals import (TRANSFORMATION_CHOICES, TRANSFORMATION_RESIZE,
|
||||
TRANSFORMATION_ROTATE, TRANSFORMATION_ZOOM, DIMENSION_SEPARATOR,
|
||||
FILE_FORMATS)
|
||||
@@ -43,6 +42,8 @@ def create_image_cache_filename(input_filepath, *args, **kwargs):
|
||||
|
||||
|
||||
def convert(input_filepath, output_filepath=None, cleanup_files=False, mimetype=None, *args, **kwargs):
|
||||
from .runtime import backend
|
||||
|
||||
size = kwargs.get('size')
|
||||
file_format = kwargs.get('file_format', DEFAULT_FILE_FORMAT)
|
||||
zoom = kwargs.get('zoom', DEFAULT_ZOOM_LEVEL)
|
||||
@@ -121,6 +122,8 @@ def convert(input_filepath, output_filepath=None, cleanup_files=False, mimetype=
|
||||
|
||||
|
||||
def get_page_count(input_filepath):
|
||||
from .runtime import backend
|
||||
|
||||
# Try to determine the page count first with the TextParser
|
||||
with open(input_filepath, 'rb') as descriptor:
|
||||
mimetype, encoding = get_mimetype(descriptor, input_filepath, mimetype_only=True)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
"""Configuration options for the converter app"""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from smart_settings.api import Setting, SettingNamespace
|
||||
|
||||
namespace = SettingNamespace('converter', _(u'Converter'), module='converter.conf.settings')
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='IM_CONVERT_PATH',
|
||||
global_name='CONVERTER_IM_CONVERT_PATH',
|
||||
default=u'/usr/bin/convert',
|
||||
description=_(u'File path to imagemagick\'s convert program.'),
|
||||
exists=True,
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='IM_IDENTIFY_PATH',
|
||||
global_name='CONVERTER_IM_IDENTIFY_PATH',
|
||||
default=u'/usr/bin/identify',
|
||||
description=_(u'File path to imagemagick\'s identify program.'),
|
||||
exists=True,
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='GM_PATH',
|
||||
global_name='CONVERTER_GM_PATH',
|
||||
default=u'/usr/bin/gm',
|
||||
description=_(u'File path to graphicsmagick\'s program.'),
|
||||
exists=True,
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='GM_SETTINGS',
|
||||
global_name='CONVERTER_GM_SETTINGS',
|
||||
default=u'',
|
||||
description=_(u'Set of configuration options to pass to the GraphicsMagick executable to fine tune it\'s functionality as explained in the GraphicsMagick documentation.'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='GRAPHICS_BACKEND',
|
||||
global_name='CONVERTER_GRAPHICS_BACKEND',
|
||||
default=u'converter.backends.python',
|
||||
description=_(u'Graphics conversion backend to use. Options are: converter.backends.imagemagick, converter.backends.graphicsmagick and converter.backends.python.'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='LIBREOFFICE_PATH',
|
||||
global_name='CONVERTER_LIBREOFFICE_PATH',
|
||||
default=u'/usr/bin/libreoffice',
|
||||
description=_(u'Path to the libreoffice program.'),
|
||||
exists=True
|
||||
)
|
||||
|
||||
#{'name': u'OCR_OPTIONS', 'global_name': u'CONVERTER_OCR_OPTIONS', 'default': u'-colorspace Gray -depth 8 -resample 200x200'},
|
||||
#{'name': u'HIGH_QUALITY_OPTIONS', 'global_name': u'CONVERTER_HIGH_QUALITY_OPTIONS', 'default': u'-density 400'},
|
||||
#{'name': u'PRINT_QUALITY_OPTIONS', 'global_name': u'CONVERTER_PRINT_QUALITY_OPTIONS', 'default': u'-density 500'},
|
||||
7
apps/converter/icons.py
Normal file
7
apps/converter/icons.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from icons.literals import PICTURES
|
||||
from icons import Icon
|
||||
|
||||
icon_format_list = Icon(PICTURES)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation.api import Link
|
||||
|
||||
from .icons import icon_format_list
|
||||
|
||||
def is_superuser(context):
|
||||
return context['request'].user.is_staff or context['request'].user.is_superuser
|
||||
|
||||
formats_list = Link(text=_('file formats'), view='formats_list', sprite='pictures', icon='pictures.png', condition=is_superuser, children_view_regex=[r'formats_list'])
|
||||
formats_list = Link(text=_('file formats'), view='formats_list', icon=icon_format_list, condition=is_superuser, children_view_regex=[r'formats_list'])
|
||||
|
||||
@@ -5,9 +5,8 @@ import subprocess
|
||||
import logging
|
||||
|
||||
from mimetype.api import get_mimetype
|
||||
from common.conf.settings import TEMPORARY_DIRECTORY
|
||||
from common.settings import TEMPORARY_DIRECTORY
|
||||
|
||||
from .conf.settings import LIBREOFFICE_PATH
|
||||
from .exceptions import OfficeBackendError, UnknownFileFormat
|
||||
|
||||
CACHED_FILE_SUFFIX = u'_office_converter'
|
||||
@@ -79,7 +78,9 @@ class OfficeConverter(object):
|
||||
|
||||
class OfficeConverterBackendDirect(object):
|
||||
def __init__(self):
|
||||
self.libreoffice_path = LIBREOFFICE_PATH if LIBREOFFICE_PATH else u'/usr/bin/libreoffice'
|
||||
from .settings import LIBREOFFICE_PATH
|
||||
|
||||
self.libreoffice_path = LIBREOFFICE_PATH
|
||||
if not os.path.exists(self.libreoffice_path):
|
||||
raise OfficeBackendError('cannot find LibreOffice executable')
|
||||
logger.debug('self.libreoffice_path: %s' % self.libreoffice_path)
|
||||
|
||||
63
apps/converter/registry.py
Normal file
63
apps/converter/registry.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from smart_settings import LocalScope
|
||||
|
||||
from .icons import icon_format_list
|
||||
from .links import formats_list
|
||||
|
||||
name = 'converter'
|
||||
label = _(u'Converter')
|
||||
description = _(u'Handles file type convertions.')
|
||||
icon = icon_format_list
|
||||
dependencies = ['app_registry']
|
||||
tool_links = [formats_list]
|
||||
|
||||
settings = [
|
||||
{
|
||||
'name': 'IM_CONVERT_PATH',
|
||||
'default': u'/usr/bin/convert',
|
||||
'description': _(u'File path to imagemagick\'s convert program.'),
|
||||
'exists': True,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'IM_IDENTIFY_PATH',
|
||||
'default': u'/usr/bin/identify',
|
||||
'description': _(u'File path to imagemagick\'s identify program.'),
|
||||
'exists': True,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'GM_PATH',
|
||||
'default': u'/usr/bin/gm',
|
||||
'description': _(u'File path to graphicsmagick\'s program.'),
|
||||
'exists': True,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'GM_SETTINGS',
|
||||
'default': u'',
|
||||
'description': _(u'Set of configuration options to pass to the GraphicsMagick executable to fine tune it\'s functionality as explained in the GraphicsMagick documentation.'),
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'GRAPHICS_BACKEND',
|
||||
'default': u'converter.backends.python',
|
||||
'description': _(u'Graphics conversion backend to use. Options are: converter.backends.imagemagick, converter.backends.graphicsmagick and converter.backends.python.'),
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'LIBREOFFICE_PATH',
|
||||
'default': u'/usr/bin/libreoffice',
|
||||
'description': _(u'Path to the libreoffice program.'),
|
||||
'exists': True,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
#{'name': u'OCR_OPTIONS', 'global_name': u'CONVERTER_OCR_OPTIONS', 'default': u'-colorspace Gray -depth 8 -resample 200x200'},
|
||||
#{'name': u'HIGH_QUALITY_OPTIONS', 'global_name': u'CONVERTER_HIGH_QUALITY_OPTIONS', 'default': u'-density 400'},
|
||||
#{'name': u'PRINT_QUALITY_OPTIONS', 'global_name': u'CONVERTER_PRINT_QUALITY_OPTIONS', 'default': u'-density 500'},
|
||||
@@ -1,10 +1,18 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from .office_converter import OfficeConverter
|
||||
from .exceptions import OfficeBackendError
|
||||
|
||||
from .settings import GRAPHICS_BACKEND
|
||||
from .utils import load_backend
|
||||
|
||||
try:
|
||||
office_converter = OfficeConverter()
|
||||
except OfficeBackendError:
|
||||
office_converter = None
|
||||
|
||||
try:
|
||||
backend = load_backend().ConverterClass()
|
||||
except ImproperlyConfigured:
|
||||
raise ImproperlyConfigured(u'Missing or incorrect converter backend: %s' % GRAPHICS_BACKEND)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from .settings import GRAPHICS_BACKEND
|
||||
|
||||
|
||||
def _lazy_load(fn):
|
||||
_cached = []
|
||||
@@ -16,10 +20,9 @@ def _lazy_load(fn):
|
||||
|
||||
@_lazy_load
|
||||
def load_backend():
|
||||
from converter.conf.settings import GRAPHICS_BACKEND as backend_name
|
||||
|
||||
try:
|
||||
module = import_module('.base', 'converter.backends.%s' % backend_name)
|
||||
module = import_module('.base', 'converter.backends.%s' % GRAPHICS_BACKEND)
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"Short names for CONVERTER_BACKEND are deprecated; prepend with 'converter.backends.'",
|
||||
@@ -29,7 +32,7 @@ def load_backend():
|
||||
except ImportError, e:
|
||||
# Look for a fully qualified converter backend name
|
||||
try:
|
||||
return import_module('.base', backend_name)
|
||||
return import_module('.base', GRAPHICS_BACKEND)
|
||||
except ImportError, e_user:
|
||||
# The converter backend wasn't found. Display a helpful error message
|
||||
# listing all possible (built-in) converter backends.
|
||||
@@ -41,11 +44,11 @@ def load_backend():
|
||||
except EnvironmentError:
|
||||
available_backends = []
|
||||
available_backends.sort()
|
||||
if backend_name not in available_backends:
|
||||
if GRAPHICS_BACKEND not in available_backends:
|
||||
error_msg = ("%r isn't an available converter backend. \n" +
|
||||
"Try using converter.backends.XXX, where XXX is one of:\n %s\n" +
|
||||
"Error was: %s") % \
|
||||
(backend_name, ", ".join(map(repr, available_backends)), e_user)
|
||||
(GRAPHICS_BACKEND, ", ".join(map(repr, available_backends)), e_user)
|
||||
raise ImproperlyConfigured(error_msg)
|
||||
else:
|
||||
# If there's some other error, this must be an error in Mayan itself.
|
||||
|
||||
@@ -8,7 +8,7 @@ from django.core.exceptions import PermissionDenied
|
||||
from common.utils import encapsulate
|
||||
|
||||
from .api import get_format_list
|
||||
from .conf.settings import GRAPHICS_BACKEND
|
||||
from .settings import GRAPHICS_BACKEND
|
||||
|
||||
|
||||
def formats_list(request):
|
||||
|
||||
8
apps/diagnostics/icons.py
Normal file
8
apps/diagnostics/icons.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from icons.literals import PILL, LIGHTNING
|
||||
from icons import Icon
|
||||
|
||||
icon_diagnostic = Icon(PILL)
|
||||
icon_diagnostic_execute = Icon(LIGHTNING)
|
||||
|
||||
@@ -4,5 +4,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation.api import Link
|
||||
|
||||
diagnostic_list = Link(text=_(u'diagnostics'), view='diagnostic_list', icon='pill.png', sprite='pill')
|
||||
diagnostic_execute = Link(text=_(u'execute'), view='diagnostic_execute', args='object.id', sprite='lightning')
|
||||
from .icons import icon_diagnostic, icon_diagnostic_execute
|
||||
|
||||
diagnostic_list = Link(text=_(u'diagnostics'), view='diagnostic_list', icon=icon_diagnostic)
|
||||
diagnostic_execute = Link(text=_(u'execute'), view='diagnostic_execute', args='object.id', icon=icon_diagnostic_execute)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from navigation.api import bind_links
|
||||
from project_setup.api import register_setup
|
||||
#from project_setup.api import register_setup
|
||||
from hkp import Key as KeyServerKey
|
||||
|
||||
from .api import Key
|
||||
from .links import (private_keys, public_keys, key_delete, key_query,
|
||||
key_receive, key_setup)
|
||||
key_receive)#, key_setup)
|
||||
|
||||
#bind_links(['key_delete', 'key_private_list', 'key_public_list', 'key_query'], [private_keys, public_keys, key_query], menu_name='sidebar')
|
||||
bind_links(['key_delete', 'key_public_list', 'key_query'], [public_keys, key_query], menu_name='sidebar')
|
||||
@@ -14,4 +14,4 @@ bind_links(['key_delete', 'key_public_list', 'key_query'], [public_keys, key_que
|
||||
bind_links([Key], [key_delete])
|
||||
bind_links([KeyServerKey], [key_receive])
|
||||
|
||||
register_setup(key_setup)
|
||||
#register_setup(key_setup)
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
"""
|
||||
Configuration options for the django_gpg app
|
||||
"""
|
||||
import os
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from smart_settings.api import Setting, SettingNamespace
|
||||
|
||||
namespace = SettingNamespace('django_gpg', _(u'Signatures'), module='django_gpg.conf.settings', sprite='text_signature')
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='KEYSERVERS',
|
||||
global_name='SIGNATURES_KEYSERVERS',
|
||||
default=['pool.sks-keyservers.net'],
|
||||
description=_(u'List of keyservers to be queried for unknown keys.'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='GPG_HOME',
|
||||
global_name='SIGNATURES_GPG_HOME',
|
||||
default=os.path.join(settings.PROJECT_ROOT, u'gpg_home'),
|
||||
description=_(u'Home directory used to store keys as well as configuration files.'),
|
||||
exists=True,
|
||||
)
|
||||
13
apps/django_gpg/icons.py
Normal file
13
apps/django_gpg/icons.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from icons.literals import (DOCUMENT_SIGNATURE, KEY, KEY_DELETE, KEY_ADD,
|
||||
ZOOM, LIGHTNING)
|
||||
from icons import Icon
|
||||
|
||||
icon_private_keys = Icon(KEY)
|
||||
icon_public_keys = Icon(KEY)
|
||||
icon_key_delete = Icon(KEY_DELETE)
|
||||
icon_key_query = Icon(ZOOM)
|
||||
icon_key_receive = Icon(KEY_ADD)
|
||||
icon_key_setup = Icon(KEY)
|
||||
icon_document_signature = Icon(DOCUMENT_SIGNATURE)
|
||||
@@ -4,12 +4,14 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation.api import Link
|
||||
|
||||
from .icons import (icon_private_keys, icon_public_keys, icon_key_delete,
|
||||
icon_key_query, icon_key_receive, icon_key_setup)
|
||||
from .permissions import (PERMISSION_KEY_VIEW, PERMISSION_KEY_DELETE,
|
||||
PERMISSION_KEYSERVER_QUERY, PERMISSION_KEY_RECEIVE)
|
||||
|
||||
private_keys = Link(text=_(u'private keys'), view='key_private_list', args='object.pk', sprite='key', icon='key.png', permissions=[PERMISSION_KEY_VIEW])
|
||||
public_keys = Link(text=_(u'public keys'), view='key_public_list', args='object.pk', sprite='key', icon='key.png', permissions=[PERMISSION_KEY_VIEW])
|
||||
key_delete = Link(text=_(u'delete'), view='key_delete', args=['object.fingerprint', 'object.type'], sprite='key_delete', permissions=[PERMISSION_KEY_DELETE])
|
||||
key_query = Link(text=_(u'query keyservers'), view='key_query', sprite='zoom', permissions=[PERMISSION_KEYSERVER_QUERY])
|
||||
key_receive = Link(text=_(u'import'), view='key_receive', args='object.keyid', sprite='key_add', keep_query=True, permissions=[PERMISSION_KEY_RECEIVE])
|
||||
key_setup = Link(text=_(u'key management'), view='key_public_list', args='object.pk', sprite='key', icon='key.png', permissions=[PERMISSION_KEY_VIEW], children_view_regex=[r'^key_'])
|
||||
private_keys = Link(text=_(u'private keys'), view='key_private_list', args='object.pk', icon=icon_private_keys, permissions=[PERMISSION_KEY_VIEW])
|
||||
public_keys = Link(text=_(u'public keys'), view='key_public_list', args='object.pk', icon=icon_private_keys, permissions=[PERMISSION_KEY_VIEW])
|
||||
key_delete = Link(text=_(u'delete'), view='key_delete', args=['object.fingerprint', 'object.type'], icon=icon_key_delete, permissions=[PERMISSION_KEY_DELETE])
|
||||
key_query = Link(text=_(u'query keyservers'), view='key_query', icon=icon_key_query, permissions=[PERMISSION_KEYSERVER_QUERY])
|
||||
key_receive = Link(text=_(u'import'), view='key_receive', args='object.keyid', icon=icon_key_receive, keep_query=True, permissions=[PERMISSION_KEY_RECEIVE])
|
||||
key_setup = Link(text=_(u'key management'), view='key_public_list', args='object.pk', icon=icon_key_setup, permissions=[PERMISSION_KEY_VIEW], children_view_regex=[r'^key_'])
|
||||
|
||||
34
apps/django_gpg/registry.py
Normal file
34
apps/django_gpg/registry.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from smart_settings import LocalScope
|
||||
|
||||
from .icons import icon_document_signature
|
||||
from .links import key_setup
|
||||
|
||||
name = 'django_gpg'
|
||||
label = _(u'GPG')
|
||||
description = _(u'Handles digital signatures.')
|
||||
icon = icon_document_signature
|
||||
dependencies = ['app_registry', 'permissions']
|
||||
setup_links = [key_setup]
|
||||
|
||||
settings = [
|
||||
{
|
||||
'name': 'KEYSERVERS',
|
||||
'default': ['pool.sks-keyservers.net'],
|
||||
'description': _(u'List of keyservers to be queried for unknown keys.'),
|
||||
'scopes': [LocalScope()],
|
||||
},
|
||||
{
|
||||
'name': 'GPG_HOME',
|
||||
'default': os.path.join(settings.PROJECT_ROOT, u'gpg_home'),
|
||||
'description': _(u'Home directory used to store keys as well as configuration files.'),
|
||||
'exists': True,
|
||||
'scopes': [LocalScope()],
|
||||
}
|
||||
]
|
||||
@@ -3,7 +3,7 @@ from __future__ import absolute_import
|
||||
import sys
|
||||
|
||||
from .api import GPG
|
||||
from .conf.settings import KEYSERVERS, GPG_HOME
|
||||
from .settings import KEYSERVERS, GPG_HOME
|
||||
|
||||
try:
|
||||
gpg = GPG(home=GPG_HOME, keyservers=KEYSERVERS)
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import tempfile
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from acls.api import class_permissions
|
||||
from app_registry.models import App
|
||||
from common.utils import validate_path, encapsulate
|
||||
from diagnostics.api import DiagnosticNamespace
|
||||
from common.utils import encapsulate
|
||||
#from diagnostics.api import DiagnosticNamespace
|
||||
from history.permissions import PERMISSION_HISTORY_VIEW
|
||||
from maintenance.api import MaintenanceNamespace
|
||||
from navigation.api import (bind_links, register_top_menu,
|
||||
register_model_list_columns,
|
||||
register_sidebar_template, Link, register_multi_item_links)
|
||||
from project_setup.api import register_setup
|
||||
from statistics.api import register_statistics
|
||||
|
||||
# Register document type links
|
||||
from .models import (Document, DocumentPage,
|
||||
DocumentPageTransformation, DocumentType, DocumentTypeFilename,
|
||||
DocumentVersion)
|
||||
@@ -24,7 +20,6 @@ from .permissions import (PERMISSION_DOCUMENT_PROPERTIES_EDIT,
|
||||
PERMISSION_DOCUMENT_DOWNLOAD, PERMISSION_DOCUMENT_TRANSFORM,
|
||||
PERMISSION_DOCUMENT_EDIT, PERMISSION_DOCUMENT_VERSION_REVERT,
|
||||
PERMISSION_DOCUMENT_NEW_VERSION)
|
||||
from .conf import settings as document_settings
|
||||
from .widgets import document_thumbnail
|
||||
from .links import (document_list, document_list_recent,
|
||||
document_create_siblings, document_view_simple, document_view_advanced,
|
||||
@@ -35,7 +30,8 @@ from .links import (document_list, document_list_recent,
|
||||
document_missing_list)
|
||||
from .links import (document_type_list, document_type_setup, document_type_document_list,
|
||||
document_type_edit, document_type_delete, document_type_create, document_type_filename_list,
|
||||
document_type_filename_create, document_type_filename_edit, document_type_filename_delete)
|
||||
document_type_filename_create, document_type_filename_edit, document_type_filename_delete,
|
||||
link_documents_menu)
|
||||
from .links import document_version_list, document_version_revert
|
||||
from .links import (document_page_transformation_list, document_page_transformation_create,
|
||||
document_page_transformation_edit, document_page_transformation_delete,
|
||||
@@ -46,9 +42,7 @@ from .links import (document_page_transformation_list, document_page_transformat
|
||||
document_multiple_clear_transformations, document_multiple_delete,
|
||||
document_multiple_download, document_version_text_compare)
|
||||
from .links import document_clear_image_cache
|
||||
from .statistics import get_statistics
|
||||
|
||||
# Register document type links
|
||||
bind_links([DocumentType], [document_type_document_list, document_type_filename_list, document_type_edit, document_type_delete])
|
||||
bind_links([DocumentTypeFilename], [document_type_filename_edit, document_type_filename_delete])
|
||||
|
||||
@@ -86,8 +80,8 @@ bind_links('document_page_transformation_list', [document_page_transformation_cr
|
||||
bind_links('document_page_transformation_create', [document_page_transformation_create], menu_name='sidebar')
|
||||
bind_links(['document_page_transformation_edit', 'document_page_transformation_delete'], [document_page_transformation_create], menu_name='sidebar')
|
||||
|
||||
namespace = DiagnosticNamespace(_(u'documents'))
|
||||
namespace.create_tool(document_missing_list)
|
||||
#namespace = DiagnosticNamespace(_(u'documents'))
|
||||
#namespace.create_tool(document_missing_list)
|
||||
|
||||
namespace = MaintenanceNamespace(_(u'documents'))
|
||||
namespace.create_tool(document_find_all_duplicates)
|
||||
@@ -104,10 +98,7 @@ register_model_list_columns(Document, [
|
||||
|
||||
register_top_menu(
|
||||
'documents',
|
||||
link=Link(sprite='page', text=_(u'documents'), view='document_list_recent',
|
||||
children_url_regex=[r'^documents/[^t]', r'^metadata/[^s]', r'comments', r'tags/document', r'grouping/[^s]', r'history/list/for_object/documents'],
|
||||
children_view_regex=[r'document_acl', r'smart_link_instance'],
|
||||
children_views=['document_folder_list', 'folder_add_document', 'document_index_list', 'upload_version', ]),
|
||||
link=link_documents_menu,
|
||||
position=1
|
||||
)
|
||||
|
||||
@@ -119,11 +110,6 @@ bind_links([Document], [document_view_advanced], menu_name='form_header', positi
|
||||
bind_links([Document], [document_history_view], menu_name='form_header')
|
||||
bind_links([Document], [document_version_list], menu_name='form_header')
|
||||
|
||||
if (validate_path(document_settings.CACHE_PATH) == False) or (not document_settings.CACHE_PATH):
|
||||
setattr(document_settings, 'CACHE_PATH', tempfile.mkdtemp())
|
||||
|
||||
register_setup(document_type_setup)
|
||||
|
||||
class_permissions(Document, [
|
||||
PERMISSION_DOCUMENT_PROPERTIES_EDIT,
|
||||
PERMISSION_DOCUMENT_EDIT,
|
||||
@@ -136,12 +122,3 @@ class_permissions(Document, [
|
||||
PERMISSION_HISTORY_VIEW
|
||||
])
|
||||
|
||||
register_statistics(get_statistics)
|
||||
|
||||
try:
|
||||
app = App.register('documents', _(u'Documents'))
|
||||
except App.UnableToRegister:
|
||||
pass
|
||||
else:
|
||||
app.set_dependencies(['app_registry'])
|
||||
#AppBackup(app, [ModelBackup(), FileBackup(document_settings.STORAGE_BACKEND)])
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
"""Configuration options for the documents app"""
|
||||
|
||||
import hashlib
|
||||
import uuid
|
||||
import os
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from storage.backends.filebasedstorage import FileBasedStorage
|
||||
from smart_settings.api import Setting, SettingNamespace
|
||||
|
||||
|
||||
def default_checksum(x):
|
||||
"""hashlib.sha256(x).hexdigest()"""
|
||||
return hashlib.sha256(x).hexdigest()
|
||||
|
||||
|
||||
def default_uuid():
|
||||
"""unicode(uuid.uuid4())"""
|
||||
return unicode(uuid.uuid4())
|
||||
|
||||
|
||||
namespace = SettingNamespace('documents', _(u'Documents'), module='documents.conf.settings', sprite='page')
|
||||
|
||||
# Saving
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='CHECKSUM_FUNCTION',
|
||||
global_name='DOCUMENTS_CHECKSUM_FUNCTION',
|
||||
default=default_checksum,
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='UUID_FUNCTION',
|
||||
global_name='DOCUMENTS_UUID_FUNCTION',
|
||||
default=default_uuid,
|
||||
)
|
||||
|
||||
# Storage
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='STORAGE_BACKEND',
|
||||
global_name='DOCUMENTS_STORAGE_BACKEND',
|
||||
default=FileBasedStorage,
|
||||
)
|
||||
|
||||
# Usage
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='PREVIEW_SIZE',
|
||||
global_name='DOCUMENTS_PREVIEW_SIZE',
|
||||
default=u'640x480',
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='PRINT_SIZE',
|
||||
global_name='DOCUMENTS_PRINT_SIZE',
|
||||
default=u'1400',
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='MULTIPAGE_PREVIEW_SIZE',
|
||||
global_name='DOCUMENTS_MULTIPAGE_PREVIEW_SIZE',
|
||||
default=u'160x120',
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='THUMBNAIL_SIZE',
|
||||
global_name='DOCUMENTS_THUMBNAIL_SIZE',
|
||||
default=u'50x50',
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='DISPLAY_SIZE',
|
||||
global_name='DOCUMENTS_DISPLAY_SIZE',
|
||||
default=u'1200',
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='RECENT_COUNT',
|
||||
global_name='DOCUMENTS_RECENT_COUNT',
|
||||
default=40,
|
||||
description=_(u'Maximum number of recent (created, edited, viewed) documents to remember per user.'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='ZOOM_PERCENT_STEP',
|
||||
global_name='DOCUMENTS_ZOOM_PERCENT_STEP',
|
||||
default=50,
|
||||
description=_(u'Amount in percent zoom in or out a document page per user interaction.'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='ZOOM_MAX_LEVEL',
|
||||
global_name='DOCUMENTS_ZOOM_MAX_LEVEL',
|
||||
default=200,
|
||||
description=_(u'Maximum amount in percent (%) to allow user to zoom in a document page interactively.'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='ZOOM_MIN_LEVEL',
|
||||
global_name='DOCUMENTS_ZOOM_MIN_LEVEL',
|
||||
default=50,
|
||||
description=_(u'Minimum amount in percent (%) to allow user to zoom out a document page interactively.'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='ROTATION_STEP',
|
||||
global_name='DOCUMENTS_ROTATION_STEP',
|
||||
default=90,
|
||||
description=_(u'Amount in degrees to rotate a document page per user interaction.'),
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='CACHE_PATH',
|
||||
global_name='DOCUMENTS_CACHE_PATH',
|
||||
default=os.path.join(settings.PROJECT_ROOT, 'image_cache'),
|
||||
exists=True
|
||||
)
|
||||
@@ -8,7 +8,7 @@ from django.utils.safestring import mark_safe
|
||||
|
||||
from common.forms import DetailForm
|
||||
from common.literals import PAGE_SIZE_CHOICES, PAGE_ORIENTATION_CHOICES
|
||||
from common.conf.settings import DEFAULT_PAPER_SIZE, DEFAULT_PAGE_ORIENTATION
|
||||
from common.settings import DEFAULT_PAPER_SIZE, DEFAULT_PAGE_ORIENTATION
|
||||
from common.widgets import TextAreaDiv
|
||||
|
||||
from .models import (Document, DocumentType,
|
||||
|
||||
20
apps/documents/icons.py
Normal file
20
apps/documents/icons.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from icons.literals import (LAYOUT, MAGNIFIER, PAGE, PAGE_COPY, PAGE_GEAR, PAGE_DELETE,
|
||||
PAGE_EDIT, PAGE_REFRESH, PAGE_SAVE, PAGE_WHITE_COPY, PAGE_WORLD, PRINTER,
|
||||
TABLE_RELATIONSHIP)
|
||||
from icons import Icon
|
||||
|
||||
icon_documents = Icon(PAGE)
|
||||
icon_create_siblings = Icon(PAGE_COPY)
|
||||
icon_document_properties = Icon(PAGE_GEAR)
|
||||
icon_document_delete = Icon(PAGE_DELETE)
|
||||
icon_document_edit = Icon(PAGE_EDIT)
|
||||
icon_document_preview = Icon(MAGNIFIER)
|
||||
icon_document_download = Icon(PAGE_SAVE)
|
||||
icon_find_duplicates = Icon(PAGE_WHITE_COPY)
|
||||
icon_print = Icon(PRINTER)
|
||||
icon_version_revert = Icon(PAGE_REFRESH)
|
||||
icon_version_compare = Icon(TABLE_RELATIONSHIP)
|
||||
icon_versions = Icon(PAGE_WORLD)
|
||||
icon_document_types = Icon(LAYOUT)
|
||||
@@ -4,6 +4,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation.api import Link
|
||||
from history.permissions import PERMISSION_HISTORY_VIEW
|
||||
from history.icons import icon_history_link
|
||||
|
||||
from .permissions import (PERMISSION_DOCUMENT_CREATE,
|
||||
PERMISSION_DOCUMENT_PROPERTIES_EDIT, PERMISSION_DOCUMENT_VIEW,
|
||||
@@ -13,9 +14,10 @@ from .permissions import (PERMISSION_DOCUMENT_CREATE,
|
||||
PERMISSION_DOCUMENT_TYPE_EDIT, PERMISSION_DOCUMENT_TYPE_DELETE,
|
||||
PERMISSION_DOCUMENT_TYPE_CREATE, PERMISSION_DOCUMENT_TYPE_VIEW,
|
||||
PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE)
|
||||
|
||||
from .conf.settings import ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL
|
||||
|
||||
from .icons import (icon_documents, icon_create_siblings, icon_document_delete,
|
||||
icon_document_properties, icon_document_edit, icon_document_preview,
|
||||
icon_document_download, icon_find_duplicates, icon_print, icon_version_revert,
|
||||
icon_version_compare, icon_versions, icon_document_types)
|
||||
|
||||
# Document page links expressions
|
||||
def is_first_page(context):
|
||||
@@ -27,10 +29,12 @@ def is_last_page(context):
|
||||
|
||||
|
||||
def is_min_zoom(context):
|
||||
from .settings import ZOOM_MIN_LEVEL
|
||||
return context['zoom'] <= ZOOM_MIN_LEVEL
|
||||
|
||||
|
||||
def is_max_zoom(context):
|
||||
from .settings import ZOOM_MAX_LEVEL
|
||||
return context['zoom'] >= ZOOM_MAX_LEVEL
|
||||
|
||||
|
||||
@@ -38,25 +42,25 @@ def is_current_version(context):
|
||||
return context['object'].document.latest_version.timestamp == context['object'].timestamp
|
||||
|
||||
|
||||
document_list = Link(text=_(u'all documents'), view='document_list', sprite='page')
|
||||
document_list_recent = Link(text=_(u'recent documents'), view='document_list_recent', sprite='page')
|
||||
document_create_siblings = Link(text=_(u'clone metadata'), view='document_create_siblings', args='object.id', sprite='page_copy', permissions=[PERMISSION_DOCUMENT_CREATE])
|
||||
document_view_simple = Link(text=_(u'details'), view='document_view_simple', args='object.id', sprite='page', permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_view_advanced = Link(text=_(u'properties'), view='document_view_advanced', args='object.id', sprite='page_gear', permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_delete = Link(text=_(u'delete'), view='document_delete', args='object.id', sprite='page_delete', permissions=[PERMISSION_DOCUMENT_DELETE])
|
||||
document_multiple_delete = Link(text=_(u'delete'), view='document_multiple_delete', sprite='page_delete', permissions=[PERMISSION_DOCUMENT_DELETE])
|
||||
document_edit = Link(text=_(u'edit'), view='document_edit', args='object.id', sprite='page_edit', permissions=[PERMISSION_DOCUMENT_PROPERTIES_EDIT])
|
||||
document_preview = Link(text=_(u'preview'), klass='fancybox', view='document_preview', args='object.id', sprite='magnifier', permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_download = Link(text=_(u'download'), view='document_download', args='object.id', sprite='page_save', permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
|
||||
document_multiple_download = Link(text=_(u'download'), view='document_multiple_download', sprite='page_save', permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
|
||||
document_version_download = Link(text=_(u'download'), view='document_version_download', args='object.pk', sprite='page_save', permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
|
||||
document_find_duplicates = Link(text=_(u'find duplicates'), view='document_find_duplicates', args='object.id', sprite='page_white_copy', permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_find_all_duplicates = Link(text=_(u'find all duplicates'), view='document_find_all_duplicates', sprite='page_white_copy', permissions=[PERMISSION_DOCUMENT_VIEW], description=_(u'Search all the documents\' checksums and return a list of the exact matches.'))
|
||||
document_list = Link(text=_(u'all documents'), view='document_list', icon=icon_documents)
|
||||
document_list_recent = Link(text=_(u'recent documents'), view='document_list_recent', icon=icon_documents)
|
||||
document_create_siblings = Link(text=_(u'clone metadata'), view='document_create_siblings', args='object.id', icon=icon_create_siblings, permissions=[PERMISSION_DOCUMENT_CREATE])
|
||||
document_view_simple = Link(text=_(u'details'), view='document_view_simple', args='object.id', icon=icon_documents, permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_view_advanced = Link(text=_(u'properties'), view='document_view_advanced', args='object.id', icon=icon_document_properties, permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_delete = Link(text=_(u'delete'), view='document_delete', args='object.id', icon=icon_document_delete, permissions=[PERMISSION_DOCUMENT_DELETE])
|
||||
document_multiple_delete = Link(text=_(u'delete'), view='document_multiple_delete', icon=icon_document_delete, permissions=[PERMISSION_DOCUMENT_DELETE])
|
||||
document_edit = Link(text=_(u'edit'), view='document_edit', args='object.id', icon=icon_document_edit, permissions=[PERMISSION_DOCUMENT_PROPERTIES_EDIT])
|
||||
document_preview = Link(text=_(u'preview'), klass='fancybox', view='document_preview', args='object.id', icon=icon_document_preview, permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_download = Link(text=_(u'download'), view='document_download', args='object.id', icon=icon_document_download, permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
|
||||
document_multiple_download = Link(text=_(u'download'), view='document_multiple_download', icon=icon_document_download, permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
|
||||
document_version_download = Link(text=_(u'download'), view='document_version_download', args='object.pk', icon=icon_document_download, permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
|
||||
document_find_duplicates = Link(text=_(u'find duplicates'), view='document_find_duplicates', args='object.id', icon=icon_find_duplicates, permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_find_all_duplicates = Link(text=_(u'find all duplicates'), view='document_find_all_duplicates', icon=icon_find_duplicates, permissions=[PERMISSION_DOCUMENT_VIEW], description=_(u'Search all the documents\' checksums and return a list of the exact matches.'))
|
||||
document_update_page_count = Link(text=_(u'update office documents\' page count'), view='document_update_page_count', sprite='page_white_csharp', permissions=[PERMISSION_DOCUMENT_TOOLS], description=_(u'Update the page count of the office type documents. This is useful when enabling office document support after there were already office type documents in the database.'))
|
||||
document_clear_transformations = Link(text=_(u'clear transformations'), view='document_clear_transformations', args='object.id', sprite='page_paintbrush', permissions=[PERMISSION_DOCUMENT_TRANSFORM])
|
||||
document_multiple_clear_transformations = Link(text=_(u'clear transformations'), view='document_multiple_clear_transformations', sprite='page_paintbrush', permissions=[PERMISSION_DOCUMENT_TRANSFORM])
|
||||
document_print = Link(text=_(u'print'), view='document_print', args='object.id', sprite='printer', permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_history_view = Link(text=_(u'history'), view='history_for_object', args=['"documents"', '"document"', 'object.pk'], sprite='book_go', permissions=[PERMISSION_HISTORY_VIEW])
|
||||
document_print = Link(text=_(u'print'), view='document_print', args='object.id', icon=icon_print, permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_history_view = Link(text=_(u'history'), view='history_for_object', args=['"documents"', '"document"', 'object.pk'], icon=icon_history_link, permissions=[PERMISSION_HISTORY_VIEW])
|
||||
document_missing_list = Link(text=_(u'Find missing document files'), view='document_missing_list', sprite='page_find', description=_(u'Return a list of documents found on the database but that don\'t physically exist in the document storage.'), permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
|
||||
# Tools
|
||||
@@ -82,13 +86,13 @@ document_page_rotate_left = Link(text=_(u'rotate left'), klass='no-parent-histor
|
||||
document_page_view_reset = Link(text=_(u'reset view'), klass='no-parent-history', view='document_page_view_reset', args='page.pk', sprite='page_white', permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
|
||||
# Document versions
|
||||
document_version_list = Link(text=_(u'versions'), view='document_version_list', args='object.pk', sprite='page_world', permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_version_revert = Link(text=_(u'revert'), view='document_version_revert', args='object.pk', sprite='page_refresh', permissions=[PERMISSION_DOCUMENT_VERSION_REVERT], conditional_disable=is_current_version)
|
||||
document_version_text_compare = Link(text=_(u'compare (text)'), view='document_version_text_compare', args='object.pk', sprite='table_relationship', permissions=[PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE])
|
||||
document_version_list = Link(text=_(u'versions'), view='document_version_list', args='object.pk', icon=icon_versions, permissions=[PERMISSION_DOCUMENT_VIEW])
|
||||
document_version_revert = Link(text=_(u'revert'), view='document_version_revert', args='object.pk', icon=icon_version_revert, permissions=[PERMISSION_DOCUMENT_VERSION_REVERT], conditional_disable=is_current_version)
|
||||
document_version_text_compare = Link(text=_(u'compare (text)'), view='document_version_text_compare', args='object.pk', icon=icon_version_compare, permissions=[PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE])
|
||||
|
||||
# Document type related links
|
||||
document_type_list = Link(text=_(u'document type list'), view='document_type_list', sprite='layout', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW])
|
||||
document_type_setup = Link(text=_(u'document types'), view='document_type_list', sprite='layout', icon='layout.png', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW], children_view_regex=[r'^document_type_'])
|
||||
document_type_list = Link(text=_(u'document type list'), view='document_type_list', icon=icon_document_types, permissions=[PERMISSION_DOCUMENT_TYPE_VIEW])
|
||||
document_type_setup = Link(text=_(u'document types'), view='document_type_list', sprite='layout', icon=icon_document_types, permissions=[PERMISSION_DOCUMENT_TYPE_VIEW], children_view_regex=[r'^document_type_'])
|
||||
document_type_document_list = Link(text=_(u'documents of this type'), view='document_type_document_list', args='document_type.id', sprite='page_go', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW])
|
||||
document_type_edit = Link(text=_(u'edit'), view='document_type_edit', args='document_type.id', sprite='layout_edit', permissions=[PERMISSION_DOCUMENT_TYPE_EDIT])
|
||||
document_type_delete = Link(text=_(u'delete'), view='document_type_delete', args='document_type.id', sprite='layout_delete', permissions=[PERMISSION_DOCUMENT_TYPE_DELETE])
|
||||
@@ -101,3 +105,8 @@ document_type_filename_delete = Link(text=_(u'delete'), view='document_type_file
|
||||
|
||||
# TODO: remove this
|
||||
document_type_views = ['setup_document_type_metadata', 'document_type_list', 'document_type_document_list', 'document_type_edit', 'document_type_delete', 'document_type_create', 'document_type_filename_list', 'document_type_filename_create', 'document_type_filename_edit', 'document_type_filename_delete']
|
||||
|
||||
link_documents_menu = Link(icon=icon_documents, text=_(u'documents'), view='document_list_recent',
|
||||
children_url_regex=[r'^documents/[^t]', r'^metadata/[^s]', r'comments', r'tags/document', r'grouping/[^s]', r'history/list/for_object/documents'],
|
||||
children_view_regex=[r'document_acl', r'smart_link_instance'],
|
||||
children_views=['document_folder_list', 'folder_add_document', 'document_index_list', 'upload_version', ])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from ast import literal_eval
|
||||
from datetime import datetime
|
||||
|
||||
from django.db import models
|
||||
|
||||
@@ -24,3 +25,23 @@ class DocumentPageTransformationManager(models.Manager):
|
||||
warnings.append(e)
|
||||
|
||||
return transformations, warnings
|
||||
|
||||
|
||||
class RecentDocumentManager(models.Manager):
|
||||
def add_document_for_user(self, user, document):
|
||||
from .settings import RECENT_COUNT
|
||||
|
||||
if user.is_authenticated():
|
||||
self.model.objects.filter(user=user, document=document).delete()
|
||||
new_recent = self.model(user=user, document=document, datetime_accessed=datetime.now())
|
||||
new_recent.save()
|
||||
to_delete = self.model.objects.filter(user=user)[RECENT_COUNT:]
|
||||
for recent_to_delete in to_delete:
|
||||
recent_to_delete.delete()
|
||||
|
||||
def get_for_user(self, user):
|
||||
document_model = models.get_model('documents', 'Document')
|
||||
if user.is_authenticated():
|
||||
return document_model.objects.filter(recentdocument__user=user)
|
||||
else:
|
||||
return []
|
||||
|
||||
@@ -21,20 +21,24 @@ from django.utils.translation import ugettext
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from converter.api import get_page_count
|
||||
from converter.api import get_available_transformations_choices
|
||||
from converter.api import convert
|
||||
from converter.exceptions import UnknownFileFormat, UnkownConvertError
|
||||
from mimetype.api import (get_mimetype, get_icon_file_path,
|
||||
get_error_icon_file_path)
|
||||
import converter
|
||||
#from converter import api as converter_api
|
||||
#from converter.api import get_page_count
|
||||
#from converter.api import get_available_transformations_choices
|
||||
#from converter.api import convert
|
||||
#from converter.exceptions import UnknownFileFormat, UnkownConvertError
|
||||
#from mimetype.api import (get_mimetype, get_icon_file_path,
|
||||
# get_error_icon_file_path)
|
||||
from converter.literals import (DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION,
|
||||
DEFAULT_PAGE_NUMBER)
|
||||
|
||||
from .conf.settings import RECENT_COUNT
|
||||
from .conf.settings import (CHECKSUM_FUNCTION, UUID_FUNCTION,
|
||||
STORAGE_BACKEND, DISPLAY_SIZE, CACHE_PATH,
|
||||
ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL)
|
||||
from .managers import DocumentPageTransformationManager
|
||||
from mimetype.icons import icon_file_extension_error
|
||||
|
||||
#from .settings import (CHECKSUM_FUNCTION, UUID_FUNCTION,
|
||||
# STORAGE_BACKEND, DISPLAY_SIZE, CACHE_PATH,
|
||||
# ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL)
|
||||
|
||||
from .managers import DocumentPageTransformationManager, RecentDocumentManager
|
||||
from .utils import document_save_to_temp_dir
|
||||
from .literals import (RELEASE_LEVEL_FINAL, RELEASE_LEVEL_CHOICES,
|
||||
VERSION_UPDATE_MAJOR, VERSION_UPDATE_MINOR, VERSION_UPDATE_MICRO)
|
||||
@@ -51,6 +55,8 @@ def get_filename_from_uuid(instance, filename):
|
||||
Store the orignal filename of the uploaded file and replace it with
|
||||
a UUID
|
||||
"""
|
||||
from .settings import UUID_FUNCTION
|
||||
|
||||
instance.filename = filename
|
||||
return UUID_FUNCTION()
|
||||
|
||||
@@ -82,6 +88,8 @@ class Document(models.Model):
|
||||
|
||||
@staticmethod
|
||||
def clear_image_cache():
|
||||
from .settings import CACHE_PATH
|
||||
|
||||
for the_file in os.listdir(CACHE_PATH):
|
||||
file_path = os.path.join(CACHE_PATH, the_file)
|
||||
if os.path.isfile(file_path):
|
||||
@@ -100,6 +108,8 @@ class Document(models.Model):
|
||||
return ('document_view_simple', [self.pk])
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
from .settings import UUID_FUNCTION
|
||||
|
||||
if not self.pk:
|
||||
self.uuid = UUID_FUNCTION()
|
||||
self.date_added = datetime.datetime.now()
|
||||
@@ -107,6 +117,8 @@ class Document(models.Model):
|
||||
self.mark_indexable()
|
||||
|
||||
def get_cached_image_name(self, page, version):
|
||||
from .settings import CACHE_PATH
|
||||
|
||||
document_version = DocumentVersion.objects.get(pk=version)
|
||||
document_page = document_version.documentpage_set.get(page_number=page)
|
||||
transformations, warnings = document_page.get_transformation_list()
|
||||
@@ -114,6 +126,7 @@ class Document(models.Model):
|
||||
return os.path.join(CACHE_PATH, hash_value), transformations
|
||||
|
||||
def get_image_cache_name(self, page, version):
|
||||
from converter.api import convert
|
||||
cache_file_path, transformations = self.get_cached_image_name(page, version)
|
||||
if os.path.exists(cache_file_path):
|
||||
return cache_file_path
|
||||
@@ -122,13 +135,22 @@ class Document(models.Model):
|
||||
document_file = document_save_to_temp_dir(document_version, document_version.checksum)
|
||||
return convert(document_file, output_filepath=cache_file_path, page=page, transformations=transformations, mimetype=self.file_mimetype)
|
||||
|
||||
def get_valid_image(self, size=DISPLAY_SIZE, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, version=None):
|
||||
def get_valid_image(self, size=None, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, version=None):
|
||||
from converter.api import convert
|
||||
|
||||
if not size:
|
||||
size = DISPLAY_SIZE
|
||||
|
||||
if not version:
|
||||
version = self.latest_version.pk
|
||||
image_cache_name = self.get_image_cache_name(page=page, version=version)
|
||||
return convert(image_cache_name, cleanup_files=False, size=size, zoom=zoom, rotation=rotation)
|
||||
|
||||
def get_image(self, size=DISPLAY_SIZE, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, as_base64=False, version=None):
|
||||
def get_image(self, size=None, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, as_base64=False, version=None):
|
||||
from .settings import ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL
|
||||
if not size:
|
||||
size = DISPLAY_SIZE
|
||||
|
||||
if zoom < ZOOM_MIN_LEVEL:
|
||||
zoom = ZOOM_MIN_LEVEL
|
||||
|
||||
@@ -139,12 +161,12 @@ class Document(models.Model):
|
||||
|
||||
try:
|
||||
file_path = self.get_valid_image(size=size, page=page, zoom=zoom, rotation=rotation, version=version)
|
||||
except UnknownFileFormat:
|
||||
except converter.UnknownFileFormat:
|
||||
file_path = get_icon_file_path(self.file_mimetype)
|
||||
except UnkownConvertError:
|
||||
file_path = get_error_icon_file_path()
|
||||
except converter.UnkownConvertError:
|
||||
file_path = icon_file_extension_error.get_filepath()
|
||||
except:
|
||||
file_path = get_error_icon_file_path()
|
||||
file_path = icon_file_extension_error.get_filepath()
|
||||
|
||||
if as_base64:
|
||||
image = open(file_path, 'r')
|
||||
@@ -340,7 +362,7 @@ class DocumentVersion(models.Model):
|
||||
comment = models.TextField(blank=True, verbose_name=_(u'comment'))
|
||||
|
||||
# File related fields
|
||||
file = models.FileField(upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'file'))
|
||||
file = models.FileField(upload_to=get_filename_from_uuid, verbose_name=_(u'file'))
|
||||
mimetype = models.CharField(max_length=64, null=True, blank=True, editable=False)
|
||||
encoding = models.CharField(max_length=64, null=True, blank=True, editable=False)
|
||||
filename = models.CharField(max_length=255, default=u'', editable=False, db_index=True)
|
||||
@@ -424,6 +446,8 @@ class DocumentVersion(models.Model):
|
||||
Open a document version's file and update the checksum field using the
|
||||
user provided checksum function
|
||||
"""
|
||||
from .settings import CHECKSUM_FUNCTION
|
||||
|
||||
if self.exists():
|
||||
source = self.open()
|
||||
self.checksum = unicode(CHECKSUM_FUNCTION(source.read()))
|
||||
@@ -432,6 +456,7 @@ class DocumentVersion(models.Model):
|
||||
self.save()
|
||||
|
||||
def update_page_count(self, save=True):
|
||||
from coverter.api import get_page_count
|
||||
handle, filepath = tempfile.mkstemp()
|
||||
# Just need the filepath, close the file description
|
||||
os.close(handle)
|
||||
@@ -439,7 +464,7 @@ class DocumentVersion(models.Model):
|
||||
self.save_to_file(filepath)
|
||||
try:
|
||||
detected_pages = get_page_count(filepath)
|
||||
except UnknownFileFormat:
|
||||
except converter.UnknownFileFormat:
|
||||
# If converter backend doesn't understand the format,
|
||||
# use 1 as the total page count
|
||||
detected_pages = 1
|
||||
@@ -660,7 +685,8 @@ class DocumentPageTransformation(models.Model):
|
||||
"""
|
||||
document_page = models.ForeignKey(DocumentPage, verbose_name=_(u'document page'))
|
||||
order = models.PositiveIntegerField(default=0, blank=True, null=True, verbose_name=_(u'order'), db_index=True)
|
||||
transformation = models.CharField(choices=get_available_transformations_choices(), max_length=128, verbose_name=_(u'transformation'))
|
||||
#transformation = models.CharField(choices=get_available_transformations_choices(), max_length=128, verbose_name=_(u'transformation'))
|
||||
transformation = models.CharField(max_length=128, verbose_name=_(u'transformation'))
|
||||
arguments = models.TextField(blank=True, null=True, verbose_name=_(u'arguments'), help_text=_(u'Use dictionaries to indentify arguments, example: %s') % u'{\'degrees\':90}', validators=[ArgumentsValidator()])
|
||||
objects = DocumentPageTransformationManager()
|
||||
|
||||
@@ -673,23 +699,6 @@ class DocumentPageTransformation(models.Model):
|
||||
verbose_name_plural = _(u'document page transformations')
|
||||
|
||||
|
||||
class RecentDocumentManager(models.Manager):
|
||||
def add_document_for_user(self, user, document):
|
||||
if user.is_authenticated():
|
||||
self.model.objects.filter(user=user, document=document).delete()
|
||||
new_recent = self.model(user=user, document=document, datetime_accessed=datetime.datetime.now())
|
||||
new_recent.save()
|
||||
to_delete = self.model.objects.filter(user=user)[RECENT_COUNT:]
|
||||
for recent_to_delete in to_delete:
|
||||
recent_to_delete.delete()
|
||||
|
||||
def get_for_user(self, user):
|
||||
if user.is_authenticated():
|
||||
return Document.objects.filter(recentdocument__user=user)
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
class RecentDocument(models.Model):
|
||||
"""
|
||||
Keeps a list of the n most recent accessed or created document for
|
||||
|
||||
22
apps/documents/post_init.py
Normal file
22
apps/documents/post_init.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import tempfile
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.utils import validate_path, encapsulate
|
||||
|
||||
from .models import DocumentVersion, get_filename_from_uuid
|
||||
from .settings import STORAGE_BACKEND, CACHE_PATH
|
||||
|
||||
|
||||
def init_validate_cache_path():
|
||||
if (validate_path(CACHE_PATH) == False) or (not CACHE_PATH):
|
||||
setattr(document_settings, 'CACHE_PATH', tempfile.mkdtemp())
|
||||
|
||||
def init_set_storage_backend():
|
||||
# Monkey patch the file field until this is resolved: AttributeError:
|
||||
# The 'file' attribute can only be accessed from DocumentVersion instances.
|
||||
#DocumentVersion.file.storage = STORAGE_BACKEND()
|
||||
DocumentVersion.add_to_class('file', models.FileField(upload_to=get_filename_from_uuid, verbose_name=_(u'file'), storage=STORAGE_BACKEND()))
|
||||
132
apps/documents/registry.py
Normal file
132
apps/documents/registry.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""
|
||||
Configuration options for the documents app
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import hashlib
|
||||
import uuid
|
||||
import os
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings as django_settings
|
||||
|
||||
from storage.backends.filebasedstorage import FileBasedStorage
|
||||
from smart_settings import LocalScope
|
||||
|
||||
from .icons import icon_documents
|
||||
from .links import document_type_setup
|
||||
from .statistics import get_statistics
|
||||
|
||||
def default_checksum(x):
|
||||
"""hashlib.sha256(x).hexdigest()"""
|
||||
return hashlib.sha256(x).hexdigest()
|
||||
|
||||
|
||||
def default_uuid():
|
||||
"""unicode(uuid.uuid4())"""
|
||||
return unicode(uuid.uuid4())
|
||||
|
||||
|
||||
name = 'documents'
|
||||
label = _(u'Documents')
|
||||
description = _(u'Base app that handles documents instances.')
|
||||
icon = icon_documents
|
||||
dependencies = ['app_registry', 'icons', 'storage', 'permissions', 'navigation']
|
||||
setup_links = [document_type_setup]
|
||||
#AppBackup(app, [ModelBackup(), FileBackup(document_settings.STORAGE_BACKEND)])
|
||||
|
||||
settings = [
|
||||
{
|
||||
'name': 'IM_CONVERT_PATH',
|
||||
'default': u'/usr/bin/convert',
|
||||
'description': _(u'File path to imagemagick\'s convert program.'),
|
||||
'exists': True,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
|
||||
# Saving
|
||||
|
||||
{
|
||||
'name': 'CHECKSUM_FUNCTION',
|
||||
'default': default_checksum,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'UUID_FUNCTION',
|
||||
'default': default_uuid,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
|
||||
# Storage
|
||||
|
||||
{
|
||||
'name': 'STORAGE_BACKEND',
|
||||
'default': FileBasedStorage,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
|
||||
# Usage
|
||||
|
||||
{
|
||||
'name': 'PREVIEW_SIZE',
|
||||
'default': u'640x480',
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'PRINT_SIZE',
|
||||
'default': u'1400',
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'MULTIPAGE_PREVIEW_SIZE',
|
||||
'default': u'160x120',
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'THUMBNAIL_SIZE',
|
||||
'default': u'50x50',
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'DISPLAY_SIZE',
|
||||
'default': u'1200',
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'RECENT_COUNT',
|
||||
'default': 40,
|
||||
'description': _(u'Maximum number of recent (created, edited, viewed}, documents to remember per user.'),
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'ZOOM_PERCENT_STEP',
|
||||
'default': 50,
|
||||
'description': _(u'Amount in percent zoom in or out a document page per user interaction.'),
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'ZOOM_MAX_LEVEL',
|
||||
'default': 200,
|
||||
'description': _(u'Maximum amount in percent (%}, to allow user to zoom in a document page interactively.'),
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'ZOOM_MIN_LEVEL',
|
||||
'default': 50,
|
||||
'description': _(u'Minimum amount in percent (%}, to allow user to zoom out a document page interactively.'),
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'ROTATION_STEP',
|
||||
'default': 90,
|
||||
'description': _(u'Amount in degrees to rotate a document page per user interaction.'),
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
{
|
||||
'name': 'CACHE_PATH',
|
||||
'default': os.path.join(django_settings.PROJECT_ROOT, 'image_cache'),
|
||||
'exists': True,
|
||||
'scopes': [LocalScope()]
|
||||
},
|
||||
]
|
||||
statistics=[get_statistics]
|
||||
@@ -5,11 +5,12 @@ from django.db.models import Avg, Count, Min, Max
|
||||
|
||||
from common.utils import pretty_size, pretty_size_10
|
||||
|
||||
from .conf.settings import STORAGE_BACKEND
|
||||
from .models import Document, DocumentType, DocumentPage, DocumentVersion
|
||||
|
||||
|
||||
def get_used_size(path, file_list):
|
||||
from .settings import STORAGE_BACKEND
|
||||
|
||||
total_size = 0
|
||||
for filename in file_list:
|
||||
try:
|
||||
@@ -21,6 +22,8 @@ def get_used_size(path, file_list):
|
||||
|
||||
|
||||
def storage_count(path=u'.'):
|
||||
from .settings import STORAGE_BACKEND
|
||||
|
||||
try:
|
||||
directories, files = STORAGE_BACKEND().listdir(path)
|
||||
except OSError:
|
||||
|
||||
@@ -2,9 +2,10 @@ from __future__ import absolute_import
|
||||
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
from .conf.settings import (PREVIEW_SIZE, PRINT_SIZE, THUMBNAIL_SIZE,
|
||||
from .settings import (PREVIEW_SIZE, PRINT_SIZE, THUMBNAIL_SIZE,
|
||||
DISPLAY_SIZE, MULTIPAGE_PREVIEW_SIZE)
|
||||
|
||||
|
||||
urlpatterns = patterns('documents.views',
|
||||
url(r'^list/$', 'document_list', (), 'document_list'),
|
||||
url(r'^list/recent/$', 'document_list_recent', (), 'document_list_recent'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
from common.conf.settings import TEMPORARY_DIRECTORY
|
||||
from common.settings import TEMPORARY_DIRECTORY
|
||||
|
||||
|
||||
def document_save_to_temp_dir(document, filename, buffer_size=1024 * 1024):
|
||||
|
||||
@@ -16,14 +16,14 @@ from django.core.exceptions import PermissionDenied
|
||||
from django.conf import settings
|
||||
|
||||
import sendfile
|
||||
from common.utils import pretty_size, parse_range, urlquote, \
|
||||
return_diff, encapsulate
|
||||
from common.utils import (pretty_size, parse_range, urlquote,
|
||||
return_diff, encapsulate)
|
||||
from common.widgets import two_state_template
|
||||
from common.literals import PAGE_SIZE_DIMENSIONS, \
|
||||
PAGE_ORIENTATION_PORTRAIT, PAGE_ORIENTATION_LANDSCAPE
|
||||
from common.conf.settings import DEFAULT_PAPER_SIZE
|
||||
from converter.literals import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION, \
|
||||
DEFAULT_PAGE_NUMBER, DEFAULT_FILE_FORMAT_MIMETYPE
|
||||
from common.literals import (PAGE_SIZE_DIMENSIONS,
|
||||
PAGE_ORIENTATION_PORTRAIT, PAGE_ORIENTATION_LANDSCAPE)
|
||||
from common.settings import DEFAULT_PAPER_SIZE
|
||||
from converter.literals import (DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION,
|
||||
DEFAULT_PAGE_NUMBER, DEFAULT_FILE_FORMAT_MIMETYPE)
|
||||
from converter.office_converter import OfficeConverter
|
||||
from filetransfers.api import serve_file
|
||||
from navigation.utils import resolve_to_name
|
||||
@@ -31,9 +31,10 @@ from permissions.models import Permission
|
||||
from acls.models import AccessEntry
|
||||
from common.compressed_files import CompressedFile
|
||||
|
||||
from .conf.settings import (PREVIEW_SIZE, STORAGE_BACKEND, ZOOM_PERCENT_STEP,
|
||||
from .settings import (PREVIEW_SIZE, STORAGE_BACKEND, ZOOM_PERCENT_STEP,
|
||||
ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL, ROTATION_STEP, PRINT_SIZE,
|
||||
RECENT_COUNT)
|
||||
|
||||
from .permissions import (PERMISSION_DOCUMENT_CREATE,
|
||||
PERMISSION_DOCUMENT_PROPERTIES_EDIT, PERMISSION_DOCUMENT_VIEW,
|
||||
PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD,
|
||||
|
||||
@@ -8,9 +8,10 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.http import urlencode
|
||||
|
||||
from mimetype.icons import icon_file_extension_error
|
||||
|
||||
from converter.literals import (DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION,
|
||||
DEFAULT_PAGE_NUMBER)
|
||||
from mimetype.api import get_error_icon_url
|
||||
|
||||
|
||||
def document_thumbnail(document, **kwargs):
|
||||
@@ -73,7 +74,7 @@ def document_html_widget(document, view='document_thumbnail', click_view=None, p
|
||||
}
|
||||
})
|
||||
.error(function(data) {
|
||||
$('#document-%(pk)d-%(page)d').html('<img src="%(error_image)s" />');
|
||||
$('#document-%(pk)d-%(page)d').html('%(error_image)s');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -82,7 +83,7 @@ def document_html_widget(document, view='document_thumbnail', click_view=None, p
|
||||
'pk': document.pk,
|
||||
'page': page if page else 1,
|
||||
'plain_template': mark_safe(u''.join(plain_template)),
|
||||
'error_image': u''.join([settings.STATIC_URL, get_error_icon_url()]),
|
||||
'error_image': icon_file_extension_error.display_big(),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ from signaler.signals import post_update_index, pre_update_index
|
||||
from lock_manager import Lock, LockError
|
||||
|
||||
from .models import IndexableObject
|
||||
from .conf.settings import INDEX_UPDATE_INTERVAL
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -63,3 +62,5 @@ bind_links(['results'], [search_again], menu_name='sidebar')
|
||||
dynamic_search_scheduler = LocalScheduler('search', _(u'Search'))
|
||||
dynamic_search_scheduler.add_interval_job('search_index_update', _(u'Update the search index with the most recent modified documents.'), search_index_update, seconds=INDEX_UPDATE_INTERVAL)
|
||||
dynamic_search_scheduler.start()
|
||||
|
||||
# register_top_menu('search', link=Link(text=_(u'search'), view='search', sprite='zoom', children_url_regex=[r'^search/']))
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
"""Configuration options for the dynamic_search app"""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from smart_settings.api import Setting, SettingNamespace
|
||||
|
||||
namespace = SettingNamespace('dynamic_search', _(u'Searching'), module='dynamic_search.conf.settings', sprite='zoom')
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='RECENT_COUNT',
|
||||
global_name='SEARCH_RECENT_COUNT',
|
||||
default=5,
|
||||
description=_(u'Maximum number of search queries to remember per user.')
|
||||
)
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='INDEX_UPDATE_INTERVAL',
|
||||
global_name='SEARCH_INDEX_UPDATE_INTERVAL',
|
||||
default=1800,
|
||||
description=_(u'Interval in second on which to trigger the search index update.')
|
||||
)
|
||||
25
apps/dynamic_search/registry.py
Normal file
25
apps/dynamic_search/registry.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
#from .icons import icon_history_list
|
||||
#from .links import history_list
|
||||
|
||||
label = _(u'Search')
|
||||
#description = _(u'Handles the events registration and event logging.')
|
||||
dependencies = ['app_registry', 'icons', 'navigation']
|
||||
#icon = icon_history_list
|
||||
#tool_links = [history_list]
|
||||
#- namespace=namespace,
|
||||
#- name='RECENT_COUNT',
|
||||
#- global_name='SEARCH_RECENT_COUNT',
|
||||
#- default=5,
|
||||
#- description=_(u'Maximum number of search queries to remember per user.')
|
||||
#-)
|
||||
#-
|
||||
#-Setting(
|
||||
#- namespace=namespace,
|
||||
#- name='INDEX_UPDATE_INTERVAL',
|
||||
#- global_name='SEARCH_INDEX_UPDATE_INTERVAL',
|
||||
#- default=1800,
|
||||
#- description=_(u'Interval in second on which to trigger the search index update.')
|
||||
@@ -2,17 +2,12 @@ from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from app_registry.models import App
|
||||
from app_registry.classes import ModelBackup
|
||||
from common.utils import encapsulate
|
||||
from navigation.api import bind_links, register_model_list_columns
|
||||
from project_tools.api import register_tool
|
||||
|
||||
from .models import History
|
||||
from .widgets import history_entry_type_link
|
||||
from .links import history_list, history_details
|
||||
|
||||
register_tool(history_list)
|
||||
from .links import history_details
|
||||
|
||||
register_model_list_columns(History, [
|
||||
{
|
||||
@@ -30,11 +25,3 @@ register_model_list_columns(History, [
|
||||
])
|
||||
|
||||
bind_links([History], [history_details])
|
||||
|
||||
try:
|
||||
app = App.register('history', _(u'History'))
|
||||
except App.UnableToRegister:
|
||||
pass
|
||||
else:
|
||||
app.set_backup([ModelBackup()])
|
||||
app.set_dependencies(['app_registry'])
|
||||
|
||||
9
apps/history/icons.py
Normal file
9
apps/history/icons.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from icons.literals import BOOK_OPEN, BOOK, BOOK_GO
|
||||
from icons import Icon
|
||||
|
||||
icon_history_list = Icon(BOOK)
|
||||
icon_history_details = Icon(BOOK_OPEN)
|
||||
icon_history_link = Icon(BOOK_GO)
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from navigation.api import Link
|
||||
|
||||
from .permissions import PERMISSION_HISTORY_VIEW
|
||||
from .icons import icon_history_list, icon_history_details
|
||||
|
||||
history_list = Link(text=_(u'history'), view='history_list', sprite='book', icon='book.png', permissions=[PERMISSION_HISTORY_VIEW], children_view_regex=[r'history_[l,v]'])
|
||||
history_details = Link(text=_(u'details'), view='history_view', sprite='book_open', args='object.pk', permissions=[PERMISSION_HISTORY_VIEW])
|
||||
history_list = Link(text=_(u'history'), view='history_list', icon=icon_history_list, permissions=[PERMISSION_HISTORY_VIEW], children_view_regex=[r'history_[l,v]'])
|
||||
history_details = Link(text=_(u'details'), view='history_view', icon=icon_history_details, args='object.pk', permissions=[PERMISSION_HISTORY_VIEW])
|
||||
|
||||
@@ -44,7 +44,10 @@ class History(models.Model):
|
||||
dictionary = models.TextField(verbose_name=_(u'dictionary'), blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s - %s - %s' % (self.datetime, self.content_object, self.history_type)
|
||||
try:
|
||||
return u'%s - %s - %s' % (self.datetime, self.content_object, self.history_type)
|
||||
except AttributeError:
|
||||
return u'%s - %s - %s' % (self.datetime, _(u'unknown object'), self.history_type)
|
||||
|
||||
def get_label(self):
|
||||
return history_types_dict[self.history_type.namespace][self.history_type.name].label
|
||||
|
||||
13
apps/history/registry.py
Normal file
13
apps/history/registry.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .icons import icon_history_list
|
||||
from .links import history_list
|
||||
|
||||
name = 'history'
|
||||
label = _(u'History')
|
||||
description = _(u'Handles the events registration and event logging.')
|
||||
dependencies = ['app_registry']
|
||||
icon = icon_history_list
|
||||
tool_links = [history_list]
|
||||
@@ -1,10 +1,8 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from __future__ import absolute_import
|
||||
|
||||
from app_registry.models import App
|
||||
#from django.utils.translation import ugettext_lazy as _
|
||||
#from django.conf import settings
|
||||
|
||||
try:
|
||||
app = App.register('icons', _(u'Icons'))
|
||||
except App.UnableToRegister:
|
||||
pass
|
||||
else:
|
||||
app.set_dependencies(['app_registry'])
|
||||
from .classes import Icon
|
||||
#from storage.backends.filebasedstorage import FileBasedStorage
|
||||
#afrom app_registry.models import App
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .conf import settings
|
||||
from .sets import ICON_THEMES
|
||||
from .literals import ERROR
|
||||
|
||||
|
||||
def get_icon_name(icon):
|
||||
try:
|
||||
return ICON_THEMES[settings.ICON_SET][icon]
|
||||
except KeyError:
|
||||
return ICON_THEMES[settings.ICON_SET][ERROR]
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def get_sprite_name(sprite):
|
||||
try:
|
||||
return ICON_THEMES[settings.ICON_SET][sprite]
|
||||
except KeyError:
|
||||
return ICON_THEMES[settings.ICON_SET][ERROR]
|
||||
except AttributeError:
|
||||
pass
|
||||
50
apps/icons/classes.py
Normal file
50
apps/icons/classes.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.conf import settings
|
||||
|
||||
from .settings import ICON_SET
|
||||
from .sets import ICON_THEMES
|
||||
from .literals import ERROR
|
||||
|
||||
SIZE_SMALL = '16x16'
|
||||
SIZE_BIG = '32x32'
|
||||
|
||||
|
||||
class Icon(object):
|
||||
_registry = {}
|
||||
|
||||
def __init__(self, literal, icon_set=None):
|
||||
self.literal = literal
|
||||
self.icon_set = icon_set
|
||||
self.__class__._registry[literal] = self
|
||||
|
||||
def get_file_name(self, size):
|
||||
# TODO: Move name + size resolution to sets to support size/name and
|
||||
# name_size filename conventions
|
||||
try:
|
||||
if self.icon_set:
|
||||
return '%s/%s/%s' % (ICON_THEMES[self.icon_set].PATH, size, ICON_THEMES[self.icon_set].DICTIONARY[self.literal])
|
||||
else:
|
||||
return '%s/%s/%s' % (ICON_THEMES[ICON_SET].PATH, size, ICON_THEMES[ICON_SET].DICTIONARY[self.literal])
|
||||
except KeyError:
|
||||
return '%s/%s/%s' % (ICON_THEMES[ICON_SET].PATH, size, ICON_THEMES[ICON_SET].DICTIONARY[ERROR])
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def display(self, size): # TODO: move to widgets?
|
||||
return mark_safe(u'<img src="%s/icons/%s" />' % (settings.STATIC_URL, self.get_file_name(size)))
|
||||
|
||||
def display_small(self):
|
||||
return self.display(SIZE_SMALL)
|
||||
|
||||
def display_big(self):
|
||||
return self.display(SIZE_BIG)
|
||||
|
||||
def get_filepath(self):
|
||||
if settings.DEVELOPMENT:
|
||||
return os.path.join(settings.PROJECT_ROOT, 'apps', 'icons', 'static', 'icons', self.get_file_name(SIZE_BIG))
|
||||
else:
|
||||
return os.path.join(settings.STATIC_ROOT, self.get_file_name(SIZE_BIG))
|
||||
@@ -1,24 +0,0 @@
|
||||
"""
|
||||
Configuration options for the documents app
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from storage.backends.filebasedstorage import FileBasedStorage
|
||||
from smart_settings.api import Setting, SettingNamespace
|
||||
from ..literals import DEFAULT_ICON_SET
|
||||
|
||||
from .. import app
|
||||
print '__file__', __file__
|
||||
namespace = SettingNamespace(app.name, app.label, module='icons.conf.settings', sprite='page')
|
||||
|
||||
# Saving
|
||||
|
||||
Setting(
|
||||
namespace=namespace,
|
||||
name='ICON_SET',
|
||||
global_name='ICONS_ICON_SET',
|
||||
default=DEFAULT_ICON_SET,
|
||||
)
|
||||
7
apps/icons/icons.py
Normal file
7
apps/icons/icons.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .literals import DRAW_AIRBRUSH
|
||||
from . import Icon
|
||||
|
||||
icon_icons_app = Icon(DRAW_AIRBRUSH)
|
||||
|
||||
@@ -1,10 +1,70 @@
|
||||
#from icons.sets import fat_cow, famfamfam
|
||||
|
||||
#DEFAULT_ICON_SET = fat_cow.ID
|
||||
ICON_SET_FAT_COW = 'fat_cow'
|
||||
ICON_SET_CUSTOM = 'custom'
|
||||
|
||||
DEFAULT_ICON_SET = 'fat_cow'
|
||||
|
||||
APP = 'app'
|
||||
BACKUPS = 'backups'
|
||||
|
||||
APPLICATION_VIEW_ICONS = 'application_view_icons'
|
||||
BLACKBOARD_SUM = 'blackboard_sum'
|
||||
BOOK = 'book'
|
||||
BOOK_GO = 'book_go'
|
||||
BOOK_OPEN = 'book_open'
|
||||
CD_BURN = 'cd_burn'
|
||||
COG = 'cog'
|
||||
COMPUTER_KEY = 'computer_key'
|
||||
CROSS = 'cross'
|
||||
DRAW_AIRBRUSH = 'draw_airbrush'
|
||||
DOCUMENT_SIGNATURE = 'document_signature'
|
||||
ERROR = 'error'
|
||||
ICONS = 'icons'
|
||||
|
||||
FILE_EXTENSION_ERROR = 'file_extension_error'
|
||||
FILE_EXTENSION_UNKNOWN = 'file_extension_unknown'
|
||||
GROUP = 'user',
|
||||
GROUP_ADD = 'group_add'
|
||||
GROUP_EDIT = 'group_edit'
|
||||
GROUP_DELETE = 'group_delete'
|
||||
GROUP_KEY = 'group_key'
|
||||
INFORMATION = 'information'
|
||||
KEY = 'key'
|
||||
KEY_GO = 'key_go'
|
||||
KEY_ADD = 'key_add'
|
||||
KEY_DELETE = 'key_delete'
|
||||
KEYBOARD = 'keyboard'
|
||||
LAYOUT = 'layout'
|
||||
LIGHTNING = 'lightning'
|
||||
LOCK = 'lock'
|
||||
MAGNIFIER = 'magnifier'
|
||||
MEDAL_GOLD = 'medal_gold'
|
||||
MEDAL_GOLD_ADD = 'medal_gold_add'
|
||||
MEDAL_GOLD_DELETE = 'medal_gold_delete'
|
||||
PAGE = 'page'
|
||||
PAGE_COPY = 'page_copy'
|
||||
PAGE_GEAR = 'page_gear'
|
||||
PAGE_DELETE = 'page_delete'
|
||||
PAGE_EDIT = 'page_edit'
|
||||
PAGE_REFRESH = 'page_refresh'
|
||||
PAGE_SAVE = 'page_save'
|
||||
PAGE_WHITE_COPY = 'page_white_copy'
|
||||
PAGE_WORLD = 'page_world'
|
||||
PICTURES = 'pictures'
|
||||
PILL = 'pill'
|
||||
PLUGIN = 'plugin'
|
||||
PRINTER = 'printer'
|
||||
RAINBOW = 'rainbow'
|
||||
ROUTING_TURNAROUND_RIGHT = 'routing_turnaround_right'
|
||||
SCRIPT = 'script'
|
||||
STORAGE = 'storage'
|
||||
TABLE = 'table'
|
||||
TABLE_RELATIONSHIP = 'table_relationship'
|
||||
TEXT_DROPCAPS = 'text_dropcaps'
|
||||
TEXT_STRIKETHROUGH = 'text_strikethrough'
|
||||
TICK = 'tick'
|
||||
USER = 'user'
|
||||
USER_ADD = 'user_add'
|
||||
USER_EDIT = 'user_edit'
|
||||
USER_DELETE = 'user_delete'
|
||||
VCARD = 'vcard'
|
||||
VCARD_EDIT = 'vcard_edit'
|
||||
WRENCH = 'wrench'
|
||||
ZOOM = 'zoom'
|
||||
|
||||
11
apps/icons/registry.py
Normal file
11
apps/icons/registry.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .icons import icon_icons_app
|
||||
|
||||
name = 'icons'
|
||||
label = _(u'Icons')
|
||||
description = _(u'Handles the registration and rendering of icons and sprites.')
|
||||
dependencies = ['app_registry']
|
||||
icon = icon_icons_app
|
||||
@@ -2,17 +2,19 @@ from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from icons.sets import fat_cow, famfamfam
|
||||
from icons.sets import fat_cow, famfamfam, custom
|
||||
|
||||
|
||||
SET_CHOICES = (
|
||||
(fat_cow.ID, fat_cow.LABEL),
|
||||
(famfamfam.ID, famfamfam.LABEL),
|
||||
(custom.ID, custom.LABEL),
|
||||
)
|
||||
|
||||
ICON_THEMES = {
|
||||
fat_cow.ID: fat_cow.DICTIONARY,
|
||||
famfamfam.ID: famfamfam.DICTIONARY
|
||||
fat_cow.ID: fat_cow,
|
||||
famfamfam.ID: famfamfam,
|
||||
custom.ID: custom,
|
||||
}
|
||||
|
||||
|
||||
|
||||
12
apps/icons/sets/custom.py
Normal file
12
apps/icons/sets/custom.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from icons.literals import *
|
||||
|
||||
PATH = 'custom'
|
||||
ID = 'custom'
|
||||
LABEL = _(u'Custom')
|
||||
|
||||
DICTIONARY = {
|
||||
FILE_EXTENSION_ERROR: 'file_extension_error.png',
|
||||
FILE_EXTENSION_UNKNOWN: 'file_extension_unknown.png'
|
||||
}
|
||||
@@ -2,10 +2,11 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from icons.literals import *
|
||||
|
||||
PATH = 'famfamfam'
|
||||
ID = 'famfamfam'
|
||||
LABEL = _(u'FamFamFam')
|
||||
|
||||
DICTIONARY = {
|
||||
APP: 'plugin',
|
||||
BACKUPS: 'cd_burn',
|
||||
PLUGIN: 'plugin',
|
||||
CD_BURN: 'cd_burn',
|
||||
}
|
||||
|
||||
@@ -1,16 +1,70 @@
|
||||
import os
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from icons.literals import *
|
||||
|
||||
PATH = os.path.join('Fat Cow', '32x32')
|
||||
PATH = 'fat_cow'
|
||||
ID = 'fat_cow'
|
||||
LABEL = _(u'Fat cow')
|
||||
|
||||
DICTIONARY = {
|
||||
APP: 'plugin.png',
|
||||
BACKUPS: 'cd_burn.png',
|
||||
APPLICATION_VIEW_ICONS: 'application_view_icons.png',
|
||||
BLACKBOARD_SUM: 'blackboard_sum.png',
|
||||
BOOK: 'book.png',
|
||||
BOOK_GO: 'book_go.png',
|
||||
BOOK_OPEN: 'book_open.png',
|
||||
CD_BURN: 'cd_burn.png',
|
||||
COG: 'cog.png',
|
||||
COMPUTER_KEY: 'computer_key.png',
|
||||
CROSS: 'cross.png',
|
||||
DRAW_AIRBRUSH: 'draw_airbrush.png',
|
||||
DOCUMENT_SIGNATURE: 'document_signature.png',
|
||||
ERROR: 'error.png',
|
||||
ICONS: 'application_view_icons.png',
|
||||
GROUP: 'group.png',
|
||||
GROUP_ADD: 'group_add.png',
|
||||
GROUP_EDIT: 'group_edit.png',
|
||||
GROUP_DELETE: 'group_delete.png',
|
||||
GROUP_KEY: 'group_key.png',
|
||||
INFORMATION: 'information.png',
|
||||
KEY: 'key.png',
|
||||
KEY_GO: 'key_go.png',
|
||||
KEY_ADD: 'key_add.png',
|
||||
KEY_DELETE: 'key_delete.png',
|
||||
KEYBOARD: 'keyboard.png',
|
||||
LAYOUT: 'layout.png',
|
||||
LIGHTNING: 'lightning.png',
|
||||
LOCK: 'lock.png',
|
||||
MAGNIFIER: 'magnifier.png',
|
||||
MEDAL_GOLD: 'medal_gold_1.png',
|
||||
MEDAL_GOLD_ADD: 'medal_gold_add.png',
|
||||
MEDAL_GOLD_DELETE: 'medal_gold_delete.png',
|
||||
PAGE: 'page.png',
|
||||
PAGE_COPY: 'page_copy.png',
|
||||
PAGE_GEAR:'page_gear.png',
|
||||
PAGE_DELETE: 'page_delete.png',
|
||||
PAGE_EDIT: 'page_edit.png',
|
||||
PAGE_REFRESH: 'page_refresh.png',
|
||||
PAGE_SAVE: 'page_save.png',
|
||||
PAGE_WHITE_COPY: 'page_white_copy.png',
|
||||
PAGE_WORLD: 'page_world.png',
|
||||
PICTURES: 'pictures.png',
|
||||
PILL: 'pill.png',
|
||||
PLUGIN: 'plugin.png',
|
||||
PRINTER: 'printer.png',
|
||||
RAINBOW: 'rainbow.png',
|
||||
ROUTING_TURNAROUND_RIGHT: 'routing_turnaround_right.png',
|
||||
SCRIPT: 'script.png',
|
||||
STORAGE: 'storage.png',
|
||||
TABLE: 'table.png',
|
||||
TABLE_RELATIONSHIP: 'table_relationship.png',
|
||||
TEXT_DROPCAPS: 'text_dropcaps.png',
|
||||
TEXT_STRIKETHROUGH: 'text_strikethrough.png',
|
||||
TICK: 'tick.png',
|
||||
USER: 'user.png',
|
||||
USER_ADD:'user_add.png',
|
||||
USER_EDIT: 'user_edit.png',
|
||||
USER_DELETE: 'user_delete.png',
|
||||
VCARD: 'vcard.png',
|
||||
VCARD_EDIT: 'vcard_edit.png',
|
||||
WRENCH: 'wrench.png',
|
||||
ZOOM: 'zoom.png',
|
||||
}
|
||||
|
||||
13
apps/icons/settings.py
Normal file
13
apps/icons/settings.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .literals import DEFAULT_ICON_SET
|
||||
|
||||
|
||||
#
|
||||
# #print '__file__', __file__
|
||||
# ICON_SET = app.add_setting(
|
||||
# name='ICON_SET',
|
||||
# default=literals.DEFAULT_ICON_SET,
|
||||
# )
|
||||
|
||||
ICON_SET = DEFAULT_ICON_SET
|
||||
BIN
apps/icons/static/icons/custom/32x32/ODF_database_32x32.png
Normal file
BIN
apps/icons/static/icons/custom/32x32/ODF_database_32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
apps/icons/static/icons/custom/32x32/ODF_drawing_32x32.png
Normal file
BIN
apps/icons/static/icons/custom/32x32/ODF_drawing_32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
apps/icons/static/icons/custom/32x32/ODF_drawing_templ_32x32.png
Normal file
BIN
apps/icons/static/icons/custom/32x32/ODF_drawing_templ_32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
apps/icons/static/icons/custom/32x32/ODF_empty_32x32.png
Normal file
BIN
apps/icons/static/icons/custom/32x32/ODF_empty_32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 950 B |
BIN
apps/icons/static/icons/custom/32x32/ODF_empty_templ_32x32.png
Normal file
BIN
apps/icons/static/icons/custom/32x32/ODF_empty_templ_32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 966 B |
BIN
apps/icons/static/icons/custom/32x32/ODF_formula_32x32.png
Normal file
BIN
apps/icons/static/icons/custom/32x32/ODF_formula_32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user