Add document list item view.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
Roberto Rosario
2017-07-13 17:20:02 -04:00
parent c3d4884d34
commit ff59f34b7f
18 changed files with 420 additions and 126 deletions

View File

@@ -8,6 +8,7 @@
Add COMMON_PRODUCTION_ERROR_LOG_PATH to control path of log file. Add COMMON_PRODUCTION_ERROR_LOG_PATH to control path of log file.
Defaults to mayan/error.log. Defaults to mayan/error.log.
- Add support logging request exceptions. - Add support logging request exceptions.
- Add document list item view.
2.5.2 (2017-07-08) 2.5.2 (2017-07-08)
================== ==================

View File

@@ -235,12 +235,22 @@ a i {
} }
} }
/* Metadata */ /* List item view */
.metadata-display {
display: inline-block; .panel-item {
min-width: 200px; box-shadow: 2px 2px 18px rgba(0, 0, 0, .2);
padding-right: 10px; }
width: 49%;
.panel-item > .panel-heading {
padding: 1px 10px;
}
.panel-item > .panel-body {
padding: 10px;
}
.list-extra-column-label {
font-weight: bold;
} }
/* Content */ /* Content */
@@ -249,3 +259,15 @@ a i {
width: 95%; width: 95%;
} }
} }
/* Document widget */
.spinner-container {
margin: auto;
width: 100%;
border: 1px solid lightgray;
}
.spinner-container > .spinner-icon {
margin-left: 4px;
margin-top: 3px;
}

View File

