Merge branch 'feature/icon_server' into development

This commit is contained in:
Roberto Rosario
2012-09-11 00:16:01 -04:00
5004 changed files with 2238 additions and 1548 deletions

View File

@@ -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
View 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)

View File

@@ -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
View 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']

View File

@@ -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()

View File

@@ -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

View File

@@ -0,0 +1,6 @@
from __future__ import absolute_import
from icons.literals import PLUGIN
from icons import Icon
icon_app = Icon(PLUGIN)

View File

@@ -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])

View File

@@ -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']

View File

@@ -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

View 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']

View File

@@ -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'),
)

View File

@@ -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
View 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()])

View File

@@ -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
View 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
View 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
View 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
View 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
View 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))

View File

@@ -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'])
"""

View File

@@ -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()

View File

@@ -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()])

View File

@@ -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
View 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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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
View 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
View 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()]
)

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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):

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
View File

@@ -0,0 +1,7 @@
from __future__ import absolute_import
from icons.literals import PICTURES
from icons import Icon
icon_format_list = Icon(PICTURES)

View File

@@ -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'])

View File

@@ -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)

View 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'},

View File

@@ -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)

View File

@@ -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.

View File

@@ -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):

View 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
View 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)

View File

@@ -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_'])

View 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()],
}
]

View File

@@ -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)

View File

@@ -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)])

View File

@@ -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
)

View File

@@ -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
View 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)

View File

@@ -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', ])

View File

@@ -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 []

View File

@@ -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

View 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
View 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]

View File

@@ -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:

View File

@@ -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'),

View File

@@ -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):

View File

@@ -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,

View File

@@ -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(),
}
)

View File

@@ -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/']))

View File

@@ -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.')
)

View 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.')

View File

@@ -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
View 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)

View File

@@ -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])

View File

@@ -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
View 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]

View File

@@ -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

View File

@@ -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
View 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))

View File

@@ -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
View File

@@ -0,0 +1,7 @@
from __future__ import absolute_import
from .literals import DRAW_AIRBRUSH
from . import Icon
icon_icons_app = Icon(DRAW_AIRBRUSH)

View File

@@ -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
View 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

View File

@@ -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
View 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'
}

View File

@@ -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',
}

View File

@@ -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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

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