diff options
| author | Aurélien Bompard <aurelien@bompard.org> | 2013-10-18 16:22:57 +0200 |
|---|---|---|
| committer | Aurélien Bompard <aurelien@bompard.org> | 2013-10-18 16:22:57 +0200 |
| commit | dce69ae9bc02d96ad4eae3fefd4e8b63b47f950e (patch) | |
| tree | b0f40e2033df3375a84fc60853b940f4e3123416 | |
| parent | 9e1bafebea58102e877df12932fd5b50362a7a2a (diff) | |
| download | kittystore-dce69ae9bc02d96ad4eae3fefd4e8b63b47f950e.tar.gz kittystore-dce69ae9bc02d96ad4eae3fefd4e8b63b47f950e.tar.xz kittystore-dce69ae9bc02d96ad4eae3fefd4e8b63b47f950e.zip | |
Respect list privacy when searching
| -rw-r--r-- | kittystore/storm/model.py | 1 | ||||
| -rw-r--r-- | kittystore/storm/search.py | 27 | ||||
| -rw-r--r-- | kittystore/storm/store.py | 15 | ||||
| -rw-r--r-- | kittystore/test/__init__.py | 3 |
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:" |