@@ -197,6 +197,8 @@ MayanImage.prototype.load = function () {
}); });
this.element.attr('src', this.element.attr('data-url')); this.element.attr('src', this.element.attr('data-url'));
$.fn.matchHeight._update();
$.fn.matchHeight._maintainScroll = true;
}; };
jQuery(document).ready(function() { jQuery(document).ready(function() {

View File

@@ -168,6 +168,7 @@
<script type="text/javascript" src="{% static 'appearance/packages/fancyBox-master/lib/jquery.mousewheel.pack.js' %}"></script> <script type="text/javascript" src="{% static 'appearance/packages/fancyBox-master/lib/jquery.mousewheel.pack.js' %}"></script>
<script type="text/javascript" src="{% static 'appearance/packages/select2-4.0.3/dist/js/select2.min.js' %}"></script> <script type="text/javascript" src="{% static 'appearance/packages/select2-4.0.3/dist/js/select2.min.js' %}"></script>
<script type="text/javascript" src="{% static 'appearance/packages/toastr-master-998959b0/build/toastr.min.js' %}"></script> <script type="text/javascript" src="{% static 'appearance/packages/toastr-master-998959b0/build/toastr.min.js' %}"></script>
<script type="text/javascript" src="{% static 'appearance/packages/jquery.matchHeight-min.js' %}"></script>
<script type="text/javascript" src="{% static 'appearance/js/base.js' %}"></script> <script type="text/javascript" src="{% static 'appearance/js/base.js' %}"></script>
{% endcompress %} {% endcompress %}

View File

@@ -12,6 +12,10 @@
<hr> <hr>
{% endif %} {% endif %}
{% include 'appearance/generic_list_subtemplate.html' %} {% if list_as_items %}
{% endblock %} {% include 'appearance/generic_list_items_subtemplate.html' %}
{% else %}
{% include 'appearance/generic_list_subtemplate.html' %}
{% endif %}
{% endblock content %}

View File

@@ -0,0 +1,212 @@
{% load i18n %}
{% load static %}
{% load common_tags %}
{% load navigation_tags %}
<script>
'use strict';
waitForJQuery(function() {
$(function() {
$('.panel-item').matchHeight({
byRow: false,
});
});
});
</script>
<div class="row">
<div class="col-xs-12">
<h4>
{% if page_obj %}
{% if page_obj.paginator.num_pages != 1 %}
{% blocktrans with page_obj.start_index as start and page_obj.end_index as end and page_obj.paginator.object_list|length as total and page_obj.number as page_number and page_obj.paginator.num_pages as total_pages %}Total ({{ start }} - {{ end }} out of {{ total }}) (Page {{ page_number }} of {{ total_pages }}){% endblocktrans %}
{% else %}
{% blocktrans with page_obj.paginator.object_list|length as total %}Total: {{ total }}{% endblocktrans %}
{% endif %}
{% else %}
{% blocktrans with object_list|length as total %}Total: {{ total }}{% endblocktrans %}
{% endif %}
</h4>
<hr>
<div class="well center-block">
<form action="{% url 'common:multi_object_action_view' %}" class="pure-form" method="get">
{% if object_list %}
{% if not hide_multi_item_actions %}
{% get_multi_item_links_form object_list %}
{% endif %}
{% if multi_item_actions %}
<fieldset>
{{ multi_item_form }}
&nbsp;<button class="btn btn-primary btn-xs" type="submit" name="{{ form.prefix }}-submit">
{% trans 'Submit' %}
</button>
</fieldset>
{% endif %}
{% endif %}
<hr/>
{% if scrollable_content %}
<div style="border: 1px solid; height: {{ scrollable_content_height }}; overflow: auto;">
{% endif %}
<div class="row">
{% for object in object_list %}
<div class="col-xs-12 col-sm-4 col-md-3 col-lg-2">
<div class="panel panel-primary panel-item">
<div class="panel-heading">
<div class="form-group">
<div class="checkbox">
<label for="id_indexes_0">
{% if multi_item_actions %}
{% if multi_select_item_properties %}
<input class="checkbox" type="checkbox" name="properties_{{ object|get_encoded_parameter:multi_select_item_properties }}" value="" />
{% else %}
<input class="checkbox" type="checkbox" name="pk_{{ object.pk }}" value="" />
{% endif %}
{% endif %}
<span style="color: white; word-break: break-all;">
{% if not hide_object %}
{% if main_object %}
{% with object|object_property:main_object as object %}
{% if not hide_link %}<a href="{{ object.get_absolute_url }}">{{ object }}</a>{% else %}{{ object }}{% endif %}
{% endwith %}
{% else %}
{% if not hide_link %}<a href="{{ object.get_absolute_url }}">{{ object }}</a>{% else %}{{ object }}{% endif %}
{% endif %}
{% endif %}
</span>
</label>
</div>
</div>
</div>
<div class="panel-body">
{% if not hide_columns %}
{% for column in object|get_source_columns %}
<div class="text-center" style="">{% source_column_resolve column=column %}{{ column_result }}</div>
{% endfor %}
{% endif %}
{% for column in extra_columns %}
<div class="text-center"><span class="list-extra-column-label">{{ column.name }}</span>: {{ object|object_property:column.attribute }}</div>
{% endfor %}
{% if not hide_links %}
<p class="text-center">
{% get_menu_links 'object menu' source=object as resolved_links %}
{% for object_navigation_links in resolved_links %}
{% with 'true' as horizontal %}
{% include 'navigation/generic_navigation.html' %}
{% endwith %}
{% endfor %}
</p>
{% endif %}
</div>
</div>
</div>
{% empty %}
<p class="text-center">{% trans 'No results' %}</p>
{% endfor %}
</div>
{% comment %}
<div class="table-responsive">
<table class="table table-condensed table-striped">
<tbody>
{% if not hide_header %}
<tr>
{% if multi_item_actions %}
<th class="first"><input type="checkbox" class="checkbox toggle" /></th>
{% endif %}
{% if not hide_object %}
<th>{% trans 'Identifier' %}</th>
{% endif %}
{% if not hide_columns %}
{% for column in object_list|get_source_columns %}
<th>{{ column.label }}</th>
{% endfor %}
{% endif %}
{% for column in extra_columns %}
<th>{{ column.name }}</th>
{% endfor %}
{% if not hide_links %}
<th class="">&nbsp;</th>
{% endif %}
</tr>
{% endif %}
{% for object in object_list %}
<tr>
{% if multi_item_actions %}
<td>
{% if multi_select_item_properties %}
<input type="checkbox" class="checkbox" name="properties_{{ object|get_encoded_parameter:multi_select_item_properties }}" value="" />
{% else %}
<input type="checkbox" class="checkbox" name="pk_{{ object.pk }}" value="" />
{% endif %}
</td>
{% endif %}
{% if not hide_object %}
{% if main_object %}
{% with object|object_property:main_object as object %}
<td>{% if not hide_link %}<a href="{{ object.get_absolute_url }}">{{ object }}</a>{% else %}{{ object }}{% endif %}</td>
{% endwith %}
{% else %}
<td>{% if not hide_link %}<a href="{{ object.get_absolute_url }}">{{ object }}</a>{% else %}{{ object }}{% endif %}</td>
{% endif %}
{% endif %}
{% if not hide_columns %}
{% for column in object|get_source_columns %}
<td>{% source_column_resolve column=column %}{{ column_result }}</td>
{% endfor %}
{% endif %}
{% for column in extra_columns %}
<td>{{ object|object_property:column.attribute }}</td>
{% endfor %}
{% if not hide_links %}
<td class="last">
{% get_menu_links 'object menu' source=object as resolved_links %}
{% for object_navigation_links in resolved_links %}
{% with 'true' as horizontal %}
{% include 'navigation/generic_navigation.html' %}
{% endwith %}
{% endfor %}
</td>
{% endif %}
</tr>
{% empty %}
<tr><td class="text-center" colspan=99>{% trans 'No results' %}</td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% endcomment %}
{% if scrollable_content %}
</div>
{% endif %}
</form>
{% include 'pagination/pagination.html' %}
</div>
</div>
</div>

View File

@@ -17,14 +17,18 @@
<hr> <hr>
{% endif %} {% endif %}
<div class="row"> <div class="row">
<div class="col-xs-12 col-sm-12 col-md-4"> <div class="col-xs-12 col-sm-12 col-md-12 col-lg-2">
<h4>{% trans 'Navigation:' %}</h4> <h4>{% trans 'Navigation:' %}</h4>
<div class="jstree"></div> <div class="jstree"></div>
</div> </div>
<div class="col-xs-12 col-sm-12 col-md-8"> <div class="col-xs-12 col-sm-12 col-md-12 col-lg-10">
{% with document_list as object_list %} {% with document_list as object_list %}
{% include 'appearance/generic_list_subtemplate.html' %} {% if list_as_items %}
{% include 'appearance/generic_list_items_subtemplate.html' %}
{% else %}
{% include 'appearance/generic_list_subtemplate.html' %}
{% endif %}
{% endwith %} {% endwith %}
</div> </div>
</div> </div>

View File

@@ -107,6 +107,7 @@ class CabinetDetailView(TemplateView):
), ),
'document_list': self.get_document_queryset(), 'document_list': self.get_document_queryset(),
'hide_links': True, 'hide_links': True,
'list_as_items': True,
'object': cabinet, 'object': cabinet,
'title': _('Details of cabinet: %s') % cabinet.get_full_path(), 'title': _('Details of cabinet: %s') % cabinet.get_full_path(),
} }

