summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAurélien Bompard <aurelien@bompard.org>2013-10-18 16:22:57 +0200
committerAurélien Bompard <aurelien@bompard.org>2013-10-18 16:22:57 +0200
commitdce69ae9bc02d96ad4eae3fefd4e8b63b47f950e (patch)
treeb0f40e2033df3375a84fc60853b940f4e3123416
parent9e1bafebea58102e877df12932fd5b50362a7a2a (diff)
downloadkittystore-dce69ae9bc02d96ad4eae3fefd4e8b63b47f950e.tar.gz
kittystore-dce69ae9bc02d96ad4eae3fefd4e8b63b47f950e.tar.xz
kittystore-dce69ae9bc02d96ad4eae3fefd4e8b63b47f950e.zip
Respect list privacy when searching
-rw-r--r--kittystore/storm/model.py1
-rw-r--r--kittystore/storm/search.py27
-rw-r--r--kittystore/storm/store.py15
-rw-r--r--kittystore/test/__init__.py3
4 files changed, 37 insertions, 9 deletions
diff --git a/kittystore/storm/model.py b/kittystore/storm/model.py
index eb89186..29cd546 100644
--- a/kittystore/storm/model.py
+++ b/kittystore/storm/model.py
@@ -162,6 +162,7 @@ class Thread(Storm):
order_by=Email.thread_order
)
category_obj = Reference(category_id, "Category.id")
+ mlist = Reference(list_name, "List.name")
_starting_email = None
def __init__(self, list_name, thread_id, date_active=None):
diff --git a/kittystore/storm/search.py b/kittystore/storm/search.py
index 3098aa6..65a79aa 100644
--- a/kittystore/storm/search.py
+++ b/kittystore/storm/search.py
@@ -18,9 +18,11 @@ import os
import shutil
from whoosh.index import create_in, exists_in, open_dir
-from whoosh.fields import Schema, ID, TEXT, DATETIME, KEYWORD
+from whoosh.fields import Schema, ID, TEXT, DATETIME, KEYWORD, BOOLEAN
from whoosh.analysis import StemmingAnalyzer
from whoosh.qparser import MultifieldParser
+from whoosh.query import Term
+from mailman.interfaces.archiver import ArchivePolicy
from .model import Email
@@ -28,6 +30,7 @@ from .model import Email
def email_to_search_doc(email):
if not isinstance(email, Email):
raise ValueError("not an instance of the Email class")
+ private_list = (email.mlist.archive_policy == ArchivePolicy.private)
search_doc = {
"list_name": email.list_name,
"message_id": email.message_id,
@@ -36,6 +39,7 @@ def email_to_search_doc(email):
"subject": email.subject,
"content": email.content,
"date": email.date, # UTC
+ "private_list": private_list,
}
attachments = [a.name for a in email.attachments]
if attachments:
@@ -61,6 +65,7 @@ class SearchEngine(object):
date=DATETIME(),
attachments=TEXT,
tags=KEYWORD(commas=True, scorable=True),
+ private_list=BOOLEAN(),
)
@property
@@ -86,7 +91,8 @@ class SearchEngine(object):
else:
writer.commit()
- def search(self, query, page=None, limit=10, sortedby=None, reverse=False):
+ def search(self, query, list_name=None, page=None, limit=10,
+ sortedby=None, reverse=False):
"""
TODO: Should the searcher be shared?
http://pythonhosted.org/Whoosh/threads.html#concurrency
@@ -94,16 +100,22 @@ class SearchEngine(object):
query = MultifieldParser(
["sender", "subject", "content", "attachments"],
self.index.schema).parse(query)
+ if list_name:
+ results_filter = Term("list_name", list_name)
+ else:
+ # When searching all lists, only the public lists are searched
+ results_filter = Term("private_list", False)
return_value = {"total": 0, "results": []}
with self.index.searcher() as searcher:
if page:
results = searcher.search_page(
query, page, pagelen=limit, sortedby=sortedby,
- reverse=reverse)
+ reverse=reverse, filter=results_filter)
return_value["total"] = results.total
else:
results = searcher.search(
- query, limit=limit, sortedby=sortedby, reverse=reverse)
+ query, limit=limit, sortedby=sortedby,
+ reverse=reverse, filter=results_filter)
# http://pythonhosted.org/Whoosh/searching.html#results-object
if results.has_exact_length():
return_value["total"] = len(results)
@@ -152,3 +164,10 @@ class SearchEngine(object):
shutil.rmtree(self.location)
self._index = None
self.initialize_with(store)
+ new_schema = self._get_schema()
+ writer = self.index.writer()
+ for field_name, field_type in new_schema.items():
+ if field_name not in self.index.schema:
+ print "Adding field %s to the search index" % field_name
+ writer.add_field(field_name, field_type)
+ writer.commit(optimize=True)
diff --git a/kittystore/storm/store.py b/kittystore/storm/store.py
index e2e439c..b640229 100644
--- a/kittystore/storm/store.py
+++ b/kittystore/storm/store.py
@@ -20,6 +20,7 @@ from urllib2 import HTTPError
from zope.interface import implements
from mailman.interfaces.messages import IMessageStore
+from mailman.interfaces.archiver import ArchivePolicy
from storm.locals import Desc
from storm.expr import And, Or, Count, Alias
from dateutil.tz import tzutc
@@ -97,6 +98,9 @@ class StormStore(object):
l.display_name = mlist.display_name
l.subject_prefix = mlist.subject_prefix
l.archive_policy = mlist.archive_policy
+ if l.archive_policy == ArchivePolicy.never:
+ print "Archiving disabled by list policy for %s" % list_name
+ return None
if not message.has_key("Message-Id"):
raise ValueError("No 'Message-Id' header in email", message)
msg_id = unicode(unquote(message['Message-Id']))
@@ -297,8 +301,10 @@ class StormStore(object):
def search(self, query, list_name=None, page=None, limit=10,
sortedby=None, reverse=False):
- """ Returns a list of email containing the specified keyword in
- their content.
+ """
+ Returns a list of email corresponding to the query string. The
+ sender, subject, content and attachment names are searched. If
+ list_name is None, all public lists are searched.
:param query: the query string to execute.
:param list_name: name of the mailing list in which this email
@@ -310,10 +316,9 @@ class StormStore(object):
by match score.
:param reverse: reverse the order of the results.
"""
- if list_name is not None:
- query += " list_name:%s" % list_name
results = self.search_index.search(
- query, page, limit, sortedby=sortedby, reverse=reverse)
+ query, list_name, page, limit, sortedby=sortedby,
+ reverse=reverse)
results["results"] = [ self.get_message_by_id_from_list(
r["list_name"], r["message_id"])
for r in results["results"] ]
diff --git a/kittystore/test/__init__.py b/kittystore/test/__init__.py
index 170473e..0270307 100644
--- a/kittystore/test/__init__.py
+++ b/kittystore/test/__init__.py
@@ -2,6 +2,8 @@
import os
+from mailman.interfaces.archiver import ArchivePolicy
+
def get_test_file(*fileparts):
return os.path.join(os.path.dirname(__file__), "testdata", *fileparts)
@@ -15,6 +17,7 @@ class FakeList(object):
self.fqdn_listname = name
self.display_name = None
self.subject_prefix = None
+ self.archive_policy = ArchivePolicy.public
class SettingsModule:
KITTYSTORE_URL = "sqlite:"