Add support for comparing document versions

This commit is contained in:
Roberto Rosario
2012-08-04 04:11:03 -04:00
parent 34c3fd61fb
commit c5515f5054
9 changed files with 120 additions and 5 deletions

View File

@@ -43,7 +43,7 @@ from .links import (document_page_transformation_list, document_page_transformat
document_page_navigation_last, document_page_zoom_in, document_page_zoom_out, document_page_navigation_last, document_page_zoom_in, document_page_zoom_out,
document_page_rotate_right, document_page_rotate_left, document_page_view_reset, document_page_rotate_right, document_page_rotate_left, document_page_view_reset,
document_multiple_clear_transformations, document_multiple_delete, document_multiple_clear_transformations, document_multiple_delete,
document_multiple_download) document_multiple_download, document_version_text_compare)
from .links import document_clear_image_cache from .links import document_clear_image_cache
from .statistics import get_statistics from .statistics import get_statistics
@@ -59,6 +59,7 @@ bind_links([Document], [document_view_simple, document_edit, document_print, doc
# Document Version links # Document Version links
bind_links([DocumentVersion], [document_version_revert, document_version_download]) bind_links([DocumentVersion], [document_version_revert, document_version_download])
bind_links(['document_version_list', 'upload_version', 'document_version_revert', 'document_version_text_compare', 'document_version_show_diff_text'], [document_version_text_compare], menu_name='sidebar')
secondary_menu_links = [document_list_recent, document_list] secondary_menu_links = [document_list_recent, document_list]

View File

@@ -321,3 +321,33 @@ class DocumentDownloadForm(forms.Form):
if len(self.document_versions) > 1: if len(self.document_versions) > 1:
self.fields['compressed'].initial = True self.fields['compressed'].initial = True
self.fields['compressed'].widget.attrs.update({'disabled': True}) self.fields['compressed'].widget.attrs.update({'disabled': True})
class DocumentVersionCompareForm(forms.Form):
left_version = forms.ChoiceField(label=_(u'Source version'), required=True)
right_version = forms.ChoiceField(label=_(u'Destination version'), required=True)
def make_choices(self):
return [(version.pk, unicode(version)) for version in self.document.versions.all()]
def __init__(self, *args, **kwargs):
self.document = kwargs.pop('document')
super(DocumentVersionCompareForm, self).__init__(*args, **kwargs)
choices = self.make_choices()
self.fields['left_version'].choices = choices
self.fields['left_version'].initial = choices[-2][0] if len(choices) > 1 else choices[-1][0]
self.fields['right_version'].choices = choices
self.fields['right_version'].initial = choices[-1][0]
class DocumentVersionDiffForm(forms.Form):
def __init__(self, *args, **kwargs):
contents = kwargs.pop('contents', None)
super(DocumentVersionDiffForm, self).__init__(*args, **kwargs)
self.fields['contents'].initial = contents
contents = forms.CharField(
label=_(u'Contents'),
widget=TextAreaDiv()
)

View File

@@ -11,7 +11,8 @@ from .permissions import (PERMISSION_DOCUMENT_CREATE,
PERMISSION_DOCUMENT_TRANSFORM, PERMISSION_DOCUMENT_TOOLS, PERMISSION_DOCUMENT_TRANSFORM, PERMISSION_DOCUMENT_TOOLS,
PERMISSION_DOCUMENT_EDIT, PERMISSION_DOCUMENT_VERSION_REVERT, PERMISSION_DOCUMENT_EDIT, PERMISSION_DOCUMENT_VERSION_REVERT,
PERMISSION_DOCUMENT_TYPE_EDIT, PERMISSION_DOCUMENT_TYPE_DELETE, PERMISSION_DOCUMENT_TYPE_EDIT, PERMISSION_DOCUMENT_TYPE_DELETE,
PERMISSION_DOCUMENT_TYPE_CREATE, PERMISSION_DOCUMENT_TYPE_VIEW) PERMISSION_DOCUMENT_TYPE_CREATE, PERMISSION_DOCUMENT_TYPE_VIEW,
PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE)
from .conf.settings import ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL from .conf.settings import ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL
@@ -83,6 +84,7 @@ document_page_view_reset = Link(text=_(u'reset view'), klass='no-parent-history'
# Document versions # Document versions
document_version_list = Link(text=_(u'versions'), view='document_version_list', args='object.pk', sprite='page_world', permissions=[PERMISSION_DOCUMENT_VIEW]) document_version_list = Link(text=_(u'versions'), view='document_version_list', args='object.pk', sprite='page_world', permissions=[PERMISSION_DOCUMENT_VIEW])
document_version_revert = Link(text=_(u'revert'), view='document_version_revert', args='object.pk', sprite='page_refresh', permissions=[PERMISSION_DOCUMENT_VERSION_REVERT], conditional_disable=is_current_version) document_version_revert = Link(text=_(u'revert'), view='document_version_revert', args='object.pk', sprite='page_refresh', permissions=[PERMISSION_DOCUMENT_VERSION_REVERT], conditional_disable=is_current_version)
document_version_text_compare = Link(text=_(u'compare (text)'), view='document_version_text_compare', args='object.pk', sprite='table_relationship', permissions=[PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE])
# Document type related links # Document type related links
document_type_list = Link(text=_(u'document type list'), view='document_type_list', sprite='layout', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW]) document_type_list = Link(text=_(u'document type list'), view='document_type_list', sprite='layout', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW])

