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

Commit

Permalink
Merge pull request #207 from editorsnotes/fix-editing-bugs
Browse files Browse the repository at this point in the history
Fix (MORE) editing bugs...
  • Loading branch information
ptgolden committed Mar 7, 2014
2 parents fe40b3c + 0ffa874 commit 80ea268
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 55 deletions.
51 changes: 34 additions & 17 deletions editorsnotes/admin_custom/forms/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,37 @@

from editorsnotes.main.models.topics import Topic, TopicAssignment

#class TopicAssignmentWidget(forms.widgets.HiddenInput):
# def render(self, name, value, attrs=None):
# hidden_input = super(TopicAssignmentWidget, self).render(name, value, attrs=None)
# if value:
# topic = get_object_or_404(ProjectTopicContainer, id=value)
# extra_content = topic.preferred_name
# else:
# extra_content = ''
# return mark_safe(extra_content + hidden_input)
#
#class TopicAssignmentForm(ModelForm):
# class Meta:
# model = TopicNodeAssignment
# widgets = {'topic': TopicNodeAssignmentWidget()}
#
#TopicAssignmentFormset = generic_inlineformset_factory(
# TopicNodeAssignment, form=TopicNodeAssignmentForm, extra=1)
class TopicAssignmentWidget(forms.widgets.HiddenInput):
def render(self, name, value, attrs=None):
hidden_input = super(TopicAssignmentWidget, self).render(name, value, attrs=None)
if value:
topic = get_object_or_404(Topic, id=value)
extra_content = topic.preferred_name
else:
extra_content = ''
return mark_safe(extra_content + hidden_input)

