Skip to content
This repository has been archived by the owner on Aug 26, 2022. It is now read-only.

Commit

Permalink
Merge pull request #46 from lmorchard/688245-edit-conflicts
Browse files Browse the repository at this point in the history
Bug 688245 - kuma: section edits - collisions
  • Loading branch information
groovecoder committed Nov 16, 2011
2 parents fc3f057 + e419074 commit f74cc70
Show file tree
Hide file tree
Showing 11 changed files with 591 additions and 39 deletions.
2 changes: 1 addition & 1 deletion apps/wiki/content.py
Expand Up @@ -153,7 +153,7 @@ def __iter__(self):
reverse('wiki.edit_document',
args=[self.slug],
locale=self.locale),
urlencode({'section': id, 'raw': 'true',
urlencode({'section': id,
'edit_links': 'true'})
)
}},
Expand Down
43 changes: 43 additions & 0 deletions apps/wiki/forms.py
Expand Up @@ -51,6 +51,8 @@
SLUG_COLLIDES = _lazy(u'Another document with this slug already exists.')
OTHER_COLLIDES = _lazy(u'Another document with this metadata already exists.')

MIDAIR_COLLISION = _lazy(u'This document was modified while you were editing it.')


class DocumentForm(forms.ModelForm):
"""Form to create/edit a document."""
Expand Down Expand Up @@ -223,6 +225,9 @@ class RevisionForm(forms.ModelForm):
widget=CheckboxSelectMultiple, required=False,
choices=REVIEW_FLAG_TAGS)

current_rev = forms.CharField(required=False,
widget=forms.HiddenInput())