View File

@@ -71,34 +71,38 @@ class CheckoutDocumentView(SingleObjectCreateView):
class CheckoutListView(DocumentListView): class CheckoutListView(DocumentListView):
extra_context = {
'title': _('Documents checked out'),
'hide_links': True,
'extra_columns': (
{
'name': _('User'),
'attribute': encapsulate(
lambda document: document.checkout_info().user.get_full_name() or document.checkout_info().user
)
},
{
'name': _('Checkout time and date'),
'attribute': encapsulate(
lambda document: document.checkout_info().checkout_datetime
)
},
{
'name': _('Checkout expiration'),
'attribute': encapsulate(
lambda document: document.checkout_info().expiration_datetime
)
},
),
}
def get_document_queryset(self): def get_document_queryset(self):
return DocumentCheckout.objects.checked_out_documents() return DocumentCheckout.objects.checked_out_documents()
def get_extra_context(self):
context = super(CheckoutListView, self).get_extra_context()
context.update(
{
'title': _('Documents checked out'),
'extra_columns': (
{
'name': _('User'),
'attribute': encapsulate(
lambda document: document.checkout_info().user.get_full_name() or document.checkout_info().user
)
},
{
'name': _('Checkout time and date'),
'attribute': encapsulate(
lambda document: document.checkout_info().checkout_datetime
)
},
{
'name': _('Checkout expiration'),
'attribute': encapsulate(
lambda document: document.checkout_info().expiration_datetime
)
},
),
}
)
return context
class CheckoutDetailView(SingleObjectDetailView): class CheckoutDetailView(SingleObjectDetailView):
form_class = DocumentCheckoutDefailForm form_class = DocumentCheckoutDefailForm