class TopicAssignmentForm(ModelForm):
topic_id = forms.IntegerField(widget=TopicAssignmentWidget)
class Meta:
model = TopicAssignment
fields = ('topic_id',)
def __init__(self, *args, **kwargs):
super(TopicAssignmentForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.id:
self.fields['topic_id'].initial = self.instance.topic.id
def clean_topic_id(self):
data = self.cleaned_data['topic_id']
if self.instance and self.instance.id:
return data
# If this is a new object, the "topic_id" is actually the
# "topic_node_id". This is hacky and will be removed when we eventually
# create interfaces for editing anything with the API
topic_qs = Topic.objects.filter(topic_node_id=data)
if not topic_qs.exists():
raise forms.ValidationError('This topic does not exist.')
self.cleaned_data['topic_qs'] = topic_qs
return data

TopicAssignmentFormset = generic_inlineformset_factory(
TopicAssignment, form=TopicAssignmentForm, extra=1)
7 changes: 4 additions & 3 deletions editorsnotes/admin_custom/forms/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ def save_zotero_data(self):
elif self.changed_data is not None and 'zotero_string' in self.changed_data:
document.zotero_data = self.cleaned_data['zotero_string']
document.save()
link, created = ZoteroLink.objects.get_or_create(zotero_item=document)
if not created:
link.save()
if not document.zotero_link:
ZoteroLink.objects.create(zotero_item=document)
else:
document.zotero_link.save()
return document

class DocumentLinkForm(ModelForm):
Expand Down
6 changes: 4 additions & 2 deletions editorsnotes/admin_custom/forms/topics.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class TopicForm(models.ModelForm):
class Media:
js = (
"function/admin-bootstrap-topic.js",
"function/admin-bootstrap-note-sections.js",
)
class Meta:
model = Topic
Expand All @@ -28,7 +27,10 @@ class CitationForm(models.ModelForm):
class Meta:
model = Citation
fields = ('document', 'notes', 'ordering',)
widgets = {'document': forms.widgets.HiddenInput()}
widgets = {
'document': forms.widgets.HiddenInput(),
'ordering': forms.widgets.HiddenInput()
}

CitationFormset = generic_inlineformset_factory(
Citation, form=CitationForm, extra=1)
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,14 @@ $(document).ready(function () {
.autocomplete({
source: function (request, response) {
$.ajax({
url: '/api/topics/',
url: '/api' + window.location.pathname.match(/^\/projects\/[^/]+/)[0] + '/topics/',
dataType: 'json',
data: {'q': request.term},
beforeSend: function () {
$('#related-topics-loading').css('display', 'inline-block');
},
success: function (data) {
response($.map(data, function (item, index) {
response($.map(data.results, function (item, index) {
return { id: item.id, label: item.preferred_name, uri: item.uri };
}));
},
Expand Down Expand Up @@ -262,7 +262,7 @@ $(document).ready(function () {
// Replace this input with a text field
$oldExtraField
.prepend(ui.item.label)
.find('input[name$=topic]').val(ui.item.id);
.find('input[name$="topic_id"]').val(ui.item.id);

// Clear this input
event.preventDefault();
Expand Down
86 changes: 86 additions & 0 deletions editorsnotes/admin_custom/static/function/admin-bootstrap-topic.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,94 @@
function bindCitation($citation) {
var $input = $citation.find('.citation-document input')
, prefix = 'id_' + $citation.prop('id')
, autocompleteopts
, editor = new wysihtml5.Editor(prefix + '-notes', {
toolbar: $citation.prop('id') + '-toolbar',
parserRules: wysihtml5ParserRules,
useLineBreaks: false,
stylesheets: ['/static/function/wysihtml5/stylesheet.css']
});

$('#' + prefix + '-toolbar').show();

if ($input.length) {
autocompleteopts= $.extend($('body').data('baseAutocompleteOptions'), {
select: function (e, ui) {
if (!ui.item) return;
$citation.find('.citation-document').addClass('document-selected');
$input.replaceWith(ui.item.value);
$citation.find('#' + prefix + '-document').val(ui.item.id);
}
});

$input.autocomplete(autocompleteopts)
.data('ui-autocomplete')._renderItem = function (ul, item) {
return $('<li>')
.data('ui-autocomplete-item', item)
.append('<a>' + item.value + '</a>')
.appendTo(ul)
}
}



}

function citationFormset($el) {
var lastForm;

this.$el = $el;
this.$items = $el.find('#citation-items');
this.managementForm = $el.find('#id_citation-TOTAL_FORMS');
this.lastForm = this.$items.children().last();
this.blankForm = this.lastForm.clone();
this.counter = parseInt(this.managementForm.val(), 10);

this.$items.children().each(function (idx, el) {
bindCitation($(el));
});

return this;
}

citationFormset.prototype.addCitation = function () {
var that = this
, newForm
, searchstr

if (this.lastForm.is(':hidden')) {
this.lastForm.removeClass('hide');
return
}

newForm = this.blankForm.clone().removeClass('hide');
searchstr = 'citation-' + (this.counter - 1) + '-'

newForm.prop('id', 'citation-' + that.counter);
newForm.find('[name^="' + searchstr + '"], [id^="' + searchstr + '"]')
.each(function (idx, el) {
if (el.name) el.name = el.name.replace('-' + (that.counter - 1) + '-', '-' + that.counter + '-');
if (el.id) el.id = el.id.replace('-' + (that.counter - 1) + '-', '-' + that.counter + '-');
});

newForm.appendTo(that.$items);
bindCitation(newForm);

this.counter += 1;
this.managementForm.val(that.counter);
}


$(document).ready(function() {
var editor = new wysihtml5.Editor('id_summary', {
toolbar: 'summary-toolbar',
parserRules: wysihtml5ParserRules,
useLineBreaks: false,
stylesheets: ['/static/function/wysihtml5/stylesheet.css']
});

var formset = new citationFormset($('#citations-formset'));

$('#add-citation').on('click', formset.addCitation.bind(formset));

});
23 changes: 23 additions & 0 deletions editorsnotes/admin_custom/static/style/bootstrap-admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,29 @@
display: none;
}

/* Hide delete field for citations */
.citation input[id$="-DELETE"] {
display: none;
}

/* Topic citation spacing (DELETE ME GOD DELETE ME WHEN YOU CAN) */
.citation-document i {
font-size: 24px;
position: relative;
top: 4px;
margin-right: 4px;
}
.citation-document input {
width: 750px;
}

.citation-document.document-selected * {
display: inline-block;
}
#citations-formset .citation {
margin: 1em 0 2em;
}


/* Transcript editor should be large */
fieldset[name="transcript_content"] textarea {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
{% for citation in citations_formset %}
{% with citation.instance as c %}

<div class="citation citation-edit {% if forloop.last %}hide{% endif %}" id="{{ citation.prefix }}">
<div class="citation citation-edit {% if not c %}hide{% endif %}" id="{{ citation.prefix }}">

<div class="citation-document">
<i class="icon-file"></i>
{% if c.document %} {{ c.document.description|as_text }} {% endif %}
{% if c.document %}
{{ c.document.description|as_text }}
{% else %}
<input data-target-model="documents" data-project-slug="{{ project.slug }}" />
{% endif %}
</div>

<div class="citation-notes">{% if c.has_notes %}{{ c.notes|as_html }}{% endif %}</div>

{% include "includes/wysihtml5_full_toolbar.html" with toolbar_id=citation.prefix|add:"-toolbar" %}
<div class="citation-fields">
{% for field in citation %} {{ field }} {% endfor %}
</div>
Expand All @@ -28,7 +31,6 @@

</div>

{% include "includes/wysihtml5_full_toolbar.html" with toolbar_id="citation-toolbar" %}
{% include "includes/add_document_modal.html" %}

</div>
8 changes: 5 additions & 3 deletions editorsnotes/admin_custom/templates/topic_admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,16 @@ <h3>{% if form.instance.id %}Change {% else %}Add {% endif %}topic</h3>
</div>

<div class="article-citations" style="margin: 1em .5em;">
<h4>Citations</h4>
<legend>Citations</legend>
{% include "includes/citation_formset.html" with citations_formset=formsets.citation %}
<a href="#" id="add-citation">Add citation</a>
<button type="button" class="btn btn-primary" id="add-citation">Add citation</a>
</div>

</fieldset>

<button type="submit" class="btn btn-primary">Save</button>
<hr />

<button type="submit" class="btn">Save</button>

</form>
{% endblock %}
62 changes: 62 additions & 0 deletions editorsnotes/admin_custom/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

class TopicAdminTestCase(WebTest):
fixtures = ['projects.json']
def create_test_topic(self, name=u'Emma Goldman'):
project = main_models.Project.objects.get(slug='emma');
node, topic = main_models.Topic.objects.create_along_with_node(
name, project, project.members.get())
return topic
def test_create_topic(self):
"A user should be able to create a topic with a unique name."
topic_name = u'Emma Goldman'
Expand All @@ -33,6 +38,63 @@ def test_create_topic(self):
self.assertEqual(error_messages[0].text.strip(),
u'Topic with this preferred name already exists.')

def test_add_citation(self):
topic = self.create_test_topic()
document = main_models.Document.objects.create(
description="a test document",
project_id=topic.project_id,
creator_id=topic.creator_id,
last_updater_id=topic.last_updater_id)
url = reverse('admin:main_topic_change',
kwargs={'project_slug': 'emma', 'topic_node_id': topic.topic_node_id})

form = self.app.get(url, user='barry').forms[1]
form['citation-0-document'] = document.id
form['citation-0-notes'] = 'it was an interesting thing I found in here'

resp = form.submit().follow()
topic = main_models.Topic.objects.get(id=topic.id)
self.assertEqual(topic.summary_cites.count(), 1)
self.assertEqual(topic.summary_cites.get().document_id, document.id)
self.assertEqual(topic.summary_cites.get().object_id, topic.id)

def test_add_related_topics(self):
topic = self.create_test_topic()

# This is to make sure topic id counter and topic node id counter are
# off. hope you can handle that
main_models.TopicNode.objects.create(_preferred_name='dummy',
creator_id=1, last_updater_id=1)

topic_2 = self.create_test_topic(name=u'Alexander Berkman')
topic_3 = self.create_test_topic(name=u'Ben Reitman')

url = reverse('admin:main_topic_change',
kwargs={'project_slug': 'emma', 'topic_node_id': topic.topic_node_id})

# Add one topic
form = self.app.get(url, user='barry').forms[1]
form['topicassignment-0-topic_id'] = topic_2.topic_node_id
resp = form.submit().follow()
self.assertEqual(topic.related_topics.count(), 1)
self.assertEqual(topic.related_topics.get().topic_id, topic_2.id)

# Add another topic
form = self.app.get(url, user='barry').forms[1]
form['topicassignment-1-topic_id'] = topic_3.topic_node_id
resp = form.submit().follow()
self.assertEqual(topic.related_topics.count(), 2)
self.assertEqual(sorted([topic_2.id, topic_3.id]),
sorted(topic.related_topics.values_list('topic_id', flat=True)))

# Delete both topics
form = self.app.get(url, user='barry').forms[1]
form['topicassignment-0-DELETE'] = 'true'
form['topicassignment-1-DELETE'] = 'true'
resp = form.submit().follow()
self.assertEqual(topic.related_topics.count(), 0)


def test_create_empty_topic_error(self):
"A user should not be able to create a topic with an empty name."
add_url = reverse('admin:main_topic_add', kwargs={'project_slug': 'emma'})
Expand Down
10 changes: 5 additions & 5 deletions editorsnotes/admin_custom/views/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.views.generic.edit import View, ModelFormMixin, TemplateResponseMixin
import reversion

from editorsnotes.main.models import Project
from editorsnotes.main.models import Project, Topic
from editorsnotes.main.models.auth import RevisionProject

VIEW_ERROR_MSG = 'You do not have permission to view {}.'
Expand Down Expand Up @@ -159,15 +159,15 @@ def form_invalid(self, form, formsets):
self.get_context_data(form=form, formsets=formsets))

def save_topicassignment_formset_form(self, form):
if not form.cleaned_data['topic']:
if not form.cleaned_data['topic_qs']:
return
if form.instance and form.instance.id:
return
if form.cleaned_data['topic'] in self.object.related_topics.all():
return
ta = form.save(commit=False)
ta.topic = form.cleaned_data['topic_qs'].get(project__slug=self.project.slug)
if ta.topic in self.object.related_topics.all():
return
ta.creator = self.request.user
ta.topic = form.cleaned_data['topic']
ta.content_object = self.object
ta.save()
return ta
Loading

0 comments on commit 80ea268

Please sign in to comment.