summaryrefslogtreecommitdiffstats
path: root/hyperkitty
diff options
context:
space:
mode:
Diffstat (limited to 'hyperkitty')
-rw-r--r--hyperkitty/static/css/hyperkitty-message.css27
-rw-r--r--hyperkitty/static/js/hyperkitty-thread.js34
-rw-r--r--hyperkitty/templates/thread.html1
-rw-r--r--hyperkitty/templates/threads/category.html14
-rw-r--r--hyperkitty/templates/threads/right_col.html3
-rw-r--r--hyperkitty/urls.py2
-rw-r--r--hyperkitty/views/forms.py3
-rw-r--r--hyperkitty/views/thread.py46
8 files changed, 128 insertions, 2 deletions
diff --git a/hyperkitty/static/css/hyperkitty-message.css b/hyperkitty/static/css/hyperkitty-message.css
index 2dfb9df..f7af9b2 100644
--- a/hyperkitty/static/css/hyperkitty-message.css
+++ b/hyperkitty/static/css/hyperkitty-message.css
@@ -148,6 +148,33 @@
}
+/* Categories */
+
+#thread-overview-info #thread-category {
+ padding: 10px 0;
+ text-align: center;
+}
+#thread-overview-info #thread-category form {
+ margin: 0;
+ display: none;
+}
+#thread-overview-info #thread-category form select {
+ font-size: 90%;
+ width: 12em;
+}
+#thread-overview-info #thread-category a.label {
+ font-size: 120%;
+ line-height: 120%;
+}
+
+#thread-category .question {
+ background-color: #f89406;
+}
+#thread-category .announce {
+ background-color: #3a87ad;
+}
+
+
/* Participants */
#participants {
diff --git a/hyperkitty/static/js/hyperkitty-thread.js b/hyperkitty/static/js/hyperkitty-thread.js
index 5b7765d..f2c3cd0 100644
--- a/hyperkitty/static/js/hyperkitty-thread.js
+++ b/hyperkitty/static/js/hyperkitty-thread.js
@@ -21,6 +21,39 @@
/*
+ * Categories
+ */
+
+function setup_category() {
+ $("#thread-category form").submit(function (e) {
+ e.preventDefault();
+ $.ajax({
+ type: "POST",
+ //dataType: "json",
+ data : $(this).serialize(),
+ url: $(this).attr("action"),
+ success: function(data) {
+ $("#thread-category").html(data);
+ setup_category();
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ // authentication and invalid data
+ alert(jqXHR.responseText);
+ }
+ });
+ });
+ $("#thread-category a.label").click(function(e) {
+ e.preventDefault();
+ $(this).hide();
+ $("#thread-category form").show();
+ });
+ $("#thread-category form select").change(function() {
+ $(this).parents("form").first().submit();
+ });
+}
+
+
+/*
* Tagging
*/
@@ -39,7 +72,6 @@ function setup_tags() {
e.preventDefault();
$(this).parents("form").first().submit();
});
- console.log($(this));
$(this).find("#id_tag").value("");
},
error: function(jqXHR, textStatus, errorThrown) {
diff --git a/hyperkitty/templates/thread.html b/hyperkitty/templates/thread.html
index 491d08a..ee102fa 100644
--- a/hyperkitty/templates/thread.html
+++ b/hyperkitty/templates/thread.html
@@ -77,6 +77,7 @@
<script type="text/javascript">
$(document).ready(function() {
+ setup_category();
setup_tags();
setup_favorites();
setup_emails_list();
diff --git a/hyperkitty/templates/threads/category.html b/hyperkitty/templates/threads/category.html
new file mode 100644
index 0000000..e830678
--- /dev/null
+++ b/hyperkitty/templates/threads/category.html
@@ -0,0 +1,14 @@
+{% load url from future %}
+
+ <a class="label {{category}}">
+ {% if category %}
+ {{ category|title }}
+ {% else %}
+ No category
+ {% endif %}
+ </a>
+ <form method="post" action="{% url 'thread_set_category' mlist_fqdn=mlist.name threadid=threadid %}">
+ {% csrf_token %}
+ {{ category_form.as_p }}
+ </form>
+
diff --git a/hyperkitty/templates/threads/right_col.html b/hyperkitty/templates/threads/right_col.html
index 5384d03..c92eabd 100644
--- a/hyperkitty/templates/threads/right_col.html
+++ b/hyperkitty/templates/threads/right_col.html
@@ -38,6 +38,9 @@
<i class="unread icon-eye-close"></i> {{ unread_count }} unread messages
{% endif %}
</p>
+ <div id="thread-category">
+ {% include 'threads/category.html' %}
+ </div>
<div id="tags">
{% include 'threads/tags.html' %}
</div>
diff --git a/hyperkitty/urls.py b/hyperkitty/urls.py
index 015737b..44a6fdb 100644
--- a/hyperkitty/urls.py
+++ b/hyperkitty/urls.py
@@ -84,6 +84,8 @@ urlpatterns = patterns('hyperkitty.views',
'thread.suggest_tags', name='suggest_tags'),
url(r'^list/(?P<mlist_fqdn>[^/@]+@[^/@]+)/thread/(?P<threadid>\w+)/favorite$',
'thread.favorite', name='favorite'),
+ url(r'^list/(?P<mlist_fqdn>[^/@]+@[^/@]+)/thread/(?P<threadid>\w+)/category$',
+ 'thread.set_category', name='thread_set_category'),
# Search
diff --git a/hyperkitty/views/forms.py b/hyperkitty/views/forms.py
index 99f158f..19bba26 100644
--- a/hyperkitty/views/forms.py
+++ b/hyperkitty/views/forms.py
@@ -135,3 +135,6 @@ class PostForm(forms.Form):
message = forms.CharField(widget=forms.Textarea)
attachment = forms.FileField(required=False, label="",
widget=AttachmentFileInput)
+
+class CategoryForm(forms.Form):
+ category = forms.ChoiceField(label="", required=False)
diff --git a/hyperkitty/views/thread.py b/hyperkitty/views/thread.py
index 56f9f5c..8b080ba 100644
--- a/hyperkitty/views/thread.py
+++ b/hyperkitty/views/thread.py
@@ -34,7 +34,7 @@ from django.utils.timezone import utc
import robot_detection
from hyperkitty.models import Tag, Favorite, LastView
-from forms import AddTagForm, ReplyForm
+from hyperkitty.views.forms import AddTagForm, ReplyForm, CategoryForm
from hyperkitty.lib import get_months, get_store, stripped_subject
from hyperkitty.lib.voting import set_message_votes
@@ -99,6 +99,12 @@ def thread_index(request, mlist_fqdn, threadid, month=None, year=None):
else:
fav_action = "rm"
+ # Category
+ categories = [ (c, c.title()) for c in store.get_categories() ] \
+ + [("", "no categories")]
+ category_form = CategoryForm(initial={"category": thread.category or ""})
+ category_form["category"].field.choices = categories
+
# Extract relative dates
today = datetime.date.today()
days_old = today - thread.starting_email.date.date()
@@ -151,6 +157,8 @@ def thread_index(request, mlist_fqdn, threadid, month=None, year=None):
'participants': thread.participants,
'last_view': last_view,
'unread_count': unread_count,
+ 'category_form': category_form,
+ 'category': thread.category,
}
context["participants"].sort(key=lambda x: x[0].lower())
@@ -286,3 +294,39 @@ def favorite(request, mlist_fqdn, threadid):
raise SuspiciousOperation
return HttpResponse("success", mimetype='text/plain')
+
+def set_category(request, mlist_fqdn, threadid):
+ """ Set the category for a given thread. """
+ if not request.user.is_authenticated():
+ return HttpResponse('You must be logged in to add a tag',
+ content_type="text/plain", status=403)
+ if request.method != 'POST':
+ raise SuspiciousOperation
+
+ store = get_store(request)
+ categories = [ (c, c.title()) for c in store.get_categories() ] \
+ + [("", "No categories")]
+ category_form = CategoryForm(request.POST)
+ category_form["category"].field.choices = categories
+
+ if not category_form.is_valid():
+ return HttpResponse("Error settings category: invalid data",
+ content_type="text/plain", status=500)
+
+ category = category_form.cleaned_data["category"]
+ thread = store.get_thread(mlist_fqdn, threadid)
+ if category and category not in store.get_categories():
+ raise Http404("No such category: %s" % category)
+ if category != thread.category:
+ thread.category = category
+ store.commit()
+
+ # Now refresh the category widget
+ FakeMList = namedtuple("MailingList", ["name"])
+ context = {
+ "category_form": category_form,
+ "mlist": FakeMList(name=mlist_fqdn),
+ "threadid": threadid,
+ "category": thread.category,
+ }
+ return render(request, "threads/category.html", context)