View File

@@ -23,3 +23,5 @@ PERMISSION_DOCUMENT_TYPE_VIEW = Permission.objects.register(documents_setup_name
PERMISSION_DOCUMENT_TYPE_EDIT = Permission.objects.register(documents_setup_namespace, 'document_type_edit', _(u'Edit document types')) PERMISSION_DOCUMENT_TYPE_EDIT = Permission.objects.register(documents_setup_namespace, 'document_type_edit', _(u'Edit document types'))
PERMISSION_DOCUMENT_TYPE_DELETE = Permission.objects.register(documents_setup_namespace, 'document_type_delete', _(u'Delete document types')) PERMISSION_DOCUMENT_TYPE_DELETE = Permission.objects.register(documents_setup_namespace, 'document_type_delete', _(u'Delete document types'))
PERMISSION_DOCUMENT_TYPE_CREATE = Permission.objects.register(documents_setup_namespace, 'document_type_create', _(u'Create document types')) PERMISSION_DOCUMENT_TYPE_CREATE = Permission.objects.register(documents_setup_namespace, 'document_type_create', _(u'Create document types'))
PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE = Permission.objects.register(documents_setup_namespace, 'document_versions_text_compare', _(u'Compare document version textually'))

View File

@@ -36,6 +36,8 @@ urlpatterns = patterns('documents.views',
url(r'^(?P<document_pk>\d+)/version/all/$', 'document_version_list', (), 'document_version_list'), url(r'^(?P<document_pk>\d+)/version/all/$', 'document_version_list', (), 'document_version_list'),
url(r'^document/version/(?P<document_version_pk>\d+)/download/$', 'document_download', (), 'document_version_download'), url(r'^document/version/(?P<document_version_pk>\d+)/download/$', 'document_download', (), 'document_version_download'),
url(r'^document/version/(?P<document_version_pk>\d+)/revert/$', 'document_version_revert', (), 'document_version_revert'), url(r'^document/version/(?P<document_version_pk>\d+)/revert/$', 'document_version_revert', (), 'document_version_revert'),
url(r'^document/(?P<document_pk>\d+)/version_compare/text/$', 'document_version_text_compare', (), 'document_version_text_compare'),
url(r'^document/version/show/text/diff/(?P<left_document_version_pk>\d+)/vs/(?P<right_document_version_pk>\d+)/$', 'document_version_show_diff_text', (), 'document_version_show_diff_text'),
url(r'^multiple/clear_transformations/$', 'document_multiple_clear_transformations', (), 'document_multiple_clear_transformations'), 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'^duplicates/list/$', 'document_find_all_duplicates', (), 'document_find_all_duplicates'),

View File

@@ -40,17 +40,20 @@ from .permissions import (PERMISSION_DOCUMENT_CREATE,
PERMISSION_DOCUMENT_TRANSFORM, PERMISSION_DOCUMENT_TOOLS, PERMISSION_DOCUMENT_TRANSFORM, PERMISSION_DOCUMENT_TOOLS,
PERMISSION_DOCUMENT_EDIT, PERMISSION_DOCUMENT_VERSION_REVERT, PERMISSION_DOCUMENT_EDIT, PERMISSION_DOCUMENT_VERSION_REVERT,
PERMISSION_DOCUMENT_TYPE_EDIT, PERMISSION_DOCUMENT_TYPE_DELETE, PERMISSION_DOCUMENT_TYPE_EDIT, PERMISSION_DOCUMENT_TYPE_DELETE,
PERMISSION_DOCUMENT_TYPE_CREATE, PERMISSION_DOCUMENT_TYPE_VIEW) PERMISSION_DOCUMENT_TYPE_CREATE, PERMISSION_DOCUMENT_TYPE_VIEW,
PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE)
from .events import history_document_edited from .events import history_document_edited
from .forms import (DocumentForm_edit, DocumentPropertiesForm, from .forms import (DocumentForm_edit, DocumentPropertiesForm,
DocumentPreviewForm, DocumentPageForm, DocumentPreviewForm, DocumentPageForm,
DocumentPageTransformationForm, DocumentContentForm, DocumentPageTransformationForm, DocumentContentForm,
DocumentPageForm_edit, DocumentPageForm_text, PrintForm, DocumentPageForm_edit, DocumentPageForm_text, PrintForm,
DocumentTypeForm, DocumentTypeFilenameForm, DocumentTypeForm, DocumentTypeFilenameForm,
DocumentTypeFilenameForm_create, DocumentDownloadForm) DocumentTypeFilenameForm_create, DocumentDownloadForm,
DocumentVersionCompareForm, DocumentVersionDiffForm)
from .models import (Document, DocumentType, DocumentPage, from .models import (Document, DocumentType, DocumentPage,
DocumentPageTransformation, RecentDocument, DocumentTypeFilename, DocumentPageTransformation, RecentDocument, DocumentTypeFilename,
DocumentVersion) DocumentVersion)
from .widgets import diff_widget
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -1364,3 +1367,60 @@ def document_version_revert(request, document_version_pk):
'message': _(u'All later version after this one will be deleted too.'), 'message': _(u'All later version after this one will be deleted too.'),
'form_icon': u'page_refresh.png', 'form_icon': u'page_refresh.png',
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
def document_version_text_compare(request, document_pk):
document = get_object_or_404(Document, pk=document_pk)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE, request.user, document)
RecentDocument.objects.add_document_for_user(request.user, document)
if request.method == 'POST':
form = DocumentVersionCompareForm(document=document, data=request.POST)
if form.is_valid():
return HttpResponseRedirect(reverse('document_version_show_diff_text', args=[form.cleaned_data['left_version'], form.cleaned_data['right_version']]))
else:
form = DocumentVersionCompareForm(document=document)
return render_to_response('generic_form.html', {
'object': document,
'title': _(u'Textually compare versions of document: %s') % document,
'form': form,
'submit_label': _(u'Compare'),
'submit_icon_famfam': 'table_relationship',
},
context_instance=RequestContext(request))
def document_version_show_diff_text(request, left_document_version_pk, right_document_version_pk):
left_document_version = get_object_or_404(DocumentVersion, pk=left_document_version_pk)
right_document_version = get_object_or_404(DocumentVersion, pk=right_document_version_pk)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE])
except PermissionDenied:
# Make sure user has the compare permission for both document
# versions' root documents
AccessEntry.objects.check_access(PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE, request.user, left_document_version.document)
AccessEntry.objects.check_access(PERMISSION_DOCUMENT_VERSIONS_TEXT_COMPARE, request.user, right_document_version.document)
context = {
'form': DocumentVersionDiffForm(contents=diff_widget(left_document_version.content, right_document_version.content)),
'read_only': True
}
if left_document_version.document == right_document_version.document:
context['object'] = left_document_version.document
context['title'] = _(u'Textual difference between version %(version1)s and %(version2)s of document: %(document)s') % {
'version1': left_document_version, 'version2': right_document_version,
'document': left_document_version.document}
else:
context['title'] = _(u'Textual difference between document: %(document1)s and %(document2)s.') % {
'document1': left_document_version.document, 'document2': right_document_version.document}
return render_to_response('generic_form.html', context,
context_instance=RequestContext(request))

