1# Copyright (C) 2001-2018 by the Free Software Foundation, Inc. 2# 3# This program is free software; you can redistribute it and/or 4# modify it under the terms of the GNU General Public License 5# as published by the Free Software Foundation; either version 2 6# of the License, or (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 16# USA. 17 18import re 19 20from Mailman import mm_cfg 21from Mailman import Utils 22from Mailman.i18n import _ 23from Mailman.Logging.Syslog import syslog 24from Mailman.Gui.GUIBase import GUIBase 25 26try: 27 True, False 28except NameError: 29 True = 1 30 False = 0 31 32OR = '|' 33 34 35 36class Topics(GUIBase): 37 def GetConfigCategory(self): 38 return 'topics', _('Topics') 39 40 def GetConfigInfo(self, mlist, category, subcat=None): 41 if category <> 'topics': 42 return None 43 WIDTH = mm_cfg.TEXTFIELDWIDTH 44 45 return [ 46 _('List topic keywords'), 47 48 ('topics_enabled', mm_cfg.Radio, (_('Disabled'), _('Enabled')), 0, 49 _('''Should the topic filter be enabled or disabled?'''), 50 51 _("""The topic filter categorizes each incoming email message 52 according to <a 53 href="https://docs.python.org/2/library/re.html">regular 54 expression filters</a> you specify below. If the message's 55 <code>Subject:</code> or <code>Keywords:</code> header contains a 56 match against a topic filter, the message is logically placed 57 into a topic <em>bucket</em>. Each user can then choose to only 58 receive messages from the mailing list for a particular topic 59 bucket (or buckets). Any message not categorized in a topic 60 bucket registered with the user is not delivered to the list. 61 62 <p>Note that this feature only works with regular delivery, not 63 digest delivery. 64 65 <p>The body of the message can also be optionally scanned for 66 <code>Subject:</code> and <code>Keywords:</code> headers, as 67 specified by the <a 68 href="?VARHELP=topics/topics_bodylines_limit">topics_bodylines_limit</a> 69 configuration variable.""")), 70 71 ('topics_bodylines_limit', mm_cfg.Number, 5, 0, 72 _('How many body lines should the topic matcher scan?'), 73 74 _("""The topic matcher will scan this many lines of the message 75 body looking for topic keyword matches. Body scanning stops when 76 either this many lines have been looked at, or a non-header-like 77 body line is encountered. By setting this value to zero, no body 78 lines will be scanned (i.e. only the <code>Keywords:</code> and 79 <code>Subject:</code> headers will be scanned). By setting this 80 value to a negative number, then all body lines will be scanned 81 until a non-header-like line is encountered. 82 """)), 83 84 ('topics', mm_cfg.Topics, 0, 0, 85 _('Topic keywords, one per line, to match against each message.'), 86 87 _("""Each topic keyword is actually a regular expression, which is 88 matched against certain parts of a mail message, specifically the 89 <code>Keywords:</code> and <code>Subject:</code> message headers. 90 Note that the first few lines of the body of the message can also 91 contain a <code>Keywords:</code> and <code>Subject:</code> 92 "header" on which matching is also performed.""")), 93 94 ] 95 96 def handleForm(self, mlist, category, subcat, cgidata, doc): 97 # MAS: Did we come from the authentication page? 98 if not cgidata.has_key('topic_box_01'): 99 return 100 topics = [] 101 # We start i at 1 and keep going until we no longer find items keyed 102 # with the marked tags. 103 i = 1 104 while True: 105 deltag = 'topic_delete_%02d' % i 106 boxtag = 'topic_box_%02d' % i 107 reboxtag = 'topic_rebox_%02d' % i 108 desctag = 'topic_desc_%02d' % i 109 wheretag = 'topic_where_%02d' % i 110 addtag = 'topic_add_%02d' % i 111 newtag = 'topic_new_%02d' % i 112 i += 1 113 # Was this a delete? If so, we can just ignore this entry 114 if cgidata.has_key(deltag): 115 continue 116 # Get the data for the current box 117 name = cgidata.getfirst(boxtag) 118 pattern = cgidata.getfirst(reboxtag) 119 desc = cgidata.getfirst(desctag) 120 if name is None: 121 # We came to the end of the boxes 122 break 123 if cgidata.has_key(newtag) and (not name or not pattern): 124 # This new entry is incomplete. 125 doc.addError(_("""Topic specifications require both a name and 126 a pattern. Incomplete topics will be ignored.""")) 127 continue 128 # Make sure the pattern was a legal regular expression 129 name = Utils.websafe(name) 130 try: 131 orpattern = OR.join(pattern.splitlines()) 132 re.compile(orpattern) 133 except (re.error, TypeError): 134 safepattern = Utils.websafe(orpattern) 135 doc.addError(_("""The topic pattern '%(safepattern)s' is not a 136 legal regular expression. It will be discarded.""")) 137 continue 138 # Was this an add item? 139 if cgidata.has_key(addtag): 140 # Where should the new one be added? 141 where = cgidata.getfirst(wheretag) 142 if where == 'before': 143 # Add a new empty topics box before the current one 144 topics.append(('', '', '', True)) 145 topics.append((name, pattern, desc, False)) 146 # Default is to add it after... 147 else: 148 topics.append((name, pattern, desc, False)) 149 topics.append(('', '', '', True)) 150 # Otherwise, just retain this one in the list 151 else: 152 topics.append((name, pattern, desc, False)) 153 # Add these topics to the mailing list object, and deal with other 154 # options. 155 mlist.topics = topics 156 try: 157 mlist.topics_enabled = int(cgidata.getfirst( 158 'topics_enabled', 159 mlist.topics_enabled)) 160 except ValueError: 161 # BAW: should really print a warning 162 pass 163 try: 164 mlist.topics_bodylines_limit = int(cgidata.getfirst( 165 'topics_bodylines_limit', 166 mlist.topics_bodylines_limit)) 167 except ValueError: 168 # BAW: should really print a warning 169 pass 170