Got upload view working, staging preview and delete working ok
This commit is contained in:
@@ -13,9 +13,6 @@ from metadata.api import get_metadata_string
|
||||
|
||||
from documents.models import Document, DocumentPage, \
|
||||
DocumentPageTransformation, DocumentType, DocumentTypeFilename
|
||||
from documents.staging import StagingFile
|
||||
from documents.conf.settings import USE_STAGING_DIRECTORY
|
||||
from documents.conf.settings import PER_USER_STAGING_DIRECTORY
|
||||
from documents.literals import PERMISSION_DOCUMENT_CREATE, \
|
||||
PERMISSION_DOCUMENT_PROPERTIES_EDIT, PERMISSION_DOCUMENT_VIEW, \
|
||||
PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD, \
|
||||
@@ -107,13 +104,6 @@ document_page_rotate_left = {'text': _(u'rotate left'), 'class': 'no-parent-hist
|
||||
|
||||
document_missing_list = {'text': _(u'Find missing document files'), 'view': 'document_missing_list', 'famfam': 'folder_page', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
|
||||
|
||||
upload_document_from_local = {'text': _(u'local'), 'view': 'upload_document_from_local', 'famfam': 'drive_disk', 'keep_query': True}
|
||||
upload_document_from_staging = {'text': _(u'staging'), 'view': 'upload_document_from_staging', 'famfam': 'drive_network', 'keep_query': True, 'condition': lambda x: USE_STAGING_DIRECTORY}
|
||||
upload_document_from_user_staging = {'text': _(u'user staging'), 'view': 'upload_document_from_user_staging', 'famfam': 'drive_user', 'keep_query': True, 'condition': lambda x: PER_USER_STAGING_DIRECTORY}
|
||||
|
||||
staging_file_preview = {'text': _(u'preview'), 'class': 'fancybox-noscaling', 'view': 'staging_file_preview', 'args': ['source', 'object.id'], 'famfam': 'drive_magnify'}
|
||||
staging_file_delete = {'text': _(u'delete'), 'view': 'staging_file_delete', 'args': ['source', 'object.id'], 'famfam': 'drive_delete'}
|
||||
|
||||
# Document type related links
|
||||
document_type_list = {'text': _(u'document type list'), 'view': 'document_type_list', 'famfam': 'layout', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
|
||||
document_type_document_list = {'text': _(u'documents of this type'), 'view': 'document_type_document_list', 'args': 'object.id', 'famfam': 'page_go', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
|
||||
@@ -141,7 +131,7 @@ register_links(['document_type_filename_edit', 'document_type_filename_delete'],
|
||||
register_links(Document, [document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations, document_create_siblings])
|
||||
register_multi_item_links(['folder_view', 'index_instance_list', 'document_type_document_list', 'search', 'results', 'document_group_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_delete])
|
||||
|
||||
register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_document', 'upload_document_from_local', 'upload_document_from_staging', 'upload_document_from_user_staging', 'document_find_duplicates'], [document_list_recent, document_list, document_create_multiple], menu_name='secondary_menu')
|
||||
register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_interactive', 'document_find_duplicates'], [document_list_recent, document_list, document_create_multiple], menu_name='secondary_menu')
|
||||
|
||||
# Document page links
|
||||
register_links(DocumentPage, [
|
||||
@@ -157,17 +147,12 @@ register_links(DocumentPage, [
|
||||
|
||||
register_links(['document_page_view'], [document_page_rotate_left, document_page_rotate_right, document_page_zoom_in, document_page_zoom_out], menu_name='form_header')
|
||||
|
||||
# Upload sources
|
||||
register_links(['upload_document_from_local', 'upload_document_from_staging', 'upload_document_from_user_staging'], [upload_document_from_local, upload_document_from_staging, upload_document_from_user_staging], menu_name='form_header')
|
||||
|
||||
register_links(DocumentPageTransformation, [document_page_transformation_edit, document_page_transformation_delete])
|
||||
register_links(DocumentPageTransformation, [document_page_transformation_page_edit, document_page_transformation_page_view], menu_name='sidebar')
|
||||
register_links('document_page_transformation_list', [document_page_transformation_create], menu_name='sidebar')
|
||||
register_links('document_page_transformation_create', [document_page_transformation_create], menu_name='sidebar')
|
||||
register_links(['document_page_transformation_edit', 'document_page_transformation_delete'], [document_page_transformation_page_transformation_list], menu_name='sidebar')
|
||||
|
||||
register_links(StagingFile, [staging_file_preview, staging_file_delete])
|
||||
|
||||
register_diagnostic('documents', _(u'Documents'), document_missing_list)
|
||||
|
||||
register_tool(document_find_all_duplicates, namespace='documents', title=_(u'documents'))
|
||||
|
||||
@@ -27,13 +27,13 @@ register_settings(
|
||||
module=u'documents.conf.settings',
|
||||
settings=[
|
||||
# Upload
|
||||
{'name': u'USE_STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_USE_STAGING_DIRECTORY', 'default': False},
|
||||
{'name': u'STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_STAGING_DIRECTORY', 'default': u'/tmp/mayan/staging', 'exists': True},
|
||||
{'name': u'PER_USER_STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_PER_USER_STAGING_DIRECTORY', 'default': False},
|
||||
{'name': u'USER_STAGING_DIRECTORY_ROOT', 'global_name': u'DOCUMENTS_USER_STAGING_DIRECTORY_ROOT', 'default': u'/tmp/mayan/staging/users', 'exists': True},
|
||||
{'name': u'USER_STAGING_DIRECTORY_EXPRESSION', 'global_name': u'DOCUMENTS_USER_STAGING_DIRECTORY_EXPRESSION', 'default': u'user.username'},
|
||||
{'name': u'DELETE_STAGING_FILE_AFTER_UPLOAD', 'global_name': u'DOCUMENTS_DELETE_STAGING_FILE_AFTER_UPLOAD', 'default': False},
|
||||
{'name': u'STAGING_FILES_PREVIEW_SIZE', 'global_name': u'DOCUMENTS_STAGING_FILES_PREVIEW_SIZE', 'default': u'640x480'},
|
||||
#{'name': u'USE_STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_USE_STAGING_DIRECTORY', 'default': False},
|
||||
#{'name': u'STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_STAGING_DIRECTORY', 'default': u'/tmp/mayan/staging', 'exists': True},
|
||||
#{'name': u'PER_USER_STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_PER_USER_STAGING_DIRECTORY', 'default': False},
|
||||
#{'name': u'USER_STAGING_DIRECTORY_ROOT', 'global_name': u'DOCUMENTS_USER_STAGING_DIRECTORY_ROOT', 'default': u'/tmp/mayan/staging/users', 'exists': True},
|
||||
#{'name': u'USER_STAGING_DIRECTORY_EXPRESSION', 'global_name': u'DOCUMENTS_USER_STAGING_DIRECTORY_EXPRESSION', 'default': u'user.username'},
|
||||
#{'name': u'DELETE_STAGING_FILE_AFTER_UPLOAD', 'global_name': u'DOCUMENTS_DELETE_STAGING_FILE_AFTER_UPLOAD', 'default': False},
|
||||
#{'name': u'STAGING_FILES_PREVIEW_SIZE', 'global_name': u'DOCUMENTS_STAGING_FILES_PREVIEW_SIZE', 'default': u'640x480'},
|
||||
# Saving
|
||||
{'name': u'CHECKSUM_FUNCTION', 'global_name': u'DOCUMENTS_CHECKSUM_FUNCTION', 'default': default_checksum},
|
||||
{'name': u'UUID_FUNCTION', 'global_name': u'DOCUMENTS_UUID_FUNCTION', 'default': default_uuid},
|
||||
|
||||
@@ -265,7 +265,7 @@ class PrintForm(forms.Form):
|
||||
page_orientation = forms.ChoiceField(choices=PAGE_ORIENTATION_CHOICES, initial=DEFAULT_PAGE_ORIENTATION, label=_(u'Page orientation'), required=True)
|
||||
page_range = forms.CharField(label=_(u'Page range'), required=False)
|
||||
|
||||
|
||||
'''
|
||||
class StagingDocumentForm(DocumentForm):
|
||||
"""
|
||||
Form that show all the files in the staging folder specified by the
|
||||
@@ -290,7 +290,7 @@ class StagingDocumentForm(DocumentForm):
|
||||
|
||||
class Meta(DocumentForm.Meta):
|
||||
exclude = ('description', 'file', 'document_type', 'tags')
|
||||
|
||||
'''
|
||||
|
||||
class DocumentTypeForm(forms.ModelForm):
|
||||
"""
|
||||
|
||||
@@ -18,10 +18,6 @@ PERMISSION_DOCUMENT_TYPE_EDIT = {'namespace': 'documents', 'name': 'document_typ
|
||||
PERMISSION_DOCUMENT_TYPE_DELETE = {'namespace': 'documents', 'name': 'document_type_delete', 'label': _(u'Delete document types')}
|
||||
PERMISSION_DOCUMENT_TYPE_CREATE = {'namespace': 'documents', 'name': 'document_type_create', 'label': _(u'Create document types')}
|
||||
|
||||
UPLOAD_SOURCE_LOCAL = u'local'
|
||||
UPLOAD_SOURCE_STAGING = u'staging'
|
||||
UPLOAD_SOURCE_USER_STAGING = u'user_staging'
|
||||
|
||||
HISTORY_DOCUMENT_CREATED = {
|
||||
'namespace': 'documents', 'name': 'document_created',
|
||||
'label': _(u'Document creation'),
|
||||
|
||||
@@ -7,18 +7,14 @@ from documents.conf.settings import PRINT_SIZE
|
||||
from documents.conf.settings import THUMBNAIL_SIZE
|
||||
from documents.conf.settings import DISPLAY_SIZE
|
||||
from documents.conf.settings import MULTIPAGE_PREVIEW_SIZE
|
||||
from documents.literals import UPLOAD_SOURCE_LOCAL, \
|
||||
UPLOAD_SOURCE_STAGING, UPLOAD_SOURCE_USER_STAGING
|
||||
#from documents.literals import UPLOAD_SOURCE_LOCAL, \
|
||||
# UPLOAD_SOURCE_STAGING, UPLOAD_SOURCE_USER_STAGING
|
||||
|
||||
urlpatterns = patterns('documents.views',
|
||||
url(r'^list/$', 'document_list', (), 'document_list'),
|
||||
url(r'^list/recent/$', 'document_list_recent', (), 'document_list_recent'),
|
||||
url(r'^create/from/local/multiple/$', 'document_create', (), 'document_create_multiple'),
|
||||
|
||||
url(r'^upload/local/$', 'upload_document_with_type', {'source': UPLOAD_SOURCE_LOCAL}, 'upload_document_from_local'),
|
||||
url(r'^upload/staging/$', 'upload_document_with_type', {'source': UPLOAD_SOURCE_STAGING}, 'upload_document_from_staging'),
|
||||
url(r'^upload/staging/user/$', 'upload_document_with_type', {'source': UPLOAD_SOURCE_USER_STAGING}, 'upload_document_from_user_staging'),
|
||||
|
||||
url(r'^(?P<document_id>\d+)/view/$', 'document_view', (), 'document_view_simple'),
|
||||
url(r'^(?P<document_id>\d+)/view/advanced/$', 'document_view', {'advanced': True}, 'document_view_advanced'),
|
||||
url(r'^(?P<document_id>\d+)/delete/$', 'document_delete', (), 'document_delete'),
|
||||
@@ -41,9 +37,6 @@ urlpatterns = patterns('documents.views',
|
||||
url(r'^multiple/clear_transformations/$', 'document_multiple_clear_transformations', (), 'document_multiple_clear_transformations'),
|
||||
url(r'^duplicates/list/$', 'document_find_all_duplicates', (), 'document_find_all_duplicates'),
|
||||
|
||||
url(r'^staging_file/type/(?P<source>\w+)/(?P<staging_file_id>\w+)/preview/$', 'staging_file_preview', (), 'staging_file_preview'),
|
||||
url(r'^staging_file/type/(?P<source>\w+)/(?P<staging_file_id>\w+)/delete/$', 'staging_file_delete', (), 'staging_file_delete'),
|
||||
|
||||
url(r'^page/(?P<document_page_id>\d+)/$', 'document_page_view', (), 'document_page_view'),
|
||||
url(r'^page/(?P<document_page_id>\d+)/text/$', 'document_page_text', (), 'document_page_text'),
|
||||
url(r'^page/(?P<document_page_id>\d+)/edit/$', 'document_page_edit', (), 'document_page_edit'),
|
||||
|
||||
@@ -36,10 +36,6 @@ from permissions.api import check_permissions
|
||||
from document_indexing.api import update_indexes, delete_indexes
|
||||
from history.api import create_history
|
||||
|
||||
from documents.conf.settings import DELETE_STAGING_FILE_AFTER_UPLOAD
|
||||
from documents.conf.settings import USE_STAGING_DIRECTORY
|
||||
from documents.conf.settings import PER_USER_STAGING_DIRECTORY
|
||||
|
||||
from documents.conf.settings import PREVIEW_SIZE
|
||||
from documents.conf.settings import THUMBNAIL_SIZE
|
||||
from documents.conf.settings import STORAGE_BACKEND
|
||||
@@ -61,7 +57,7 @@ from documents.literals import HISTORY_DOCUMENT_CREATED, \
|
||||
|
||||
from documents.forms import DocumentTypeSelectForm, \
|
||||
DocumentForm, DocumentForm_edit, DocumentPropertiesForm, \
|
||||
StagingDocumentForm, DocumentPreviewForm, \
|
||||
DocumentPreviewForm, \
|
||||
DocumentPageForm, DocumentPageTransformationForm, \
|
||||
DocumentContentForm, DocumentPageForm_edit, \
|
||||
DocumentPageForm_text, PrintForm, DocumentTypeForm, \
|
||||
@@ -69,11 +65,8 @@ from documents.forms import DocumentTypeSelectForm, \
|
||||
from documents.wizards import DocumentCreateWizard
|
||||
from documents.models import Document, DocumentType, DocumentPage, \
|
||||
DocumentPageTransformation, RecentDocument, DocumentTypeFilename
|
||||
from documents.staging import create_staging_file_class
|
||||
from documents.literals import PICTURE_ERROR_SMALL, PICTURE_ERROR_MEDIUM, \
|
||||
PICTURE_UNKNOWN_SMALL, PICTURE_UNKNOWN_MEDIUM
|
||||
from documents.literals import UPLOAD_SOURCE_LOCAL, \
|
||||
UPLOAD_SOURCE_STAGING, UPLOAD_SOURCE_USER_STAGING
|
||||
|
||||
# Document type permissions
|
||||
from documents.literals import PERMISSION_DOCUMENT_TYPE_EDIT, \
|
||||
@@ -116,10 +109,10 @@ def document_create_siblings(request, document_id):
|
||||
if document.document_type_id:
|
||||
query_dict['document_type_id'] = document.document_type_id
|
||||
|
||||
url = reverse('upload_document_from_local')
|
||||
url = reverse('upload_interactive')
|
||||
return HttpResponseRedirect('%s?%s' % (url, urlencode(query_dict)))
|
||||
|
||||
|
||||
'''
|
||||
def _handle_save_document(request, document, form=None):
|
||||
RecentDocument.objects.add_document_for_user(request.user, document)
|
||||
|
||||
@@ -279,7 +272,7 @@ def upload_document_with_type(request, source):
|
||||
}
|
||||
return render_to_response('generic_form.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
'''
|
||||
|
||||
def document_view(request, document_id, advanced=False):
|
||||
check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW])
|
||||
@@ -545,7 +538,7 @@ def document_download(request, document_id):
|
||||
messages.error(request, e)
|
||||
return HttpResponseRedirect(request.META['HTTP_REFERER'])
|
||||
|
||||
|
||||
'''
|
||||
def staging_file_preview(request, source, staging_file_id):
|
||||
check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE])
|
||||
StagingFile = create_staging_file_class(request, source)
|
||||
@@ -596,7 +589,7 @@ def staging_file_delete(request, source, staging_file_id):
|
||||
'previous': previous,
|
||||
'form_icon': u'drive_delete.png',
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
'''
|
||||
|
||||
def document_page_transformation_list(request, document_page_id):
|
||||
check_permissions(request.user, [PERMISSION_DOCUMENT_TRANSFORM])
|
||||
|
||||
@@ -30,7 +30,6 @@ class DocumentCreateWizard(BoundFormWizard):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.query_dict = {}
|
||||
self.multiple = kwargs.pop('multiple', True)
|
||||
self.step_titles = kwargs.pop('step_titles', [
|
||||
_(u'step 1 of 3: Document type'),
|
||||
_(u'step 2 of 3: Metadata selection'),
|
||||
@@ -75,13 +74,8 @@ class DocumentCreateWizard(BoundFormWizard):
|
||||
return 'generic_wizard.html'
|
||||
|
||||
def done(self, request, form_list):
|
||||
if self.multiple:
|
||||
view = 'upload_document_from_local'
|
||||
else:
|
||||
view = 'upload_document'
|
||||
|
||||
if self.document_type:
|
||||
self.query_dict['document_type_id'] = self.document_type.pk
|
||||
|
||||
url = urlquote(reverse(view), self.query_dict)
|
||||
url = urlquote(reverse('upload_interactive'), self.query_dict)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
@@ -131,6 +131,9 @@ def _get_object_navigation_links(context, menu_name=None, links_dict=object_navi
|
||||
current_view = resolve_to_name(current_path)
|
||||
context_links = []
|
||||
|
||||
# Don't fudge with the original global dictionary
|
||||
links_dict = links_dict.copy()
|
||||
|
||||
query_string = urlparse.urlparse(request.get_full_path()).query or urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).query
|
||||
parsed_query_string = urlparse.parse_qs(query_string)
|
||||
|
||||
@@ -155,6 +158,16 @@ def _get_object_navigation_links(context, menu_name=None, links_dict=object_navi
|
||||
except VariableDoesNotExist:
|
||||
obj = None
|
||||
|
||||
try:
|
||||
"""
|
||||
Check for and inject a temporary navigation dictionary
|
||||
"""
|
||||
temp_navigation_links = Variable('temporary_navigation_links').resolve(context)
|
||||
if temp_navigation_links:
|
||||
links_dict.update(temp_navigation_links)
|
||||
except VariableDoesNotExist:
|
||||
pass
|
||||
|
||||
try:
|
||||
links = links_dict[menu_name][current_view]['links']
|
||||
for link in resolve_links(context, links, current_view, current_path, parsed_query_string):
|
||||
@@ -169,6 +182,7 @@ def _get_object_navigation_links(context, menu_name=None, links_dict=object_navi
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
return context_links
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
|
||||
from navigation.api import register_links, register_top_menu, \
|
||||
register_model_list_columns, register_multi_item_links, \
|
||||
register_sidebar_template
|
||||
|
||||
from sources.staging import StagingFile
|
||||
|
||||
upload_document_from_local = {'text': _(u'local'), 'view': 'upload_document_from_local', 'famfam': 'drive_disk', 'keep_query': True}
|
||||
upload_document_from_staging = {'text': _(u'staging'), 'view': 'upload_document_from_staging', 'famfam': 'drive_network', 'keep_query': True}#, 'condition': lambda x: USE_STAGING_DIRECTORY}
|
||||
upload_document_from_user_staging = {'text': _(u'user staging'), 'view': 'upload_document_from_user_staging', 'famfam': 'drive_user', 'keep_query': True}#, 'condition': lambda x: PER_USER_STAGING_DIRECTORY}
|
||||
|
||||
staging_file_preview = {'text': _(u'preview'), 'class': 'fancybox-noscaling', 'view': 'staging_file_preview', 'args': ['source.source_type', 'source.pk', 'object.id'], 'famfam': 'zoom'}
|
||||
staging_file_delete = {'text': _(u'delete'), 'view': 'staging_file_delete', 'args': ['source.source_type', 'source.pk', 'object.id'], 'famfam': 'delete'}
|
||||
|
||||
register_links(StagingFile, [staging_file_preview, staging_file_delete])
|
||||
|
||||
|
||||
@@ -2,7 +2,5 @@ from django.contrib import admin
|
||||
|
||||
from sources.models import StagingFolder, WebForm
|
||||
|
||||
|
||||
|
||||
admin.site.register(StagingFolder)
|
||||
admin.site.register(WebForm)
|
||||
|
||||
39
apps/sources/forms.py
Normal file
39
apps/sources/forms.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.conf import settings
|
||||
|
||||
from common.forms import DetailForm
|
||||
from common.literals import PAGE_SIZE_CHOICES, PAGE_ORIENTATION_CHOICES
|
||||
from common.conf.settings import DEFAULT_PAPER_SIZE
|
||||
from common.conf.settings import DEFAULT_PAGE_ORIENTATION
|
||||
|
||||
from documents.forms import DocumentForm
|
||||
|
||||
|
||||
class StagingDocumentForm(DocumentForm):
|
||||
"""
|
||||
Form that show all the files in the staging folder specified by the
|
||||
StagingFile class passed as 'cls' argument
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
cls = kwargs.pop('cls')
|
||||
super(StagingDocumentForm, self).__init__(*args, **kwargs)
|
||||
try:
|
||||
self.fields['staging_file_id'].choices = [
|
||||
(staging_file.id, staging_file) for staging_file in cls.get_all()
|
||||
]
|
||||
except:
|
||||
pass
|
||||
|
||||
# Put staging_list field first in the field order list
|
||||
staging_list_index = self.fields.keyOrder.index('staging_file_id')
|
||||
staging_list = self.fields.keyOrder.pop(staging_list_index)
|
||||
self.fields.keyOrder.insert(0, staging_list)
|
||||
|
||||
staging_file_id = forms.ChoiceField(label=_(u'Staging file'))
|
||||
|
||||
class Meta(DocumentForm.Meta):
|
||||
exclude = ('description', 'file', 'document_type', 'tags')
|
||||
@@ -40,8 +40,8 @@ SOURCE_ICON_CHOICES = (
|
||||
(SOURCE_ICON_WORLD, _(u'world'))
|
||||
)
|
||||
|
||||
SOURCE_CHOICE_WEB_FORM = 'wform'
|
||||
SOURCE_CHOICE_STAGING = 'stagn'
|
||||
SOURCE_CHOICE_WEB_FORM = 'webform'
|
||||
SOURCE_CHOICE_STAGING = 'staging'
|
||||
|
||||
SOURCE_CHOICES = (
|
||||
(SOURCE_CHOICE_WEB_FORM, _(u'Web form')),
|
||||
@@ -56,15 +56,11 @@ class BaseModel(models.Model):
|
||||
blacklist = models.TextField(blank=True, verbose_name=_(u'blacklist'))
|
||||
document_type = models.ForeignKey(DocumentType, blank=True, null=True, verbose_name=_(u'document type'))
|
||||
|
||||
# M2M
|
||||
# Default Metadata sets
|
||||
# Default Metadata types & default values
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s (%s)' % (self.title, dict(SOURCE_CHOICES).get(self.source_type))
|
||||
|
||||
class Meta:
|
||||
ordering = ['title']
|
||||
ordering = ('title',)
|
||||
abstract = True
|
||||
|
||||
|
||||
@@ -104,6 +100,14 @@ class StagingFolder(InteractiveBaseModel):
|
||||
uncompress = models.CharField(max_length=1, choices=SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, verbose_name=_(u'uncompress'))
|
||||
delete_after_upload = models.BooleanField(default=True, verbose_name=_(u'delete after upload'))
|
||||
|
||||
def get_preview_size(self):
|
||||
dimensions = []
|
||||
dimensions.append(unicode(self.preview_width))
|
||||
if self.preview_height:
|
||||
dimensions.append(unicode(self.preview_height))
|
||||
|
||||
return u'x'.join(dimensions)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _(u'staging folder')
|
||||
verbose_name_plural = _(u'staging folder')
|
||||
|
||||
@@ -11,24 +11,30 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from converter import TRANFORMATION_CHOICES
|
||||
from converter.api import convert, cache_cleanup
|
||||
|
||||
from documents.conf.settings import STAGING_DIRECTORY
|
||||
from documents.conf.settings import DEFAULT_TRANSFORMATIONS
|
||||
from documents.conf.settings import STAGING_FILES_PREVIEW_SIZE
|
||||
from documents.conf.settings import USER_STAGING_DIRECTORY_ROOT
|
||||
from documents.conf.settings import USER_STAGING_DIRECTORY_EXPRESSION
|
||||
#from documents.conf.settings import STAGING_DIRECTORY
|
||||
STAGING_DIRECTORY = u'/tmp'
|
||||
#from documents.conf.settings import DEFAULT_TRANSFORMATIONS
|
||||
#from documents.conf.settings import STAGING_FILES_PREVIEW_SIZE
|
||||
STAGING_FILES_PREVIEW_SIZE = u'640'
|
||||
#from documents.conf.settings import USER_STAGING_DIRECTORY_ROOT
|
||||
#from documents.conf.settings import USER_STAGING_DIRECTORY_EXPRESSION
|
||||
|
||||
from documents.literals import UPLOAD_SOURCE_STAGING, \
|
||||
UPLOAD_SOURCE_USER_STAGING
|
||||
#from documents.literals import UPLOAD_SOURCE_STAGING, \
|
||||
# UPLOAD_SOURCE_USER_STAGING
|
||||
|
||||
UPLOAD_SOURCE_LOCAL = u'local'
|
||||
UPLOAD_SOURCE_STAGING = u'staging'
|
||||
UPLOAD_SOURCE_USER_STAGING = u'user_staging'
|
||||
|
||||
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)
|
||||
|
||||
STAGING_FILE_FUNCTIONS = {
|
||||
UPLOAD_SOURCE_STAGING: lambda x: STAGING_DIRECTORY,
|
||||
UPLOAD_SOURCE_USER_STAGING: lambda x: os.path.join(USER_STAGING_DIRECTORY_ROOT, eval(USER_STAGING_DIRECTORY_EXPRESSION, {'user': x.user}))
|
||||
}
|
||||
#STAGING_FILE_FUNCTIONS = {
|
||||
# UPLOAD_SOURCE_STAGING: lambda x: STAGING_DIRECTORY,
|
||||
# UPLOAD_SOURCE_USER_STAGING: lambda x: os.path.join(USER_STAGING_DIRECTORY_ROOT, eval(USER_STAGING_DIRECTORY_EXPRESSION, {'user': x.user}))
|
||||
#}
|
||||
|
||||
|
||||
def evaluate_user_staging_path(request, source):
|
||||
@@ -52,7 +58,8 @@ def _return_new_class():
|
||||
|
||||
def create_staging_file_class(request, source):
|
||||
cls = _return_new_class()
|
||||
cls.set_path(evaluate_user_staging_path(request, source))
|
||||
#cls.set_path(evaluate_user_staging_path(request, source))
|
||||
cls.set_path(source)
|
||||
return cls
|
||||
|
||||
|
||||
@@ -120,8 +127,8 @@ class StagingFile(object):
|
||||
raise Exception(ugettext(u'Unable to upload staging file: %s') % exc)
|
||||
|
||||
def delete(self):
|
||||
tranformation_string, errors = get_transformation_string(DEFAULT_TRANSFORMATIONS)
|
||||
cache_cleanup(self.filepath, size=STAGING_FILES_PREVIEW_SIZE, extra_options=tranformation_string)
|
||||
#tranformation_string, errors = get_transformation_string(DEFAULT_TRANSFORMATIONS)
|
||||
cache_cleanup(self.filepath, size=STAGING_FILES_PREVIEW_SIZE)#, extra_options=tranformation_string)
|
||||
try:
|
||||
os.unlink(self.filepath)
|
||||
except OSError, exc:
|
||||
@@ -130,9 +137,11 @@ class StagingFile(object):
|
||||
else:
|
||||
raise OSError(ugettext(u'Unable to delete staging file: %s') % exc)
|
||||
|
||||
def preview(self):
|
||||
tranformation_string, errors = get_transformation_string(DEFAULT_TRANSFORMATIONS)
|
||||
output_file = convert(self.filepath, size=STAGING_FILES_PREVIEW_SIZE, extra_options=tranformation_string, cleanup_files=False)
|
||||
def preview(self, preview_size):
|
||||
errors = []
|
||||
#tranformation_string, errors = get_transformation_string(DEFAULT_TRANSFORMATIONS)
|
||||
#output_file = convert(self.filepath, size=STAGING_FILES_PREVIEW_SIZE, extra_options=tranformation_string, cleanup_files=False)
|
||||
output_file = convert(self.filepath, size=preview_size, cleanup_files=False)
|
||||
return output_file, errors
|
||||
|
||||
|
||||
23
apps/sources/urls.py
Normal file
23
apps/sources/urls.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
#from converter.api import QUALITY_HIGH, QUALITY_PRINT
|
||||
|
||||
#from documents.conf.settings import PREVIEW_SIZE
|
||||
#from documents.conf.settings import PRINT_SIZE
|
||||
#from documents.conf.settings import THUMBNAIL_SIZE
|
||||
#from documents.conf.settings import DISPLAY_SIZE
|
||||
#from documents.conf.settings import MULTIPAGE_PREVIEW_SIZE
|
||||
#from documents.literals import UPLOAD_SOURCE_LOCAL, \
|
||||
# UPLOAD_SOURCE_STAGING, UPLOAD_SOURCE_USER_STAGING
|
||||
|
||||
urlpatterns = patterns('sources.views',
|
||||
#url(r'^upload/local/$', 'upload_document_with_type', {'source': UPLOAD_SOURCE_LOCAL}, 'upload_document_from_local'),
|
||||
#url(r'^upload/staging/$', 'upload_document_with_type', {'source': UPLOAD_SOURCE_STAGING}, 'upload_document_from_staging'),
|
||||
#url(r'^upload/staging/user/$', 'upload_document_with_type', {'source': UPLOAD_SOURCE_USER_STAGING}, 'upload_document_from_user_staging'),
|
||||
|
||||
url(r'^staging_file/type/(?P<source_type>\w+)/(?P<source_id>\d+)/(?P<staging_file_id>\w+)/preview/$', 'staging_file_preview', (), 'staging_file_preview'),
|
||||
url(r'^staging_file/type/(?P<source_type>\w+)/(?P<source_id>\d+)/(?P<staging_file_id>\w+)/delete/$', 'staging_file_delete', (), 'staging_file_delete'),
|
||||
|
||||
url(r'^upload/interactive/(?P<source_type>\w+)/(?P<source_id>\d+)/$', 'upload_interactive', (), 'upload_interactive'),
|
||||
url(r'^upload/interactive/$', 'upload_interactive', (), 'upload_interactive'),
|
||||
)
|
||||
@@ -1 +1,368 @@
|
||||
# Create your views here.
|
||||
import os
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from permissions.api import check_permissions
|
||||
from documents.literals import PERMISSION_DOCUMENT_CREATE
|
||||
from documents.models import DocumentType
|
||||
from metadata.api import save_metadata_list, \
|
||||
decode_metadata_from_url, metadata_repr_as_list
|
||||
from metadata.forms import MetadataFormSet, MetadataSelectionForm
|
||||
import sendfile
|
||||
from converter.exceptions import UnkownConvertError, UnknownFormat
|
||||
from documents.literals import PICTURE_ERROR_SMALL, PICTURE_ERROR_MEDIUM, \
|
||||
PICTURE_UNKNOWN_SMALL, PICTURE_UNKNOWN_MEDIUM
|
||||
|
||||
#TEMP
|
||||
from documents.forms import DocumentForm
|
||||
|
||||
from sources.models import WebForm, StagingFolder
|
||||
from sources.models import SOURCE_CHOICE_WEB_FORM, SOURCE_CHOICE_STAGING
|
||||
from sources.staging import create_staging_file_class
|
||||
from sources.forms import StagingDocumentForm
|
||||
|
||||
|
||||
def upload_interactive(request, source_type=None, source_id=None):
|
||||
check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE])
|
||||
|
||||
subtemplates_list = []
|
||||
|
||||
tab_links = []
|
||||
|
||||
context = {}
|
||||
|
||||
web_forms = WebForm.objects.filter(enabled=True)
|
||||
for web_form in web_forms:
|
||||
tab_links.append({
|
||||
'text': web_form.title,
|
||||
'view': 'upload_interactive',
|
||||
'args': [u'"%s"' % web_form.source_type, web_form.pk],
|
||||
'famfam': web_form.icon,
|
||||
'keep_query': True
|
||||
})
|
||||
|
||||
staging_folders = StagingFolder.objects.filter(enabled=True)
|
||||
for staging_folder in staging_folders:
|
||||
tab_links.append({
|
||||
'text': staging_folder.title,
|
||||
'view': 'upload_interactive',
|
||||
'args': [u'"%s"' % staging_folder.source_type, staging_folder.pk],
|
||||
'famfam': staging_folder.icon,
|
||||
'keep_query': True
|
||||
})
|
||||
|
||||
if web_forms.count() == 0 and staging_folders.count() == 0:
|
||||
subtemplates_list.append(
|
||||
{
|
||||
'name': 'generic_subtemplate.html',
|
||||
'context': {
|
||||
'title': _(u'Upload sources'),
|
||||
'paragraphs': [
|
||||
_(u'Not document sources have been defined or there are no sources enabled.')
|
||||
# TODO: Add link to setup
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
document_type_id = request.GET.get('document_type_id', None)
|
||||
if document_type_id:
|
||||
document_type = get_object_or_404(DocumentType, pk=document_type_id[0])
|
||||
else:
|
||||
document_type = None
|
||||
|
||||
subtemplates_list = []
|
||||
|
||||
if source_type is None and source_id is None:
|
||||
if web_forms.count():
|
||||
source_type = web_forms[0].source_type
|
||||
source_id = web_forms[0].pk
|
||||
elif staging_folders.count():
|
||||
source_type = staging_folders[0].source_type
|
||||
source_id = staging_folders[0].pk
|
||||
|
||||
|
||||
if source_type and source_id:
|
||||
if source_type == SOURCE_CHOICE_WEB_FORM:
|
||||
web_form = get_object_or_404(WebForm, pk=source_id)
|
||||
context['source'] = web_form
|
||||
form = DocumentForm(document_type=document_type)
|
||||
|
||||
subtemplates_list.append({
|
||||
'name': 'generic_form_subtemplate.html',
|
||||
'context': {
|
||||
'form': form,
|
||||
'title': _(u'upload a local document from source: %s') % web_form.title,
|
||||
},
|
||||
})
|
||||
elif source_type == SOURCE_CHOICE_STAGING:
|
||||
staging_folder = get_object_or_404(StagingFolder, pk=source_id)
|
||||
context['source'] = staging_folder
|
||||
StagingFile = create_staging_file_class(request, staging_folder.folder_path)
|
||||
form = StagingDocumentForm(cls=StagingFile,
|
||||
document_type=document_type)
|
||||
try:
|
||||
staging_filelist = StagingFile.get_all()
|
||||
except Exception, e:
|
||||
messages.error(request, e)
|
||||
staging_filelist = []
|
||||
finally:
|
||||
subtemplates_list = [
|
||||
{
|
||||
'name': 'generic_form_subtemplate.html',
|
||||
'context': {
|
||||
'form': form,
|
||||
'title': _(u'upload a document from staging source: %s') % staging_folder.title,
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'generic_list_subtemplate.html',
|
||||
'context': {
|
||||
'title': _(u'files in staging path'),
|
||||
'object_list': staging_filelist,
|
||||
'hide_link': True,
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
context.update({
|
||||
'document_type_id': document_type_id,
|
||||
'subtemplates_list': subtemplates_list,
|
||||
'sidebar_subtemplates_list': [
|
||||
{
|
||||
'name': 'generic_subtemplate.html',
|
||||
'context': {
|
||||
'title': _(u'Current metadata'),
|
||||
'paragraphs': metadata_repr_as_list(decode_metadata_from_url(request.GET)),
|
||||
'side_bar': True,
|
||||
}
|
||||
}],
|
||||
'temporary_navigation_links': {'form_header': {'upload_interactive': {'links': tab_links}}}
|
||||
})
|
||||
return render_to_response('generic_form.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def _handle_save_document(request, document, form=None):
|
||||
RecentDocument.objects.add_document_for_user(request.user, document)
|
||||
|
||||
if form:
|
||||
if form.cleaned_data['new_filename']:
|
||||
document.file_filename = form.cleaned_data['new_filename']
|
||||
document.save()
|
||||
|
||||
if form and 'document_type_available_filenames' in form.cleaned_data:
|
||||
if form.cleaned_data['document_type_available_filenames']:
|
||||
document.file_filename = form.cleaned_data['document_type_available_filenames'].filename
|
||||
document.save()
|
||||
|
||||
save_metadata_list(decode_metadata_from_url(request.GET), document, create=True)
|
||||
|
||||
warnings = update_indexes(document)
|
||||
if request.user.is_staff or request.user.is_superuser:
|
||||
for warning in warnings:
|
||||
messages.warning(request, warning)
|
||||
|
||||
create_history(HISTORY_DOCUMENT_CREATED, document, {'user': request.user})
|
||||
|
||||
|
||||
def _handle_zip_file(request, uploaded_file, document_type=None):
|
||||
filename = getattr(uploaded_file, 'filename', getattr(uploaded_file, 'name', ''))
|
||||
if filename.lower().endswith('zip'):
|
||||
zfobj = zipfile.ZipFile(uploaded_file)
|
||||
for filename in zfobj.namelist():
|
||||
if not filename.endswith('/'):
|
||||
zip_document = Document(file=SimpleUploadedFile(
|
||||
name=filename, content=zfobj.read(filename)))
|
||||
if document_type:
|
||||
zip_document.document_type = document_type
|
||||
zip_document.save()
|
||||
_handle_save_document(request, zip_document)
|
||||
messages.success(request, _(u'Extracted file: %s, uploaded successfully.') % filename)
|
||||
#Signal that uploaded file was a zip file
|
||||
return True
|
||||
else:
|
||||
#Otherwise tell parent to handle file
|
||||
return False
|
||||
|
||||
|
||||
def upload_document_with_type(request, source):
|
||||
check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE])
|
||||
|
||||
document_type_id = request.GET.get('document_type_id', None)
|
||||
if document_type_id:
|
||||
document_type = get_object_or_404(DocumentType, pk=document_type_id[0])
|
||||
else:
|
||||
document_type = None
|
||||
|
||||
if request.method == 'POST':
|
||||
if source == UPLOAD_SOURCE_LOCAL:
|
||||
form = DocumentForm(request.POST, request.FILES, document_type=document_type)
|
||||
if form.is_valid():
|
||||
try:
|
||||
expand = form.cleaned_data['expand']
|
||||
if (not expand) or (expand and not _handle_zip_file(request, request.FILES['file'], document_type)):
|
||||
instance = form.save()
|
||||
instance.save()
|
||||
if document_type:
|
||||
instance.document_type = document_type
|
||||
_handle_save_document(request, instance, form)
|
||||
messages.success(request, _(u'Document uploaded successfully.'))
|
||||
except Exception, e:
|
||||
messages.error(request, e)
|
||||
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
elif (USE_STAGING_DIRECTORY and source == UPLOAD_SOURCE_STAGING) or (PER_USER_STAGING_DIRECTORY and source == UPLOAD_SOURCE_USER_STAGING):
|
||||
StagingFile = create_staging_file_class(request, source)
|
||||
form = StagingDocumentForm(request.POST,
|
||||
request.FILES, cls=StagingFile,
|
||||
document_type=document_type)
|
||||
if form.is_valid():
|
||||
try:
|
||||
staging_file = StagingFile.get(form.cleaned_data['staging_file_id'])
|
||||
expand = form.cleaned_data['expand']
|
||||
if (not expand) or (expand and not _handle_zip_file(request, staging_file.upload(), document_type)):
|
||||
document = Document(file=staging_file.upload())
|
||||
if document_type:
|
||||
document.document_type = document_type
|
||||
document.save()
|
||||
_handle_save_document(request, document, form)
|
||||
messages.success(request, _(u'Staging file: %s, uploaded successfully.') % staging_file.filename)
|
||||
|
||||
if DELETE_STAGING_FILE_AFTER_UPLOAD:
|
||||
staging_file.delete()
|
||||
messages.success(request, _(u'Staging file: %s, deleted successfully.') % staging_file.filename)
|
||||
except Exception, e:
|
||||
messages.error(request, e)
|
||||
|
||||
return HttpResponseRedirect(request.META['HTTP_REFERER'])
|
||||
else:
|
||||
if source == UPLOAD_SOURCE_LOCAL:
|
||||
form = DocumentForm(document_type=document_type)
|
||||
elif (USE_STAGING_DIRECTORY and source == UPLOAD_SOURCE_STAGING) or (PER_USER_STAGING_DIRECTORY and source == UPLOAD_SOURCE_USER_STAGING):
|
||||
StagingFile = create_staging_file_class(request, source)
|
||||
form = StagingDocumentForm(cls=StagingFile,
|
||||
document_type=document_type)
|
||||
|
||||
subtemplates_list = []
|
||||
|
||||
if source == UPLOAD_SOURCE_LOCAL:
|
||||
subtemplates_list.append({
|
||||
'name': 'generic_form_subtemplate.html',
|
||||
'context': {
|
||||
'form': form,
|
||||
'title': _(u'upload a local document'),
|
||||
},
|
||||
})
|
||||
|
||||
elif (USE_STAGING_DIRECTORY and source == UPLOAD_SOURCE_STAGING) or (PER_USER_STAGING_DIRECTORY and source == UPLOAD_SOURCE_USER_STAGING):
|
||||
if source == UPLOAD_SOURCE_STAGING:
|
||||
form_title = _(u'upload a document from staging')
|
||||
list_title = _(u'files in staging')
|
||||
else:
|
||||
form_title = _(u'upload a document from user staging')
|
||||
list_title = _(u'files in user staging')
|
||||
try:
|
||||
staging_filelist = StagingFile.get_all()
|
||||
except Exception, e:
|
||||
messages.error(request, e)
|
||||
staging_filelist = []
|
||||
finally:
|
||||
subtemplates_list = [
|
||||
{
|
||||
'name': 'generic_form_subtemplate.html',
|
||||
'context': {
|
||||
'form': form,
|
||||
'title': form_title,
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'generic_list_subtemplate.html',
|
||||
'context': {
|
||||
'title': list_title,
|
||||
'object_list': staging_filelist,
|
||||
'hide_link': True,
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
context = {
|
||||
'source': source,
|
||||
'document_type_id': document_type_id,
|
||||
'subtemplates_list': subtemplates_list,
|
||||
'sidebar_subtemplates_list': [
|
||||
{
|
||||
'name': 'generic_subtemplate.html',
|
||||
'context': {
|
||||
'title': _(u'Current metadata'),
|
||||
'paragraphs': metadata_repr_as_list(decode_metadata_from_url(request.GET)),
|
||||
'side_bar': True,
|
||||
}
|
||||
}]
|
||||
}
|
||||
return render_to_response('generic_form.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def staging_file_preview(request, source_type, source_id, staging_file_id):
|
||||
check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE])
|
||||
staging_folder = get_object_or_404(StagingFolder, pk=source_id)
|
||||
StagingFile = create_staging_file_class(request, staging_folder.folder_path)
|
||||
try:
|
||||
output_file, errors = StagingFile.get(staging_file_id).preview(staging_folder.get_preview_size())
|
||||
if errors and (request.user.is_staff or request.user.is_superuser):
|
||||
for error in errors:
|
||||
messages.warning(request, _(u'Staging file transformation error: %(error)s') % {
|
||||
'error': error
|
||||
})
|
||||
|
||||
except UnkownConvertError, e:
|
||||
if request.user.is_staff or request.user.is_superuser:
|
||||
messages.error(request, e)
|
||||
|
||||
output_file = os.path.join(settings.MEDIA_ROOT, u'images', PICTURE_ERROR_MEDIUM)
|
||||
except UnknownFormat:
|
||||
output_file = os.path.join(settings.MEDIA_ROOT, u'images', PICTURE_UNKNOWN_MEDIUM)
|
||||
except Exception, e:
|
||||
if request.user.is_staff or request.user.is_superuser:
|
||||
messages.error(request, e)
|
||||
output_file = os.path.join(settings.MEDIA_ROOT, u'images', PICTURE_ERROR_MEDIUM)
|
||||
finally:
|
||||
return sendfile.sendfile(request, output_file)
|
||||
|
||||
|
||||
def staging_file_delete(request, source_type, source_id, staging_file_id):
|
||||
check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE])
|
||||
staging_folder = get_object_or_404(StagingFolder, pk=source_id)
|
||||
StagingFile = create_staging_file_class(request, staging_folder.folder_path)
|
||||
|
||||
staging_file = StagingFile.get(staging_file_id)
|
||||
next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', None)))
|
||||
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', None)))
|
||||
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
staging_file.delete()
|
||||
messages.success(request, _(u'Staging file delete successfully.'))
|
||||
except Exception, e:
|
||||
messages.error(request, e)
|
||||
return HttpResponseRedirect(next)
|
||||
|
||||
return render_to_response('generic_confirm.html', {
|
||||
'source': staging_folder,
|
||||
'delete_view': True,
|
||||
'object': staging_file,
|
||||
'next': next,
|
||||
'previous': previous,
|
||||
'form_icon': u'delete.png',
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
Reference in New Issue
Block a user