View File

@@ -1,5 +1,7 @@
from __future__ import absolute_import from __future__ import absolute_import
import diff_match_patch
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@@ -85,3 +87,18 @@ def document_html_widget(document, view='document_thumbnail', click_view=None, p
) )
return mark_safe(u''.join(result)) return mark_safe(u''.join(result))
def diff_widget(version1, version2):
dmp = diff_match_patch.diff_match_patch()
dmp.Diff_Timeout = 1 # Don't spend more than 1 seconds on a diff.
differences = dmp.diff_main(version1, version2)
dmp.diff_cleanupSemantic(differences)
pretty = dmp.diff_prettyHtml(differences)
#patch=dmp.patch_make(differences)
#text = dmp.patch_toText(patch)
return mark_safe(dmp.diff_prettyHtml(differences))

View File

@@ -48,7 +48,7 @@ bind_links([WatchFolder], [setup_web_form_list, setup_staging_folder_list, setup
bind_links([WatchFolder], [setup_source_transformation_list, setup_source_edit, setup_source_delete]) bind_links([WatchFolder], [setup_source_transformation_list, setup_source_edit, setup_source_delete])
# Document version # Document version
bind_links(['document_version_list', 'upload_version', 'document_version_revert'], [upload_version], menu_name='sidebar') bind_links(['document_version_list', 'upload_version', 'document_version_revert', 'document_version_text_compare', 'document_version_show_diff_text'], [upload_version], menu_name='sidebar')
bind_links(['setup_source_transformation_create', 'setup_source_transformation_edit', 'setup_source_transformation_delete', 'setup_source_transformation_list'], [setup_source_transformation_create], menu_name='sidebar') bind_links(['setup_source_transformation_create', 'setup_source_transformation_edit', 'setup_source_transformation_delete', 'setup_source_transformation_list'], [setup_source_transformation_create], menu_name='sidebar')

View File

@@ -72,3 +72,4 @@ GitPython==0.3.2.RC1
elementtree==1.2.7-20070827-preview elementtree==1.2.7-20070827-preview
Pygments==1.5 Pygments==1.5
diff-match-patch==20120106