View File

@@ -13,12 +13,16 @@
{% endif %} {% endif %}
<div class="row"> <div class="row">
<div class="col-xs-12 col-sm-12 col-md-4"> <div class="col-xs-12 col-sm-12 col-md-12 col-lg-3">
{{ navigation }} {{ navigation }}
</div> </div>
<div class="col-xs-12 col-sm-12 col-md-8"> <div class="col-xs-12 col-sm-12 col-md-12 col-lg-9">
{% include 'appearance/generic_list_subtemplate.html' %} {% if list_as_items %}
{% include 'appearance/generic_list_items_subtemplate.html' %}
{% else %}
{% include 'appearance/generic_list_subtemplate.html' %}
{% endif %}
</div> </div>
</div> </div>

View File

@@ -261,21 +261,28 @@ class IndexInstanceNodeView(DocumentListView):
return self.index_instance_node.documents.all() return self.index_instance_node.documents.all()
def get_extra_context(self): def get_extra_context(self):
context = { context = super(IndexInstanceNodeView, self).get_extra_context()
'hide_links': True, context.update(
'object': self.index_instance_node, {
'navigation': mark_safe( 'object': self.index_instance_node,
_('Navigation: %s') % node_tree( 'navigation': mark_safe(
node=self.index_instance_node, user=self.request.user _('Navigation: %s') % node_tree(
) node=self.index_instance_node, user=self.request.user
), )
'title': _( ),
'Contents for index: %s' 'title': _(
) % self.index_instance_node.get_full_path(), 'Contents for index: %s'
} ) % self.index_instance_node.get_full_path(),
}
)
if self.index_instance_node and not self.index_instance_node.index_template_node.link_documents: if self.index_instance_node and not self.index_instance_node.index_template_node.link_documents:
context.update({'hide_object': True}) context.update(
{
'hide_object': True,
'list_as_items': False,
}
)
return context return context

View File