class Meta(object):
model = Revision
fields = ('title', 'slug', 'keywords', 'summary', 'content', 'comment',
Expand Down Expand Up @@ -313,6 +318,44 @@ def clean_content(self):

return content

def clean_current_rev(self):
"""If a current revision is supplied in the form, compare it against
what the document claims is the current revision. If there's a
difference, then an edit has occurred since the form was constructed
and we treat it as a mid-air collision."""
current_rev = self.cleaned_data.get('current_rev', None)

if not current_rev:
# If there's no current_rev, just bail.
return current_rev

try:
doc_current_rev = self.instance.document.current_revision.id
if unicode(current_rev) != unicode(doc_current_rev):

if self.section_id and self.instance and self.instance.document:
# This is a section edit. So, even though the revision has
# changed, it still might not be a collision if the section
# in particular hasn't changed.
orig_ct = (Revision.objects.get(pk=current_rev)
.get_section_content(self.section_id))
curr_ct = (self.instance.document.current_revision
.get_section_content(self.section_id))
if orig_ct != curr_ct:
# Oops. Looks like the section did actually get
# changed, so yeah this is a collision.
raise forms.ValidationError(MIDAIR_COLLISION)

return current_rev

else:
# No section edit, so this is a flat-out collision.
raise forms.ValidationError(MIDAIR_COLLISION)

except Document.DoesNotExist:
# If there's no document yet, just bail.
return current_rev

def save(self, creator, document, **kwargs):
"""Persist me, and return the saved Revision.
Expand Down
7 changes: 7 additions & 0 deletions apps/wiki/models.py
Expand Up @@ -746,6 +746,13 @@ def __unicode__(self):
self.document.title,
self.id, self.content[:50])

def get_section_content(self, section_id):
"""Convenience method to extract the content for a single section"""
return(wiki.content
.parse(self.content)
.extractSection(section_id)
.serialize())

@property
def content_cleaned(self):
return bleach.clean(
Expand Down
16 changes: 13 additions & 3 deletions apps/wiki/templates/wiki/document.html
Expand Up @@ -20,7 +20,10 @@
<section id="content">
<div class="wrap">
<div id="content-main" class="full">
<article class="article" role="main">
<article class="article" role="main"
data-current-revision="{{ document.current_revision.id }}"
data-refresh-message="{{ _('Your changes were merged. However, something else has been edited, so this page will be refreshed to reflect the changes.') }}"
data-cancel-edit-message="{{ _('Abort editing in progress? Your unsaved changes will be discarded.') }}">
<header id="article-head">
<div class="title">
<h1 class="page-title">{{ document.title }}</h1>
Expand Down Expand Up @@ -71,8 +74,7 @@ <h1 class="page-title">{{ document.title }}</h1>
{% endfor %}
{% endif %}

<div id="wikiArticle" class="page-content boxed"
data-cancel-edit-message="{{ _('Abort editing in progress? Your unsaved changes will be discarded.') }}">
<div id="wikiArticle" class="page-content boxed">
{#
<div id="article-nav">
<div class="page-toc">
Expand Down Expand Up @@ -138,6 +140,14 @@ <h2>Table of Contents</h2>
<a class="cancel" href="#">{{ _('Cancel') }}</a>
<div class="src"></div>
</div>
{# This form gets filled out and used by the inline editor when a
conflict is detected. It needs to be server side in order to get supplied
with a CSRF token. #}
<form class="conflict-bouncer template" method="POST" action="">
<input type="hidden" name="form" value="rev">
<input type="hidden" name="current_rev" value="">
<input type="hidden" name="content" value="">
</form>
{% endif %}
</section>
{% endblock %}
Expand Down
73 changes: 72 additions & 1 deletion apps/wiki/templates/wiki/edit_document.html
Expand Up @@ -16,9 +16,75 @@
<div class="wrap">
<div id="content-main" class="full">
<article id="edit-document" class="article" role="main">
{{ errorlist(revision_form) }}
<form id="wiki-page-edit" class="editing" method="post" action="">
<fieldset>

{{ errorlist(revision_form) }}

{% if collision %}
<section class="collision-compare">
<p>{% trans %}
Please review the differences between your changes and the changes
saved since you started editing. You may save again, but try to
resolve any conflicts by hand so that the changes made since you
started are not lost.
{% endtrans %}</p>

<div class="revision-diff">
<header>
<div>
<h3>{{ _('Your Changes') }}</h3>
</h3>
<p>{{ _('Revision {id} by {user} on {date}')|fe(id=original_revision.id, user=original_revision.creator, date=datetimeformat(original_revision.created, format='longdatetime')) }}</p>
</div>
<div>
<h3>
<a href="{{ url('wiki.revision', current_revision.document.slug, current_revision.id) }}">
{{ _('Current Revision:')|f(num=current_revision.id) }}
</a>
</h3>
<p>{{ _('Revision {id} by {user} on {date}')|fe(id=current_revision.id, user=current_revision.creator, date=datetimeformat(current_revision.created, format='longdatetime')) }}</p>
</div>
</header>

{% set title = request.POST['title'] %}
{% set slug = request.POST['slug'] %}
{% set keywords = request.POST['request'] %}
{% set summary = request.POST['summary'] %}

{% if title and current_revision.title and title != current_revision.title %}
<h4>{{ _('Title:') }}</h4>
<div><p>{{ title }}</p></div>
<div><p>{{ current_revision.title }}</p></div>
{% endif %}

{% if slug and current_revision.slug and slug != current_revision.slug %}
<h4>{{ _('Slug:') }}</h4>
<div><p>{{ slug }}</p></div>
<div><p>{{ current_revision.slug }}</p></div>
{% endif %}

{% if keywords and current_revision.keywords and keywords != current_revision.keywords %}
<h4>{{ _('Keywords:') }}</h4>
<div><p>{{ keywords }}</p></div>
<div><p>{{ current_revision.keywords }}</p></div>
{% endif %}

{% if summary and current_revision.summary and summary != current_revision.summary %}
<h4>{{ _('Search results summary:') }}</h4>
<div><p>{{ summary }}</p></div>
<div><p>{{ current_revision.summary }}</p></div>
{% endif %}

{% if content and current_revision.content and content != current_revision.content %}
<h4>{{ _('Content:') }}</h4>
{{ diff_table(content, current_content) }}
{% endif %}

</div>
</section>
{% endif %}

<header id="article-head">

<div class="title">
Expand All @@ -37,9 +103,14 @@ <h1>{{ _('Editing <em>{title}</em>')|fe(title=revision.title) }}</h1>
</ul>
{% endif %}

{% if not collision %}
{{ revision_form.current_rev | safe }}
{% endif %}

{% include 'wiki/includes/page_buttons.html' %}

</header>

{% if revision_form %}
{{ revision_form.content | safe }}
<input type="hidden" name="form" value="rev" />
Expand Down
6 changes: 3 additions & 3 deletions apps/wiki/tests/test_content.py
Expand Up @@ -290,13 +290,13 @@ def test_section_edit_links(self):
<p>test</p>
"""
expected = """
<h1 id="s1"><a class="edit-section" data-section-id="s1" data-section-src-url="/en-US/docs/some-slug?raw=true&amp;section=s1" href="/en-US/docs/some-slug$edit?raw=true&amp;section=s1&amp;edit_links=true" title="Edit section">Edit</a>Head 1</h1>
<h1 id="s1"><a class="edit-section" data-section-id="s1" data-section-src-url="/en-US/docs/some-slug?raw=true&amp;section=s1" href="/en-US/docs/some-slug$edit?section=s1&amp;edit_links=true" title="Edit section">Edit</a>Head 1</h1>
<p>test</p>
<p>test</p>
<h2 id="s2"><a class="edit-section" data-section-id="s2" data-section-src-url="/en-US/docs/some-slug?raw=true&amp;section=s2" href="/en-US/docs/some-slug$edit?raw=true&amp;section=s2&amp;edit_links=true" title="Edit section">Edit</a>Head 2</h2>
<h2 id="s2"><a class="edit-section" data-section-id="s2" data-section-src-url="/en-US/docs/some-slug?raw=true&amp;section=s2" href="/en-US/docs/some-slug$edit?section=s2&amp;edit_links=true" title="Edit section">Edit</a>Head 2</h2>
<p>test</p>
<p>test</p>
<h3 id="s3"><a class="edit-section" data-section-id="s3" data-section-src-url="/en-US/docs/some-slug?raw=true&amp;section=s3" href="/en-US/docs/some-slug$edit?raw=true&amp;section=s3&amp;edit_links=true" title="Edit section">Edit</a>Head 3</h3>
<h3 id="s3"><a class="edit-section" data-section-id="s3" data-section-src-url="/en-US/docs/some-slug?raw=true&amp;section=s3" href="/en-US/docs/some-slug$edit?section=s3&amp;edit_links=true" title="Edit section">Edit</a>Head 3</h3>
<p>test</p>
<p>test</p>
"""
Expand Down

0 comments on commit f74cc70

Please sign in to comment.