[snowy] Add note searching. Needs lots of UI love though.
- From: Brad Taylor <btaylor src gnome org>
- To: svn-commits-list gnome org
- Subject: [snowy] Add note searching. Needs lots of UI love though.
- Date: Fri, 24 Jul 2009 20:59:50 +0000 (UTC)
commit 6419f018fc52d1d498406cf4df38a3b402aadf20
Author: Brad Taylor <brad getcoded net>
Date: Fri Jul 24 16:59:43 2009 -0400
Add note searching. Needs lots of UI love though.
TODO | 1 -
core/templatetags/truncate.py | 48 ++++++++++++++++++++++++++
notes/forms.py | 25 +++++++++++++
notes/templates/notes/base.html | 11 +++++-
notes/templates/notes/note_list.html | 37 +-------------------
notes/templates/notes/note_list_snippet.html | 35 +++++++++++++++++++
notes/templates/notes/note_search.html | 24 +++++++++++++
notes/urls.py | 1 +
notes/views.py | 25 ++++++++++++--
9 files changed, 166 insertions(+), 41 deletions(-)
---
diff --git a/TODO b/TODO
index 63fe53e..4d80258 100644
--- a/TODO
+++ b/TODO
@@ -28,7 +28,6 @@ TODO
* Note View
- Show which notebooks a note is part of
- - Note searching
- Better page to show when current user can't view a note
* Note Editing
diff --git a/core/templatetags/truncate.py b/core/templatetags/truncate.py
new file mode 100644
index 0000000..7c47600
--- /dev/null
+++ b/core/templatetags/truncate.py
@@ -0,0 +1,48 @@
+#
+# From http://www.djangosnippets.org/snippets/1516/
+# Under the public domain as specified at:
+# http://www.djangosnippets.org/about/tos/
+#
+
+from django.template import Library
+from django.utils.encoding import force_unicode
+from django.utils.functional import allow_lazy
+from django.template.defaultfilters import stringfilter
+
+register = Library()
+
+def truncate_chars(s, num):
+ """
+ Template filter to truncate a string to at most num characters respecting word
+ boundaries.
+ """
+ s = force_unicode(s)
+ length = int(num)
+ if len(s) > length:
+ length = length - 3
+ if s[length-1] == ' ' or s[length] == ' ':
+ s = s[:length].strip()
+ else:
+ words = s[:length].split()
+ if len(words) > 1:
+ del words[-1]
+ s = u' '.join(words)
+ s += '...'
+ return s
+truncate_chars = allow_lazy(truncate_chars, unicode)
+
+def truncatechars(value, arg):
+ """
+ Truncates a string after a certain number of characters, but respects word boundaries.
+
+ Argument: Number of characters to truncate after.
+ """
+ try:
+ length = int(arg)
+ except ValueError: # If the argument is not a valid integer.
+ return value # Fail silently.
+ return truncate_chars(value, length)
+truncatechars.is_safe = True
+truncatechars = stringfilter(truncatechars)
+
+register.filter(truncatechars)
diff --git a/notes/forms.py b/notes/forms.py
new file mode 100644
index 0000000..c93fe57
--- /dev/null
+++ b/notes/forms.py
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2009 Brad Taylor <brad getcoded net>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from django import forms
+
+class SearchForm(forms.Form):
+ """
+ Form that allows users to search for text in the body and title of their
+ notes.
+ """
+ query = forms.fields.CharField(min_length=2, max_length=30)
diff --git a/notes/templates/notes/base.html b/notes/templates/notes/base.html
index 312f323..4f4cae4 100644
--- a/notes/templates/notes/base.html
+++ b/notes/templates/notes/base.html
@@ -3,7 +3,9 @@
{% load i18n %}
{% block sidebar %}
-<input type="text" id="search" class="dim" name="search" accesskey="s" value="{% trans "Search your notes" %}">
+<form method="GET" name="searchForm" action="{% url note_search request.user.username %}">
+ <input type="text" id="search" class="dim" name="query" accesskey="s" value="{% trans "Search your notes" %}">
+</form>
{% endblock %}
{% block extra_body %}
@@ -15,6 +17,13 @@ $(document).ready(function() {
.unbind("focus", undim);
}
$("#search").bind("focus", undim);
+
+ $('#search').keyup(function(e) {
+ // enter key
+ if (e.keyCode == 13) {
+ document.searchForm.submit();
+ }
+ });
});
</script>
{% endblock %}
diff --git a/notes/templates/notes/note_list.html b/notes/templates/notes/note_list.html
index 98152a1..152dfb1 100644
--- a/notes/templates/notes/note_list.html
+++ b/notes/templates/notes/note_list.html
@@ -1,45 +1,10 @@
{% extends 'notes/base.html' %}
{% load i18n %}
-{% load humanize %}
-{% load pagination_tags %}
{% block title %}{% trans "All Notes" %} | {{ block.super }}{% endblock %}
{% block content-container %}
<h1>{% trans "All Notes" %}</h1>
-<table class="object_list" cellspacing="0" cellpadding="0">
- <thead>
- <tr>
- <th></th>
- <th>{% trans "Title" %}</th>
- <th>{% trans "Last Modified" %}</th>
- <th>{% trans "Visibility" %}</th>
- </tr>
- </thead>
- <tbody>
-{% autopaginate notes %}
-{% for note in notes %}
- <tr class="{% cycle 'row-odd' 'row-even' %}">
-{% if note.pinned %}
- <td class="icon"><img src="{{ MEDIA_URL }}img/pin-down_16.png" width="16" height="16" alt="Pinned"/></td>
-{% else %}
- <td class="icon"><img src="{{ MEDIA_URL }}img/note_16.png" width="16" height="16" alt="Not pinned"/></td>
-{% endif %}
- <td><a href="{{ note.get_absolute_url }}">{{ note.title }}</a></td>
- <td>{{ note.user_modified|naturalday|title }}</td>
- <td>{{ note.get_permissions_display }}</td>
- </tr>
-{% endfor %}
- </tbody>
- <tfoot>
- <tr>
-{% ifnotequal paginator.num_pages 1 %}
- <td colspan="4">{% paginate %}</td>
-{% else %}
- <td colspan="4">{% trans "Page 1 of 1" %}</td>
-{% endifnotequal %}
- </tr>
- </tfoot>
-</table>
+{% include "notes/note_list_snippet.html" %}
{% endblock %}
diff --git a/notes/templates/notes/note_list_snippet.html b/notes/templates/notes/note_list_snippet.html
new file mode 100644
index 0000000..e94fc5f
--- /dev/null
+++ b/notes/templates/notes/note_list_snippet.html
@@ -0,0 +1,35 @@
+{% load i18n %}{% load humanize %}{% load pagination_tags %}
+<table class="object_list" cellspacing="0" cellpadding="0">
+ <thead>
+ <tr>
+ <th></th>
+ <th>{% trans "Title" %}</th>
+ <th>{% trans "Last Modified" %}</th>
+ <th>{% trans "Visibility" %}</th>
+ </tr>
+ </thead>
+ <tbody>
+{% autopaginate notes %}
+{% for note in notes %}
+ <tr class="{% cycle 'row-odd' 'row-even' %}">
+{% if note.pinned %}
+ <td class="icon"><img src="{{ MEDIA_URL }}img/pin-down_16.png" width="16" height="16" alt="Pinned"/></td>
+{% else %}
+ <td class="icon"><img src="{{ MEDIA_URL }}img/note_16.png" width="16" height="16" alt="Not pinned"/></td>
+{% endif %}
+ <td><a href="{{ note.get_absolute_url }}">{{ note.title }}</a></td>
+ <td>{{ note.user_modified|naturalday|title }}</td>
+ <td>{{ note.get_permissions_display }}</td>
+ </tr>
+{% endfor %}
+ </tbody>
+ <tfoot>
+ <tr>
+{% ifnotequal paginator.num_pages 1 %}
+ <td colspan="4">{% paginate %}</td>
+{% else %}
+ <td colspan="4">{% blocktrans count paginator.count as c %}{{ c }} result found{% plural %}{{ c }} results found{% endblocktrans %}</td>
+{% endifnotequal %}
+ </tr>
+ </tfoot>
+</table>
diff --git a/notes/templates/notes/note_search.html b/notes/templates/notes/note_search.html
new file mode 100644
index 0000000..1b85cb5
--- /dev/null
+++ b/notes/templates/notes/note_search.html
@@ -0,0 +1,24 @@
+{% extends 'notes/base.html' %}
+
+{% load i18n %}
+{% load truncate %}
+
+{% block title %}{% blocktrans with query|truncatechars:20 as q %}Notes matching "{{ q }}"{% endblocktrans %} | {{ block.super }}{% endblock %}
+
+{% block content-container %}
+<h1>{% trans "Search" %}</h1>
+<form method="GET">
+ <table class="input-form">
+ {{ form.as_table }}
+ <tfoot>
+ <tr>
+ <th></th>
+ <td>
+ <input type="submit" value="{% trans "Search" %}"/>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+</form>
+{% include "notes/note_list_snippet.html" %}
+{% endblock %}
diff --git a/notes/urls.py b/notes/urls.py
index 6bc35cc..6f3a68f 100644
--- a/notes/urls.py
+++ b/notes/urls.py
@@ -21,6 +21,7 @@ from snowy.notes.models import Note
urlpatterns = patterns('',
url(r'^$', 'snowy.notes.views.note_index', name='note_index'),
url(r'^list/$', 'snowy.notes.views.note_list', name='note_list'),
+ url(r'^search/$', 'snowy.notes.views.note_search', name='note_search'),
url(r'^(?P<note_id>\d+)/$', 'snowy.notes.views.note_detail', name='note_detail_no_slug'),
url(r'^(?P<note_id>\d+)/(?P<slug>[^/]+)/$', 'snowy.notes.views.note_detail', name='note_detail'),
)
diff --git a/notes/views.py b/notes/views.py
index 24b8ba4..ec0abba 100644
--- a/notes/views.py
+++ b/notes/views.py
@@ -17,13 +17,15 @@
from django.http import HttpResponseRedirect, HttpResponseForbidden, Http404
from django.shortcuts import render_to_response, get_object_or_404
-from django.core.paginator import Paginator
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
+from django.core.paginator import Paginator
from django.contrib.auth.models import User
from django.template import RequestContext
+from django.db.models import Q
from snowy.notes.templates import CONTENT_TEMPLATES, DEFAULT_CONTENT_TEMPLATE
+from snowy.notes.forms import SearchForm
from snowy.notes.models import *
from snowy import settings
@@ -45,12 +47,29 @@ def note_index(request, username,
def note_list(request, username,
template_name='notes/note_list.html'):
author = get_object_or_404(User, username=username)
- notes = Note.objects.user_viewable(request.user, author) \
- .order_by('-user_modified')
+ notes = Note.objects.user_viewable(request.user, author)
return render_to_response(template_name,
{'notes': notes},
context_instance=RequestContext(request))
+def note_search(request, username,
+ template_name='notes/note_search.html'):
+ author = get_object_or_404(User, username=username)
+ notes = []
+ if request.method == 'GET':
+ form = SearchForm(request.GET)
+ if form.is_valid():
+ query = request.GET['query']
+ notes = Note.objects.user_viewable(request.user, author) \
+ .filter(Q(title__icontains=query) \
+ | Q(content__icontains=query))
+ else:
+ form = SearchForm()
+
+ return render_to_response(template_name,
+ {'notes': notes, 'form': form},
+ context_instance=RequestContext(request))
+
def note_detail(request, username, note_id, slug='',
template_name='notes/note_detail.html'):
def clean_content(xml, author):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]