@@ -433,11 +433,14 @@ class WorkflowDocumentListView(DocumentListView):
return Document.objects.filter(workflows__workflow=self.workflow) return Document.objects.filter(workflows__workflow=self.workflow)
def get_extra_context(self): def get_extra_context(self):
return { context = super(WorkflowDocumentListView, self).get_extra_context()
'hide_links': True, context.update(
'object': self.workflow, {
'title': _('Documents with the workflow: %s') % self.workflow 'object': self.workflow,
} 'title': _('Documents with the workflow: %s') % self.workflow
}
)
return context
class WorkflowStateDocumentListView(DocumentListView): class WorkflowStateDocumentListView(DocumentListView):
@@ -446,19 +449,22 @@ class WorkflowStateDocumentListView(DocumentListView):
def get_extra_context(self): def get_extra_context(self):
workflow_state = self.get_workflow_state() workflow_state = self.get_workflow_state()
return { context = super(WorkflowStateDocumentListView, self).get_extra_context()
'hide_links': True, context.update(
'object': workflow_state, {
'navigation_object_list': ('object', 'workflow'), 'object': workflow_state,
'workflow': WorkflowRuntimeProxy.objects.get( 'navigation_object_list': ('object', 'workflow'),
pk=workflow_state.workflow.pk 'workflow': WorkflowRuntimeProxy.objects.get(
), pk=workflow_state.workflow.pk
'title': _( ),
'Documents in the workflow "%s", state "%s"' 'title': _(
) % ( 'Documents in the workflow "%s", state "%s"'
workflow_state.workflow, workflow_state ) % (
) workflow_state.workflow, workflow_state
} )
}
)
return context
def get_workflow_state(self): def get_workflow_state(self):
workflow_state = get_object_or_404( workflow_state = get_object_or_404(

View File

@@ -21,7 +21,7 @@ setting_print_size = namespace.add_setting(
global_name='DOCUMENTS_PRINT_SIZE', default='3600' global_name='DOCUMENTS_PRINT_SIZE', default='3600'
) )
setting_thumbnail_size = namespace.add_setting( setting_thumbnail_size = namespace.add_setting(
global_name='DOCUMENTS_THUMBNAIL_SIZE', default='50x50' global_name='DOCUMENTS_THUMBNAIL_SIZE', default='300'
) )
setting_recent_count = namespace.add_setting( setting_recent_count = namespace.add_setting(
global_name='DOCUMENTS_RECENT_COUNT', default=40, global_name='DOCUMENTS_RECENT_COUNT', default=40,

View File

@@ -32,11 +32,14 @@ class DocumentTypeDocumentListView(DocumentListView):
return self.get_document_type().documents.all() return self.get_document_type().documents.all()
def get_extra_context(self): def get_extra_context(self):
return { context = super(DocumentTypeDocumentListView, self).get_extra_context()
'hide_links': True, context.update(
'object': self.get_document_type(), {
'title': _('Documents of type: %s') % self.get_document_type() 'object': self.get_document_type(),
} 'title': _('Documents of type: %s') % self.get_document_type()
}
)
return context
class DocumentTypeListView(SingleObjectListView): class DocumentTypeListView(SingleObjectListView):

View File

@@ -50,16 +50,18 @@ logger = logging.getLogger(__name__)
class DocumentListView(SingleObjectListView): class DocumentListView(SingleObjectListView):
extra_context = {
'hide_links': True,
'title': _('All documents'),
}
object_permission = permission_document_view object_permission = permission_document_view
def get_document_queryset(self): def get_document_queryset(self):
return Document.objects.defer('description', 'uuid', 'date_added', 'language', 'in_trash', 'deleted_date_time').all() return Document.objects.defer('description', 'uuid', 'date_added', 'language', 'in_trash', 'deleted_date_time').all()
def get_extra_context(self):
return {
'hide_links': True,
'list_as_items': True,
'title': _('All documents'),
}
def get_queryset(self): def get_queryset(self):
self.queryset = self.get_document_queryset().filter(is_stub=False) self.queryset = self.get_document_queryset().filter(is_stub=False)
return super(DocumentListView, self).get_queryset() return super(DocumentListView, self).get_queryset()
@@ -68,17 +70,21 @@ class DocumentListView(SingleObjectListView):
class DeletedDocumentListView(DocumentListView): class DeletedDocumentListView(DocumentListView):
object_permission = None object_permission = None
extra_context = {
'hide_link': True,
'title': _('Documents in trash'),
}
def get_document_queryset(self): def get_document_queryset(self):
return AccessControlList.objects.filter_by_access( return AccessControlList.objects.filter_by_access(
permission_document_view, self.request.user, permission_document_view, self.request.user,
queryset=DeletedDocument.trash.all() queryset=DeletedDocument.trash.all()
) )
def get_extra_context(self):
context = super(DeletedDocumentListView, self).get_extra_context()
context.update(
{
'title': _('Documents in trash'),
}
)
return context
class DeletedDocumentDeleteView(ConfirmView): class DeletedDocumentDeleteView(ConfirmView):
extra_context = { extra_context = {
@@ -186,6 +192,16 @@ class DocumentDuplicatesListView(DocumentListView):
def get_document(self): def get_document(self):
return get_object_or_404(Document, pk=self.kwargs['pk']) return get_object_or_404(Document, pk=self.kwargs['pk'])
def get_extra_context(self):
context = super(DocumentDuplicatesListView, self).get_extra_context()
context.update(
{
'object': self.get_document(),
'title': _('Duplicates for document: %s') % self.get_document(),
}
)
return context
def get_queryset(self): def get_queryset(self):
try: try:
return DuplicatedDocument.objects.get( return DuplicatedDocument.objects.get(
@@ -194,13 +210,6 @@ class DocumentDuplicatesListView(DocumentListView):
except DuplicatedDocument.DoesNotExist: except DuplicatedDocument.DoesNotExist:
return Document.objects.none() return Document.objects.none()
def get_extra_context(self):
return {
'hide_links': True,
'object': self.get_document(),
'title': _('Duplicates for document: %s') % self.get_document(),
}
class DocumentEditView(SingleObjectEditView): class DocumentEditView(SingleObjectEditView):
form_class = DocumentForm form_class = DocumentForm
@@ -374,14 +383,18 @@ class EmptyTrashCanView(ConfirmView):
class RecentDocumentListView(DocumentListView): class RecentDocumentListView(DocumentListView):
extra_context = {
'hide_links': True,
'title': _('Recent documents'),
}
def get_document_queryset(self): def get_document_queryset(self):
return RecentDocument.objects.get_for_user(self.request.user) return RecentDocument.objects.get_for_user(self.request.user)
def get_extra_context(self):
context = super(RecentDocumentListView, self).get_extra_context()
context.update(
{
'title': _('Recent documents'),
}
)
return context
class DocumentDownloadFormView(FormView): class DocumentDownloadFormView(FormView):
form_class = DocumentDownloadForm form_class = DocumentDownloadForm
@@ -762,24 +775,28 @@ class DocumentPrint(FormView):
class DuplicatedDocumentListView(DocumentListView): class DuplicatedDocumentListView(DocumentListView):
extra_context = {
'extra_columns': (
{
'name': _('Duplicates'),
'attribute': encapsulate(
lambda document: DuplicatedDocument.objects.get(
document=document
).documents.count()
)
},
),
'hide_links': True,
'title': _('Duplicated documents')
}
def get_document_queryset(self): def get_document_queryset(self):
return Document.objects.filter( return Document.objects.filter(
pk__in=DuplicatedDocument.objects.values_list( pk__in=DuplicatedDocument.objects.values_list(
'document_id', flat=True 'document_id', flat=True
) )
) )
def get_extra_context(self):
context = super(DuplicatedDocumentListView, self).get_extra_context()
context.update(
{
'extra_columns': (
{
'name': _('Duplicates'),
'attribute': encapsulate(
lambda document: DuplicatedDocument.objects.get(
document=document
).documents.count()
)
},
),
'title': _('Duplicated documents')
}
)
return context

View File

@@ -225,13 +225,13 @@ class InstanceImageWidget(object):
) )
result.append( result.append(
'<div class="spinner-container text-primary" style="margin: auto; width: {width}px; height: {height}px; border:1px solid lightgray;">' '<div class="spinner-container text-primary" style="height: {height}px;">'
'<span class="fa-stack fa-lg" style="margin-left: 4px; margin-top: 3px;">' '<span class="spinner-icon fa-stack fa-lg">'
'<i class="fa fa-file-o fa-stack-2x"></i>' '<i class="fa fa-file-o fa-stack-2x"></i>'
'<i class="fa fa-clock-o fa-stack-1x"></i>' '<i class="fa fa-clock-o fa-stack-1x"></i>'
'</span>' '</span>'
'</div>' '</div>'
'<img class="thin_border {image_class} pull-left"' '<img class="thin_border {image_class} pull-left" style="width: 100%;"'
'data-url="{preview_full_url}" src="#" ' 'data-url="{preview_full_url}" src="#" '
'/> '.format( '/> '.format(
width=self.width or '32', height=self.height or '32', width=self.width or '32', height=self.height or '32',

View File

@@ -75,11 +75,14 @@ class ResolvedSmartLinkView(DocumentListView):
'smart_link': self.smart_link.label, 'smart_link': self.smart_link.label,
} }
return { context = super(ResolvedSmartLinkView, self).get_extra_context()
'hide_links': True, context.update(
'object': self.document, {
'title': title, 'object': self.document,
} 'title': title,
}
)
return context
class SetupSmartLinkDocumentTypesView(AssignRemoveView): class SetupSmartLinkDocumentTypesView(AssignRemoveView):

View File

@@ -197,11 +197,14 @@ class TagTaggedItemListView(DocumentListView):
return self.get_tag().documents.all() return self.get_tag().documents.all()
def get_extra_context(self): def get_extra_context(self):
return { context = super(TagTaggedItemListView, self).get_extra_context()
'title': _('Documents with the tag: %s') % self.get_tag(), context.update(
'hide_links': True, {
'object': self.get_tag(), 'object': self.get_tag(),
} 'title': _('Documents with the tag: %s') % self.get_tag(),
}
)
return context
class DocumentTagListView(TagListView): class DocumentTagListView(TagListView):