Merge remote branch 'origin/master'
Conflicts: docs/TODO
This commit is contained in:
36
apps/common/templatetags/settings.py
Normal file
36
apps/common/templatetags/settings.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import re
|
||||
|
||||
from django.template import Node, Variable
|
||||
from django.template import TemplateSyntaxError, Library, VariableDoesNotExist
|
||||
from django.template.defaultfilters import stringfilter
|
||||
from django.template.defaultfilters import date as datefilter
|
||||
from django.conf import settings
|
||||
|
||||
register = Library()
|
||||
|
||||
|
||||
class SettingsNode(Node):
|
||||
def __init__(self, format_string, var_name):
|
||||
self.format_string = format_string
|
||||
self.var_name = var_name
|
||||
def render(self, context):
|
||||
#context[self.var_name] = settings(self.format_string)
|
||||
context[self.var_name] = getattr(settings, self.format_string, '')
|
||||
return ''
|
||||
|
||||
|
||||
@register.tag
|
||||
def get_setting(parser, token):
|
||||
# This version uses a regular expression to parse tag contents.
|
||||
try:
|
||||
# Splitting by None == splitting by spaces.
|
||||
tag_name, arg = token.contents.split(None, 1)
|
||||
except ValueError:
|
||||
raise TemplateSyntaxError, "%r tag requires arguments" % token.contents.split()[0]
|
||||
m = re.search(r'(.*?) as (\w+)', arg)
|
||||
if not m:
|
||||
raise TemplateSyntaxError, "%r tag had invalid arguments" % tag_name
|
||||
format_string, var_name = m.groups()
|
||||
if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
|
||||
raise TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name
|
||||
return SettingsNode(format_string[1:-1], var_name)
|
||||
@@ -12,15 +12,10 @@ document_view = {'text':_('details'), 'view':'document_view', 'args':'object.id'
|
||||
document_delete = {'text':_('delete'), 'view':'document_delete', 'args':'object.id', 'famfam':'page_delete'}
|
||||
document_edit = {'text':_('edit'), 'view':'document_edit', 'args':'object.id', 'famfam':'page_edit'}
|
||||
|
||||
#document_create_from_staging = {'text':_('upload file'), 'view':'document_create_from_staging', 'args':{'file_id':'object.id', 'document_type_id': 'document_type_id'}, 'famfam':'page_add'}
|
||||
|
||||
|
||||
register_links(Document, [document_view, document_edit, document_delete])
|
||||
register_links(Document, [document_list, document_create, document_create_multiple], menu_name='sidebar')
|
||||
register_links(['document_list', 'document_create', 'document_create_multiple', 'upload_document_with_type', 'upload_multiple_documents_with_type'], [document_list, document_create, document_create_multiple], menu_name='sidebar')
|
||||
|
||||
#register_links(StagingFile, [document_create_from_staging])
|
||||
|
||||
|
||||
register_menu([
|
||||
{'text':_('documents'), 'view':'document_list', 'links':[
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from models import MetadataType, DocumentType, Document, \
|
||||
DocumentTypeMetadataType, DocumentMetadata, DocumentTypeFilename#, \
|
||||
# DocumentFile
|
||||
DocumentTypeMetadataType, DocumentMetadata, DocumentTypeFilename, \
|
||||
MetadataIndex, DocumentMetadataIndex
|
||||
|
||||
|
||||
class MetadataTypeAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'default', 'lookup')
|
||||
|
||||
list_display = ('name', 'title', 'default', 'lookup')
|
||||
|
||||
|
||||
class MetadataIndexInline(admin.StackedInline):
|
||||
model = MetadataIndex
|
||||
extra = 1
|
||||
classes = ('collapse-open',)
|
||||
allow_add = True
|
||||
|
||||
|
||||
class DocumentTypeMetadataTypeInline(admin.StackedInline):
|
||||
model = DocumentTypeMetadataType
|
||||
@@ -24,27 +31,28 @@ class DocumentTypeFilenameInline(admin.StackedInline):
|
||||
|
||||
|
||||
class DocumentTypeAdmin(admin.ModelAdmin):
|
||||
inlines = [DocumentTypeMetadataTypeInline, DocumentTypeFilenameInline]
|
||||
inlines = [DocumentTypeFilenameInline, DocumentTypeMetadataTypeInline, MetadataIndexInline]
|
||||
|
||||
|
||||
class DocumentMetadataInline(admin.StackedInline):
|
||||
model = DocumentMetadata
|
||||
extra = 0
|
||||
classes = ('collapse-open',)
|
||||
allow_add = False
|
||||
readonly_fields = ('metadata_type', 'value')
|
||||
|
||||
|
||||
class DocumentMetadataIndexInline(admin.StackedInline):
|
||||
model = DocumentMetadataIndex
|
||||
extra = 1
|
||||
classes = ('collapse-open',)
|
||||
allow_add = True
|
||||
|
||||
|
||||
#class DocumentFileInline(admin.StackedInline):
|
||||
# model = DocumentFile
|
||||
# extra = 1
|
||||
# classes = ('collapse-open',)
|
||||
# allow_add = True
|
||||
allow_add = True
|
||||
readonly_fields = ('metadata_index', 'filename')
|
||||
|
||||
|
||||
class DocumentAdmin(admin.ModelAdmin):
|
||||
#inlines = [DocumentFileInline]#, DocumentMetadataInline,]
|
||||
inlines = [DocumentMetadataInline]
|
||||
list_display = ('uuid',)
|
||||
inlines = [DocumentMetadataInline, DocumentMetadataIndexInline]
|
||||
list_display = ('uuid', 'file_filename', 'file_extension')
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -33,3 +33,4 @@ STORAGE_DIRECTORY_NAME = getattr(settings, 'DOCUMENTS_STORAGE_DIRECTORY_NAME', '
|
||||
FILESYSTEM_FILESERVING_ENABLE = getattr(settings, 'DOCUMENTS_FILESYSTEM_FILESERVING_ENABLE', True)
|
||||
FILESYSTEM_FILESERVING_PATH = getattr(settings, 'DOCUMENTS_FILESERVING_PATH', u'/tmp/mayan/documents')
|
||||
FILESYSTEM_SLUGIFY_PATHS = getattr(settings, 'DOCUMENTS_SLUGIFY_PATHS', False)
|
||||
FILESYSTEM_MAX_RENAME_COUNT = getattr(settings, 'DOCUMENTS_FILESYSTEM_MAX_RENAME_COUNT', 200)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils.http import urlencode
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -84,7 +85,17 @@ class MetadataForm(forms.Form):
|
||||
self.document_type = kwargs['initial'].pop('document_type', None)
|
||||
self.metadata_options = kwargs['initial'].pop('metadata_options', None)
|
||||
|
||||
self.fields['name'].initial=self.metadata_type.name
|
||||
|
||||
required=self.document_type.documenttypemetadatatype_set.get(metadata_type=self.metadata_type).required
|
||||
required_string = u''
|
||||
if required:
|
||||
self.fields['value'].required=True
|
||||
required_string = ' (%s)' % ugettext(u'required')
|
||||
else:
|
||||
#TODO: FIXME: not working correctly
|
||||
self.fields['value'].required=False
|
||||
|
||||
self.fields['name'].initial='%s%s' % ((self.metadata_type.title if self.metadata_type.title else self.metadata_type.name), required_string)
|
||||
self.fields['id'].initial=self.metadata_type.id
|
||||
if self.metadata_type.default:
|
||||
try:
|
||||
@@ -97,6 +108,7 @@ class MetadataForm(forms.Form):
|
||||
choices = eval(self.metadata_type.lookup, AVAILABLE_MODELS)
|
||||
self.fields['value'] = forms.ChoiceField(label=self.fields['value'].label)
|
||||
self.fields['value'].choices = zip(choices, choices)
|
||||
self.fields['value'].required = False
|
||||
except Exception, err:
|
||||
self.fields['value'].initial = err
|
||||
self.fields['value'].widget=forms.TextInput(attrs={'readonly':'readonly'})
|
||||
@@ -104,7 +116,7 @@ class MetadataForm(forms.Form):
|
||||
id = forms.CharField(label=_(u'id'), widget=forms.HiddenInput)
|
||||
name = forms.CharField(label=_(u'Name'),
|
||||
required=False, widget=forms.TextInput(attrs={'readonly':'readonly'}))
|
||||
value = forms.CharField(label=_(u'Value'))
|
||||
value = forms.CharField(label=_(u'Value'), required=False)
|
||||
|
||||
|
||||
class DocumentCreateWizard(BoundFormWizard):
|
||||
@@ -138,8 +150,9 @@ class DocumentCreateWizard(BoundFormWizard):
|
||||
if step == 1:
|
||||
self.urldata = []
|
||||
for id, metadata in enumerate(form.cleaned_data):
|
||||
self.urldata.append(('metadata%s_id' % id,metadata['id']))
|
||||
self.urldata.append(('metadata%s_value' % id,metadata['value']))
|
||||
if metadata['value']:
|
||||
self.urldata.append(('metadata%s_id' % id,metadata['id']))
|
||||
self.urldata.append(('metadata%s_value' % id,metadata['value']))
|
||||
|
||||
|
||||
def get_template(self, step):
|
||||
|
||||
@@ -2,6 +2,7 @@ import errno
|
||||
import os
|
||||
import mimetypes
|
||||
from datetime import datetime
|
||||
import sys
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
@@ -20,6 +21,7 @@ from documents.conf.settings import STORAGE_DIRECTORY_NAME
|
||||
from documents.conf.settings import FILESYSTEM_FILESERVING_ENABLE
|
||||
from documents.conf.settings import FILESYSTEM_FILESERVING_PATH
|
||||
from documents.conf.settings import FILESYSTEM_SLUGIFY_PATHS
|
||||
from documents.conf.settings import FILESYSTEM_MAX_RENAME_COUNT
|
||||
|
||||
|
||||
if FILESYSTEM_SLUGIFY_PATHS == False:
|
||||
@@ -47,7 +49,7 @@ class DocumentType(models.Model):
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
|
||||
class Document(models.Model):
|
||||
""" Minimum fields for a document entry.
|
||||
@@ -57,6 +59,7 @@ class Document(models.Model):
|
||||
file = models.FileField(upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'file'))
|
||||
uuid = models.CharField(max_length=48, default=UUID_FUNCTION(), blank=True, editable=False)
|
||||
file_mimetype = models.CharField(max_length=64, default='', editable=False)
|
||||
#FAT filename can be up to 255 using LFN
|
||||
file_filename = models.CharField(max_length=64, default='', editable=False)
|
||||
file_extension = models.CharField(max_length=16, default='', editable=False)
|
||||
date_added = models.DateTimeField(verbose_name=_(u'added'), auto_now_add=True)
|
||||
@@ -69,7 +72,6 @@ class Document(models.Model):
|
||||
ordering = ['-date_updated', '-date_added']
|
||||
|
||||
def __unicode__(self):
|
||||
#return self.uuid
|
||||
return '%s.%s' % (self.file_filename, self.file_extension)
|
||||
|
||||
@models.permalink
|
||||
@@ -89,61 +91,129 @@ class Document(models.Model):
|
||||
super(Document, self).save(*args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
#TODO: Might not execute when done in bulk from a queryset
|
||||
#topics/db/queries.html#topics-db-queries-delete
|
||||
self.delete_fs_links()
|
||||
super(Document, self).delete(*args, **kwargs)
|
||||
|
||||
def calculate_fs_links(self):
|
||||
targets = []
|
||||
for metadata in self.documentmetadata_set.all():
|
||||
if metadata.metadata_type.documenttypemetadatatype_set.all()[0].create_directory_link:
|
||||
target_directory = os.path.join(FILESYSTEM_FILESERVING_PATH, slugify(metadata.metadata_type.name), slugify(metadata.value))
|
||||
targets.append(os.path.join(target_directory, os.extsep.join([slugify(self.file_filename), slugify(self.file_extension)])))
|
||||
return targets
|
||||
|
||||
def create_fs_links(self):
|
||||
if FILESYSTEM_FILESERVING_ENABLE:
|
||||
for target in self.calculate_fs_links():
|
||||
try:
|
||||
os.makedirs(os.path.dirname(target))
|
||||
except OSError, exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
metadata_dict = {'document':self}
|
||||
metadata_dict.update(dict([(metadata.metadata_type.name, slugify(metadata.value)) for metadata in self.documentmetadata_set.all()]))
|
||||
|
||||
for metadata_index in self.document_type.metadataindex_set.all():
|
||||
if metadata_index.enabled:
|
||||
try:
|
||||
fabricated_directory = eval(metadata_index.expression, metadata_dict)
|
||||
target_directory = os.path.join(FILESYSTEM_FILESERVING_PATH, fabricated_directory)
|
||||
try:
|
||||
os.makedirs(target_directory)
|
||||
except OSError, exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
pass
|
||||
else:
|
||||
raise OSError(ugettext(u'Unable to create metadata indexing directory: %s') % exc)
|
||||
|
||||
|
||||
next_available_filename(self, metadata_index, target_directory, slugify(self.file_filename), slugify(self.file_extension))
|
||||
except NameError:
|
||||
#Error in eval
|
||||
#TODO: find way to notify user
|
||||
pass
|
||||
else:
|
||||
raise OSError(ugettext(u'Unable to create metadata indexing directory: %s') % exc)
|
||||
try:
|
||||
os.symlink(os.path.abspath(self.file.path), target)
|
||||
except OSError, exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
pass
|
||||
else:
|
||||
raise OSError(ugettext(u'Unable to create metadata indexing symbolic link: %s') % exc)
|
||||
|
||||
|
||||
def delete_fs_links(self):
|
||||
if FILESYSTEM_FILESERVING_ENABLE:
|
||||
for target in self.calculate_fs_links():
|
||||
for document_metadata_index in self.documentmetadataindex_set.all():
|
||||
try:
|
||||
os.unlink(target)
|
||||
os.unlink(document_metadata_index.filename)
|
||||
document_metadata_index.delete()
|
||||
except OSError, exc:
|
||||
if exc.errno == errno.ENOENT:
|
||||
pass
|
||||
#No longer exits, so delete db entry anyway
|
||||
document_metadata_index.delete()
|
||||
else:
|
||||
raise OSError(ugettext(u'Unable to delete metadata indexing symbolic link: %s') % exc)
|
||||
|
||||
|
||||
path, filename = os.path.split(document_metadata_index.filename)
|
||||
|
||||
#Cleanup directory of dead stuff
|
||||
#Delete siblings that are dead links
|
||||
try:
|
||||
for f in os.listdir(path):
|
||||
filepath = os.path.join(path, f)
|
||||
if os.path.islink(filepath):
|
||||
#Get link's source
|
||||
source = os.readlink(filepath)
|
||||
if os.path.isabs(source):
|
||||
if not os.path.exists(source):
|
||||
#link's source is absolute and doesn't exit
|
||||
os.unlink(filepath)
|
||||
else:
|
||||
os.unlink(os.path.join(path, filepath))
|
||||
elif os.path.isdir(filepath):
|
||||
#is a directory, try to delete it
|
||||
try:
|
||||
os.removedirs(path)
|
||||
except:
|
||||
pass
|
||||
except OSError, exc:
|
||||
pass
|
||||
|
||||
#Remove the directory if it is empty
|
||||
try:
|
||||
os.removedirs(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
def next_available_filename(document, metadata_index, path, filename, extension, suffix=0):
|
||||
target = filename
|
||||
if suffix:
|
||||
target = '_'.join([filename, unicode(suffix)])
|
||||
filepath = os.path.join(path, os.extsep.join([target, extension]))
|
||||
matches=DocumentMetadataIndex.objects.filter(filename=filepath)
|
||||
if matches.count() == 0:
|
||||
document_metadata_index = DocumentMetadataIndex(
|
||||
document=document, metadata_index=metadata_index,
|
||||
filename=filepath)
|
||||
try:
|
||||
os.symlink(os.path.abspath(document.file.path), filepath)
|
||||
document_metadata_index.save()
|
||||
except OSError, exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
#This link should not exist, try to delete it
|
||||
try:
|
||||
os.unlink(filepath)
|
||||
#Try again with same suffix
|
||||
return next_available_filename(document, metadata_index, path, filename, extension, suffix)
|
||||
except Exception, exc:
|
||||
raise Exception(ugettext(u'Unable to create symbolic link, filename clash: %s; %s') % (filepath, exc))
|
||||
|
||||
else:
|
||||
raise OSError(ugettext(u'Unable to create symbolic link: %s; %s') % (filepath, exc))
|
||||
|
||||
return filepath
|
||||
else:
|
||||
if suffix > FILESYSTEM_MAX_RENAME_COUNT:
|
||||
raise Exception(ugettext(u'Maximum rename count reached, not creating symbolic link'))
|
||||
return next_available_filename(document, metadata_index, path, filename, extension, suffix+1)
|
||||
|
||||
|
||||
available_functions_string = (_(u' Available functions: %s') % ','.join(['%s()' % name for name, function in AVAILABLE_FUNCTIONS.items()])) if AVAILABLE_FUNCTIONS else ''
|
||||
available_models_string = (_(u' Available models: %s') % ','.join([name for name, model in AVAILABLE_MODELS.items()])) if AVAILABLE_MODELS else ''
|
||||
|
||||
class MetadataType(models.Model):
|
||||
name = models.CharField(max_length=48, verbose_name=_(u'name'))
|
||||
# title = models.CharField(max_length=48, verbose_name=_(u'title'), blank=True, null=True)
|
||||
title = models.CharField(max_length=48, verbose_name=_(u'title'), blank=True, null=True)
|
||||
default = models.CharField(max_length=128, blank=True, null=True,
|
||||
verbose_name=_(u'default'), help_text=_(u'Enter a string to be evaluated.%s') % available_functions_string)
|
||||
verbose_name=_(u'default'),
|
||||
help_text=_(u'Enter a string to be evaluated.%s') % available_functions_string)
|
||||
lookup = models.CharField(max_length=128, blank=True, null=True,
|
||||
verbose_name=_(u'lookup'), help_text=_(u'Enter a string to be evaluated. Example: [user.get_full_name() for user in User.objects.all()].%s') % available_models_string)
|
||||
#datatype = models.
|
||||
verbose_name=_(u'lookup'),
|
||||
help_text=_(u'Enter a string to be evaluated. Example: [user.get_full_name() for user in User.objects.all()].%s') % available_models_string)
|
||||
#TODO: datatype?
|
||||
|
||||
def __unicode__(self):
|
||||
# return self.title if self.title else self.name
|
||||
#return '%s - %s' % (self.name, self.title if self.title else self.name)
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
@@ -151,25 +221,11 @@ class MetadataType(models.Model):
|
||||
verbose_name_plural = _(u'metadata types')
|
||||
|
||||
|
||||
#class MetadataIndexing(models.Model):
|
||||
# metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type'))
|
||||
# indexing_string = models.CharField(
|
||||
#
|
||||
#
|
||||
# def __unicode__(self):
|
||||
# return unicode(self.metadata_type)
|
||||
#
|
||||
# class Meta:
|
||||
# verbose_name = _(u'metadata type')
|
||||
# verbose_name_plural = _(u'metadata types')
|
||||
|
||||
|
||||
class DocumentTypeMetadataType(models.Model):
|
||||
document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type'))
|
||||
metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type'))
|
||||
create_directory_link = models.BooleanField(verbose_name=_(u'create directory link'))
|
||||
#override default
|
||||
#required? -bool
|
||||
required = models.BooleanField(default=True, verbose_name=_(u'required'))
|
||||
#TODO: override default for this document type
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.metadata_type)
|
||||
@@ -179,6 +235,35 @@ class DocumentTypeMetadataType(models.Model):
|
||||
verbose_name_plural = _(u'document type metadata type connectors')
|
||||
|
||||
|
||||
class MetadataIndex(models.Model):
|
||||
document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type'))
|
||||
expression = models.CharField(max_length=128,
|
||||
verbose_name=_(u'indexing expression'),
|
||||
help_text=_(u'Enter a python string expression to be evaluated. The slash caracter "/" acts as a directory delimiter.'))
|
||||
enabled = models.BooleanField(default=True, verbose_name=_(u'enabled'))
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.expression)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _(u'metadata index')
|
||||
verbose_name_plural = _(u'metadata indexes')
|
||||
|
||||
|
||||
class DocumentMetadataIndex(models.Model):
|
||||
document = models.ForeignKey(Document, verbose_name=_(u'document'))
|
||||
metadata_index = models.ForeignKey(MetadataIndex, verbose_name=_(u'metadata index'))
|
||||
filename = models.CharField(max_length=128, verbose_name=_(u'filename'))
|
||||
suffix = models.PositiveIntegerField(default=0, verbose_name=_(u'suffix'))
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.filename)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _(u'document metadata index')
|
||||
verbose_name_plural = _(u'document metadata indexes')
|
||||
|
||||
|
||||
class DocumentMetadata(models.Model):
|
||||
document = models.ForeignKey(Document, verbose_name=_(u'document'))
|
||||
metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type'))
|
||||
|
||||
@@ -11,7 +11,7 @@ from django.utils.translation import ugettext
|
||||
from documents.conf.settings import STAGING_DIRECTORY
|
||||
|
||||
HASH_FUNCTION = lambda x: hashlib.sha256(x).hexdigest()
|
||||
|
||||
#TODO: Do benchmarks
|
||||
#func = lambda:[StagingFile.get_all() is None for i in range(100)]
|
||||
#t1=time.time();func();t2=time.time();print '%s took %0.3f ms' % (func.func_name, (t2-t1)*1000.0)
|
||||
|
||||
|
||||
@@ -12,5 +12,4 @@ urlpatterns = patterns('documents.views',
|
||||
url(r'^document/(?P<document_id>\d+)/$', 'document_view', (), 'document_view'),
|
||||
url(r'^document/(?P<document_id>\d+)/delete/$', 'document_delete', (), 'document_delete'),
|
||||
url(r'^document/(?P<document_id>\d+)/edit/$', 'document_edit', (), 'document_edit'),
|
||||
#url(r'^document/type/(?P<document_type_id>\d+)/upload/from/staging/(?P<file_id>\d+)/single/$', 'document_create_from_staging', {'multiple':True}, 'document_create_from_staging'),
|
||||
)
|
||||
|
||||
@@ -20,6 +20,8 @@ from staging import StagingFile
|
||||
|
||||
from documents.conf.settings import DELETE_STAGING_FILE_AFTER_UPLOAD
|
||||
from documents.conf.settings import USE_STAGING_DIRECTORY
|
||||
from documents.conf.settings import FILESYSTEM_FILESERVING_ENABLE
|
||||
|
||||
|
||||
def document_list(request):
|
||||
return object_list(
|
||||
@@ -29,8 +31,6 @@ def document_list(request):
|
||||
extra_context={
|
||||
'title':_(u'documents'),
|
||||
'extra_columns':[
|
||||
#{'name':_(u'filename'), 'attribute':'file_filename'},
|
||||
#{'name':_(u'extension'), 'attribute':'file_extension'},
|
||||
{'name':_(u'mimetype'), 'attribute':'file_mimetype'},
|
||||
{'name':_(u'added'), 'attribute':lambda x: x.date_added.date()},
|
||||
],
|
||||
@@ -189,10 +189,7 @@ def document_view(request, document_id):
|
||||
{'label':_(u'Exists in storage'), 'field':'exists'}
|
||||
])
|
||||
|
||||
return render_to_response('generic_detail.html', {
|
||||
'form':form,
|
||||
'object':document,
|
||||
'subtemplates_dict':[
|
||||
subtemplates_dict = [
|
||||
{
|
||||
'name':'generic_list_subtemplate.html',
|
||||
'title':_(u'metadata'),
|
||||
@@ -200,7 +197,19 @@ def document_view(request, document_id):
|
||||
'extra_columns':[{'name':_(u'value'), 'attribute':'value'}],
|
||||
'hide_link':True,
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
if FILESYSTEM_FILESERVING_ENABLE:
|
||||
subtemplates_dict.append({
|
||||
'name':'generic_list_subtemplate.html',
|
||||
'title':_(u'index links'),
|
||||
'object_list':document.documentmetadataindex_set.all(),
|
||||
'hide_link':True})
|
||||
|
||||
return render_to_response('generic_detail.html', {
|
||||
'form':form,
|
||||
'object':document,
|
||||
'subtemplates_dict':subtemplates_dict,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@@ -255,39 +264,3 @@ def document_edit(request, document_id):
|
||||
'object':document,
|
||||
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
'''
|
||||
def document_create_from_staging(request, file_id, document_type_id, multiple=True):
|
||||
if USE_STAGING_DIRECTORY:
|
||||
document_type = get_object_or_404(DocumentType, pk=document_type_id)
|
||||
staging_file = StagingFile.get(id=int(file_id))
|
||||
|
||||
try:
|
||||
document = Document(file=staging_file.upload(), document_type=document_type)
|
||||
document.save()
|
||||
except Exception, e:
|
||||
messages.error(request, e)
|
||||
else:
|
||||
url = urlparse(request.META['HTTP_REFERER'])
|
||||
#Take the url parameter defining the metadata values and turn
|
||||
# then into a dictionary
|
||||
params = dict([part.split('=') for part in url[4].split('&')])
|
||||
_save_metadata(params, document)
|
||||
messages.success(request, _(u'Staging file: %s, uploaded successfully.') % staging_file.filename)
|
||||
try:
|
||||
document.create_fs_links()
|
||||
except Exception, e:
|
||||
messages.error(request, e)
|
||||
|
||||
if DELETE_STAGING_FILE_AFTER_UPLOAD:
|
||||
try:
|
||||
staging_file.delete()
|
||||
messages.success(request, _(u'Staging file: %s, deleted successfully.') % staging_file.filename)
|
||||
except Exception, e:
|
||||
messages.error(request, e)
|
||||
|
||||
if multiple:
|
||||
return HttpResponseRedirect(request.META['HTTP_REFERER'])
|
||||
else:
|
||||
return HttpResponseRedirect(reverse('document_list'))
|
||||
'''
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{% load i18n %}
|
||||
{% load project_tags %}
|
||||
{% load navigation %}
|
||||
{% load settings %}
|
||||
|
||||
{% block html_title %}{% project_name %}{% block title %}{% endblock %}{% endblock %}
|
||||
|
||||
@@ -48,7 +49,8 @@
|
||||
<input type="submit" value="{% trans 'Go' %}" />
|
||||
</form>
|
||||
</li>
|
||||
<li><a class="logout" href="{% if user.is_anonymous %}{% url login_view %}?next=/{% else %}{% url logout_view %}{% endif %}">{% if user.is_anonymous %}{% trans 'Login' %}{% else %}{% trans 'Logout' %}{% endif %}</a></li>
|
||||
{% get_setting "LOGIN_URL" as login_url %}
|
||||
<li><a class="logout" href="{% if user.is_anonymous %}{% url login_view %}?next=/{% else %}{% url logout_view %}?next={{ login_url }}{% endif %}">{% if user.is_anonymous %}{% trans 'Login' %}{% else %}{% trans 'Logout' %}{% endif %}</a></li>
|
||||
{% endblock %}
|
||||
|
||||
{% block web_theme_main_navigation %}
|
||||
@@ -107,6 +109,7 @@
|
||||
{% block sidebar %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block web_theme_messages %}{% block messages %}{% endblock %}{% endblock %}
|
||||
|
||||
{% block web_theme_content %}{% block content %}{% endblock %}{% endblock %}
|
||||
|
||||
|
||||
18
docs/TODO
18
docs/TODO
@@ -10,6 +10,8 @@
|
||||
* Jquery upload document upload form with ajax widget - NOT NEEDED (commit: b0f31f2a8f82ff0daca081005f2fcae3f5573df5)
|
||||
* Rename dropbox from document edit view - DONE
|
||||
* Ability to rename staging file during upload - DONE
|
||||
* Implement single sign on or LDAP for intranets - DEFERRED, provided by Django AuthBackends
|
||||
* Database storage backend (sql, nosql: [mongodb]) - DEFERRED, provided by https://bitbucket.org/david/django-storages/wiki/Home
|
||||
* Document list filtering by metadata
|
||||
* Filterform date filtering widget
|
||||
* Validate GET data before saving file
|
||||
@@ -20,13 +22,25 @@
|
||||
* Show last 5 recent metadata setups for easy switch
|
||||
* Change to use model signals
|
||||
* Allow document type to be changed in document edit view
|
||||
* Implement single sign on or LDAP for intranets
|
||||
* Encrypting storage backend
|
||||
* Indicate in generic list which document don't exist in storage backend
|
||||
* Database storage backend (sql, nosql: [mongodb])
|
||||
* Add css grids
|
||||
* Document previews
|
||||
* Recognize multi-page documents
|
||||
* Staging file previews
|
||||
* Autodelete empty fs directories
|
||||
* Auto check and delete dead sym links
|
||||
* Document model's delete method might not get called when deleting in bulk
|
||||
from a queryset
|
||||
* Allow metadata entry form to mix required and non required metadata
|
||||
* Link to delete and recreate all document links
|
||||
* MuliThreading deferred OCR
|
||||
* Document previews on demand w/ imagemagick
|
||||
* Versioning support
|
||||
* Generic document anotations using layer overlays
|
||||
* Permissions
|
||||
* Roles
|
||||
* Workflows
|
||||
* Scheduled maintenance (cleanup, deferred OCR's)
|
||||
* Show document metadata in document list
|
||||
* Show abbreviated uuid in document list
|
||||
|
||||
@@ -181,6 +181,7 @@ LOGIN_EXEMPT_URLS = (
|
||||
#DOCUMENTS_FILESYSTEM_FILESERVING_ENABLE = True
|
||||
#DOCUMENTS_FILESYSTEM_FILESERVING_PATH = u'/tmp/mayan/documents'
|
||||
#DOCUMENTS_FILESYSTEM_SLUGIFY_PATHS = False
|
||||
#DOCUMENTS_FILESYSTEM_MAX_RENAME_COUNT = 200
|
||||
#======== End of configuration options =======
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user