sup 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sup might be problematic. Click here for more details.
- data/History.txt +8 -0
- data/Manifest.txt +1 -0
- data/Rakefile +11 -3
- data/ReleaseNotes +6 -0
- data/bin/sup +21 -6
- data/bin/sup-sync +3 -2
- data/bin/sup-sync-back +5 -1
- data/doc/FAQ.txt +1 -1
- data/lib/sup.rb +4 -3
- data/lib/sup/buffer.rb +14 -3
- data/lib/sup/contact.rb +1 -1
- data/lib/sup/crypto.rb +1 -1
- data/lib/sup/hook.rb +1 -1
- data/lib/sup/imap.rb +1 -1
- data/lib/sup/index.rb +62 -33
- data/lib/sup/maildir.rb +13 -11
- data/lib/sup/mbox.rb +20 -19
- data/lib/sup/mbox/loader.rb +3 -2
- data/lib/sup/mbox/ssh-file.rb +1 -0
- data/lib/sup/message-chunks.rb +4 -2
- data/lib/sup/message.rb +16 -8
- data/lib/sup/modes/compose-mode.rb +1 -1
- data/lib/sup/modes/contact-list-mode.rb +1 -1
- data/lib/sup/modes/edit-message-mode.rb +7 -3
- data/lib/sup/modes/inbox-mode.rb +1 -1
- data/lib/sup/modes/label-search-results-mode.rb +2 -2
- data/lib/sup/modes/reply-mode.rb +4 -0
- data/lib/sup/modes/scroll-mode.rb +22 -7
- data/lib/sup/modes/search-results-mode.rb +3 -3
- data/lib/sup/modes/thread-index-mode.rb +66 -22
- data/lib/sup/modes/thread-view-mode.rb +36 -16
- data/lib/sup/person.rb +6 -2
- data/lib/sup/poll.rb +3 -3
- data/lib/sup/sent.rb +3 -4
- data/lib/sup/tagger.rb +4 -2
- data/lib/sup/textfield.rb +1 -1
- data/lib/sup/thread.rb +4 -10
- data/test/test_maildir.rb +25 -0
- metadata +4 -2
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.5 / 2008-04-22
|
2
|
+
* new hooks: extra-contact-addresses, startup
|
3
|
+
* '!!' now loads all threads in current search
|
4
|
+
* general state saving speedup
|
5
|
+
* threads with unsent draft messages are now shown in red
|
6
|
+
* --compose spawns a compose-message buffer on startup
|
7
|
+
* Many bugfixes and UI improvements
|
8
|
+
|
1
9
|
== 0.4 / 2008-01-23
|
2
10
|
* GPG support for signing and encrypting outgoing mail
|
3
11
|
* New hooks: mime attachment, attribution line
|
data/Manifest.txt
CHANGED
data/Rakefile
CHANGED
@@ -9,9 +9,13 @@ end # thanks to "Mike H"
|
|
9
9
|
|
10
10
|
## allow people who use development versions by running "rake gem"
|
11
11
|
## and installing the resulting gem it to be able to do this. (gem
|
12
|
-
## versions must be in dotted-digit notation only
|
13
|
-
|
14
|
-
|
12
|
+
## versions must be in dotted-digit notation only and can be passed
|
13
|
+
## with the REL environment variable to "rake gem").
|
14
|
+
if ENV['REL']
|
15
|
+
version = ENV['REL']
|
16
|
+
else
|
17
|
+
version = Redwood::VERSION == "git" ? "999" : Redwood::VERSION
|
18
|
+
end
|
15
19
|
Hoe.new('sup', version) do |p|
|
16
20
|
p.rubyforge_name = 'sup'
|
17
21
|
p.author = "William Morgan"
|
@@ -50,3 +54,7 @@ end
|
|
50
54
|
|
51
55
|
# vim: syntax=ruby
|
52
56
|
# -*- ruby -*-
|
57
|
+
task :upload_report do |t|
|
58
|
+
sh "ditz html ditz"
|
59
|
+
sh "rsync -essh -cavz ditz wmorgan@rubyforge.org:/var/www/gforge-projects/sup/"
|
60
|
+
end
|
data/ReleaseNotes
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
Release 0.5:
|
2
|
+
|
3
|
+
Saving message state (pressing "$") has been sped up. However, this is only
|
4
|
+
automatically in effect for new messages. To make it effective for older
|
5
|
+
messages (i.e. messages indexed with versions of Sup before 0.5), you must
|
6
|
+
reindex them, e.g. by running sup-sync --all on a source.
|
data/bin/sup
CHANGED
@@ -7,7 +7,7 @@ require 'fileutils'
|
|
7
7
|
require 'trollop'
|
8
8
|
require "sup"
|
9
9
|
|
10
|
-
BIN_VERSION = "0.
|
10
|
+
BIN_VERSION = "0.5"
|
11
11
|
|
12
12
|
unless Redwood::VERSION == BIN_VERSION
|
13
13
|
$stderr.puts <<EOS
|
@@ -32,12 +32,19 @@ Usage:
|
|
32
32
|
|
33
33
|
Options are:
|
34
34
|
EOS
|
35
|
-
opt :list_hooks, "List all hooks and descriptions
|
36
|
-
opt :no_threads, "Turn
|
35
|
+
opt :list_hooks, "List all hooks and descriptions, and quit."
|
36
|
+
opt :no_threads, "Turn off threading. Helps with debugging. (Necessarily disables background polling for new messages.)"
|
37
37
|
opt :no_initial_poll, "Don't poll for new messages when starting."
|
38
|
-
opt :search, "Search for
|
38
|
+
opt :search, "Search for this query upon startup", :type => String
|
39
|
+
opt :compose, "Compose message to this recipient upon startup", :type => String
|
39
40
|
end
|
40
41
|
|
42
|
+
Redwood::HookManager.register "startup", <<EOS
|
43
|
+
Executes at startup
|
44
|
+
No variables.
|
45
|
+
No return value.
|
46
|
+
EOS
|
47
|
+
|
41
48
|
if $opts[:list_hooks]
|
42
49
|
Redwood::HookManager.print_hooks
|
43
50
|
exit
|
@@ -94,7 +101,7 @@ rescue Index::LockError => e
|
|
94
101
|
|
95
102
|
case h.ask("Should I ask that process to kill itself? ")
|
96
103
|
when /^\s*y\s*$/i
|
97
|
-
h.say "Ok, suggesting
|
104
|
+
h.say "Ok, suggesting seppuku..."
|
98
105
|
FileUtils.touch Redwood::SUICIDE_FN
|
99
106
|
sleep SuicideManager::DELAY * 2
|
100
107
|
FileUtils.rm_f Redwood::SUICIDE_FN
|
@@ -127,6 +134,8 @@ begin
|
|
127
134
|
Index.add_source SentManager.new_source
|
128
135
|
end
|
129
136
|
|
137
|
+
HookManager.run "startup"
|
138
|
+
|
130
139
|
log "starting curses"
|
131
140
|
start_cursing
|
132
141
|
|
@@ -137,6 +146,8 @@ begin
|
|
137
146
|
Ncurses::A_BOLD
|
138
147
|
c.add :index_starred_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK,
|
139
148
|
Ncurses::A_BOLD
|
149
|
+
c.add :index_draft_color, Ncurses::COLOR_RED, Ncurses::COLOR_BLACK,
|
150
|
+
Ncurses::A_BOLD
|
140
151
|
c.add :labellist_old_color, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK
|
141
152
|
c.add :labellist_new_color, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK,
|
142
153
|
Ncurses::A_BOLD
|
@@ -198,6 +209,10 @@ begin
|
|
198
209
|
|
199
210
|
imode.load_threads :num => ibuf.content_height, :when_done => lambda { reporting_thread("poll after loading inbox") { sleep 1; PollManager.poll } unless $opts[:no_threads] || $opts[:no_initial_poll] }
|
200
211
|
|
212
|
+
if $opts[:compose]
|
213
|
+
ComposeMode.spawn_nicely :to_default => $opts[:compose]
|
214
|
+
end
|
215
|
+
|
201
216
|
unless $opts[:no_threads]
|
202
217
|
PollManager.start
|
203
218
|
SuicideManager.start
|
@@ -298,7 +313,7 @@ ensure
|
|
298
313
|
Redwood::log "stopped cursing"
|
299
314
|
|
300
315
|
if SuicideManager.instantiated? && SuicideManager.die?
|
301
|
-
Redwood::log "I've been ordered to commit
|
316
|
+
Redwood::log "I've been ordered to commit seppuku. I obey!"
|
302
317
|
end
|
303
318
|
|
304
319
|
if $exceptions.empty?
|
data/bin/sup-sync
CHANGED
@@ -133,7 +133,7 @@ begin
|
|
133
133
|
num_added = num_updated = num_scanned = num_restored = 0
|
134
134
|
last_info_time = start_time = Time.now
|
135
135
|
|
136
|
-
Redwood::PollManager.add_messages_from source do |m, offset, entry|
|
136
|
+
Redwood::PollManager.add_messages_from source, :force_overwrite => true do |m, offset, entry|
|
137
137
|
num_scanned += 1
|
138
138
|
seen[m.id] = true
|
139
139
|
|
@@ -227,6 +227,8 @@ begin
|
|
227
227
|
$stderr.puts "Deleted #{num_del} / #{num_scanned} messages"
|
228
228
|
end
|
229
229
|
|
230
|
+
index.save
|
231
|
+
|
230
232
|
if opts[:optimize]
|
231
233
|
$stderr.puts "Optimizing index..."
|
232
234
|
optt = time { index.index.optimize unless opts[:dry_run] }
|
@@ -238,7 +240,6 @@ rescue Exception => e
|
|
238
240
|
File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
|
239
241
|
raise
|
240
242
|
ensure
|
241
|
-
index.save
|
242
243
|
Redwood::finish
|
243
244
|
index.unlock
|
244
245
|
end
|
data/bin/sup-sync-back
CHANGED
@@ -51,7 +51,11 @@ EOS
|
|
51
51
|
end
|
52
52
|
|
53
53
|
unless opts[:drop_deleted] || opts[:move_deleted] || opts[:drop_spam] || opts[:move_spam]
|
54
|
-
puts
|
54
|
+
puts <<EOS
|
55
|
+
Nothing to do. Please specify at least one of --drop-deleted, --move-deleted,
|
56
|
+
--drop-spam, or --move-spam.
|
57
|
+
EOS
|
58
|
+
|
55
59
|
exit
|
56
60
|
end
|
57
61
|
|
data/doc/FAQ.txt
CHANGED
@@ -111,7 +111,7 @@ S: The current solution is to directly modify RubyMail. Change line 159 of
|
|
111
111
|
multipart.rb to:
|
112
112
|
chunk = chunk[0..start]
|
113
113
|
This is because RubyMail hasn't been updated since like Ruby 1.8.2.
|
114
|
-
Please bug Matt
|
114
|
+
Please bug Matt Armstrong.
|
115
115
|
|
116
116
|
P: I see this error:
|
117
117
|
/usr/local/lib/ruby/1.8/yaml.rb:133:in `transfer': allocator undefined for Bignum (TypeError)
|
data/lib/sup.rb
CHANGED
@@ -33,7 +33,7 @@ class Module
|
|
33
33
|
end
|
34
34
|
|
35
35
|
module Redwood
|
36
|
-
VERSION = "0.
|
36
|
+
VERSION = "0.5"
|
37
37
|
|
38
38
|
BASE_DIR = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
|
39
39
|
CONFIG_FN = File.join(BASE_DIR, "config.yaml")
|
@@ -72,7 +72,7 @@ module Redwood
|
|
72
72
|
def save_yaml_obj object, fn, safe=false
|
73
73
|
if safe
|
74
74
|
safe_fn = "#{File.dirname fn}/safe_#{File.basename fn}"
|
75
|
-
mode = File.stat(fn) if File.exists? fn
|
75
|
+
mode = File.stat(fn).mode if File.exists? fn
|
76
76
|
File.open(safe_fn, "w", mode) { |f| f.puts object.to_yaml }
|
77
77
|
FileUtils.mv safe_fn, fn
|
78
78
|
else
|
@@ -169,7 +169,8 @@ if File.exists? Redwood::CONFIG_FN
|
|
169
169
|
else
|
170
170
|
require 'etc'
|
171
171
|
require 'socket'
|
172
|
-
name = Etc.getpwnam(ENV["USER"]).gecos.split(/,/).first
|
172
|
+
name = Etc.getpwnam(ENV["USER"]).gecos.split(/,/).first rescue nil
|
173
|
+
name ||= ENV["USER"]
|
173
174
|
email = ENV["USER"] + "@" +
|
174
175
|
begin
|
175
176
|
Socket.gethostbyname(Socket.gethostname).first
|
data/lib/sup/buffer.rb
CHANGED
@@ -165,6 +165,15 @@ called at least once per keystroke, so excessive computation is discouraged.
|
|
165
165
|
|
166
166
|
Variables: the same as status-bar-text hook.
|
167
167
|
Return value: a string to be used as the terminal title.
|
168
|
+
EOS
|
169
|
+
|
170
|
+
HookManager.register "extra-contact-addresses", <<EOS
|
171
|
+
A list of extra addresses to propose for tab completion, etc. when the
|
172
|
+
user is entering an email address. Can be plain email addresses or can
|
173
|
+
be full "User Name <email@domain.tld>" entries.
|
174
|
+
|
175
|
+
Variables: none
|
176
|
+
Return value: an array of email address strings.
|
168
177
|
EOS
|
169
178
|
|
170
179
|
def initialize
|
@@ -263,7 +272,8 @@ EOS
|
|
263
272
|
get_status_and_title @focus_buf # must be called outside of the ncurses lock
|
264
273
|
end
|
265
274
|
|
266
|
-
|
275
|
+
## http://rtfm.etla.org/xterm/ctlseq.html (see Operating System Controls)
|
276
|
+
print "\033]0;#{title}\07" if title && @in_x
|
267
277
|
|
268
278
|
Ncurses.mutex.lock unless opts[:sync] == false
|
269
279
|
|
@@ -455,7 +465,7 @@ EOS
|
|
455
465
|
elsif File.directory?(answer)
|
456
466
|
spawn_modal "file browser", FileBrowserMode.new(answer)
|
457
467
|
else
|
458
|
-
answer
|
468
|
+
File.expand_path answer
|
459
469
|
end
|
460
470
|
end
|
461
471
|
|
@@ -492,6 +502,7 @@ EOS
|
|
492
502
|
contacts = ContactManager.contacts.map { |c| [ContactManager.alias_for(c), c.full_address, c.email] }
|
493
503
|
|
494
504
|
completions = (recent + contacts).flatten.uniq.sort
|
505
|
+
completions += HookManager.run("extra-contact-addresses") || []
|
495
506
|
answer = BufferManager.ask_many_emails_with_completions domain, question, completions, default
|
496
507
|
|
497
508
|
if answer
|
@@ -557,7 +568,6 @@ EOS
|
|
557
568
|
|
558
569
|
def ask_getch question, accept=nil
|
559
570
|
raise "impossible!" if @asking
|
560
|
-
@asking = true
|
561
571
|
|
562
572
|
accept = accept.split(//).map { |x| x[0] } if accept
|
563
573
|
|
@@ -570,6 +580,7 @@ EOS
|
|
570
580
|
Ncurses.refresh
|
571
581
|
end
|
572
582
|
|
583
|
+
@asking = true
|
573
584
|
ret = nil
|
574
585
|
done = false
|
575
586
|
until done
|
data/lib/sup/contact.rb
CHANGED
data/lib/sup/crypto.rb
CHANGED
@@ -40,7 +40,7 @@ class CryptoManager
|
|
40
40
|
raise Error, (output || "gpg command failed: #{cmd}") unless $?.success?
|
41
41
|
|
42
42
|
envelope = RMail::Message.new
|
43
|
-
envelope.header["Content-Type"] = 'multipart/signed; protocol=
|
43
|
+
envelope.header["Content-Type"] = 'multipart/signed; protocol=application/pgp-signature; micalg=pgp-sha1'
|
44
44
|
|
45
45
|
envelope.add_part payload
|
46
46
|
signature = RMail::Message.make_attachment output, "application/pgp-signature", nil, "signature.asc"
|
data/lib/sup/hook.rb
CHANGED
@@ -86,7 +86,7 @@ class HookManager
|
|
86
86
|
log "error running hook: #{e.message}"
|
87
87
|
log e.backtrace.join("\n")
|
88
88
|
@hooks[name] = nil # disable it
|
89
|
-
BufferManager.flash "Error running hook: #{e.message}"
|
89
|
+
BufferManager.flash "Error running hook: #{e.message}" if BufferManager.instantiated?
|
90
90
|
end
|
91
91
|
context.__cleanup
|
92
92
|
result
|
data/lib/sup/imap.rb
CHANGED
data/lib/sup/index.rb
CHANGED
@@ -125,9 +125,13 @@ EOS
|
|
125
125
|
@sources[source.id] = source
|
126
126
|
end
|
127
127
|
|
128
|
-
def
|
129
|
-
|
130
|
-
|
128
|
+
def sources
|
129
|
+
## favour the inbox by listing non-archived sources first
|
130
|
+
@sources.values.sort_by { |s| s.id }.partition { |s| !s.archived? }.flatten
|
131
|
+
end
|
132
|
+
|
133
|
+
def source_for uri; sources.find { |s| s.is_source_for? uri }; end
|
134
|
+
def usual_sources; sources.find_all { |s| s.usual? }; end
|
131
135
|
|
132
136
|
def load_index dir=File.join(@dir, "ferret")
|
133
137
|
if File.exists? dir
|
@@ -137,11 +141,11 @@ EOS
|
|
137
141
|
else
|
138
142
|
Redwood::log "creating index..."
|
139
143
|
field_infos = Ferret::Index::FieldInfos.new :store => :yes
|
140
|
-
field_infos.add_field :message_id
|
144
|
+
field_infos.add_field :message_id, :index => :untokenized
|
141
145
|
field_infos.add_field :source_id
|
142
146
|
field_infos.add_field :source_info
|
143
147
|
field_infos.add_field :date, :index => :untokenized
|
144
|
-
field_infos.add_field :body
|
148
|
+
field_infos.add_field :body
|
145
149
|
field_infos.add_field :label
|
146
150
|
field_infos.add_field :subject
|
147
151
|
field_infos.add_field :from
|
@@ -157,7 +161,7 @@ EOS
|
|
157
161
|
## and adding either way. Index state will be determined by m.labels.
|
158
162
|
##
|
159
163
|
## docid and entry can be specified if they're already known.
|
160
|
-
def sync_message m, docid=nil, entry=nil
|
164
|
+
def sync_message m, docid=nil, entry=nil, opts={}
|
161
165
|
docid, entry = load_entry_for_id m.id unless docid && entry
|
162
166
|
|
163
167
|
raise "no source info for message #{m.id}" unless m.source && m.source_info
|
@@ -170,7 +174,6 @@ EOS
|
|
170
174
|
m.source.id or raise "unregistered source #{m.source} (id #{m.source.id.inspect})"
|
171
175
|
end
|
172
176
|
|
173
|
-
to = (m.to + m.cc + m.bcc).map { |x| x.email }.join(" ")
|
174
177
|
snippet =
|
175
178
|
if m.snippet_contains_encrypted_content? && $config[:discard_snippets_from_encrypted_messages]
|
176
179
|
""
|
@@ -178,18 +181,52 @@ EOS
|
|
178
181
|
m.snippet
|
179
182
|
end
|
180
183
|
|
184
|
+
## write the new document to the index. if the entry already exists in the
|
185
|
+
## index, reuse it (which avoids having to reload the entry from the source,
|
186
|
+
## which can be quite expensive for e.g. large threads of IMAP actions.)
|
187
|
+
##
|
188
|
+
## exception: if the index entry belongs to an earlier version of the
|
189
|
+
## message, use everything from the new message instead, but union the
|
190
|
+
## flags. this allows messages sent to mailing lists to have their header
|
191
|
+
## updated and to have flags set properly.
|
192
|
+
##
|
193
|
+
## minor hack: messages in sources with lower ids have priority over
|
194
|
+
## messages in sources with higher ids. so messages in the inbox will
|
195
|
+
## override everyone, and messages in the sent box will be overridden
|
196
|
+
## by everyone else.
|
197
|
+
##
|
198
|
+
## written in this manner to support previous versions of the index which
|
199
|
+
## did not keep around the entry body. upgrading is thus seamless.
|
200
|
+
entry ||= {}
|
201
|
+
labels = m.labels.uniq # override because this is the new state, unless...
|
202
|
+
|
203
|
+
## if we are a later version of a message, ignore what's in the index,
|
204
|
+
## but merge in the labels.
|
205
|
+
if entry[:source_id] && entry[:source_info] && entry[:label] &&
|
206
|
+
((entry[:source_id].to_i > source_id) || (entry[:source_info].to_i < m.source_info))
|
207
|
+
labels = (entry[:label].split(/\s+/).map { |l| l.intern } + m.labels).uniq
|
208
|
+
#Redwood::log "found updated version of message #{m.id}: #{m.subj}"
|
209
|
+
#Redwood::log "previous version was at #{entry[:source_id].inspect}:#{entry[:source_info].inspect}, this version at #{source_id.inspect}:#{m.source_info.inspect}"
|
210
|
+
#Redwood::log "merged labels are #{labels.inspect} (index #{entry[:label].inspect}, message #{m.labels.inspect})"
|
211
|
+
entry = {}
|
212
|
+
end
|
213
|
+
|
214
|
+
## if force_overwite is true, ignore what's in the index. this is used
|
215
|
+
## primarily by sup-sync to force index updates.
|
216
|
+
entry = {} if opts[:force_overwrite]
|
217
|
+
|
181
218
|
d = {
|
182
219
|
:message_id => m.id,
|
183
220
|
:source_id => source_id,
|
184
221
|
:source_info => m.source_info,
|
185
|
-
:date => m.date.to_indexable_s,
|
186
|
-
:body => m.
|
187
|
-
:snippet => snippet,
|
188
|
-
:label =>
|
189
|
-
:from => m.from ? m.from.
|
190
|
-
:to => (m.to + m.cc + m.bcc).map { |x| x.
|
191
|
-
:subject => wrap_subj(m.subj),
|
192
|
-
:refs => (m.refs + m.replytos).uniq.join(" "),
|
222
|
+
:date => (entry[:date] || m.date.to_indexable_s),
|
223
|
+
:body => (entry[:body] || m.indexable_content),
|
224
|
+
:snippet => snippet, # always override
|
225
|
+
:label => labels.uniq.join(" "),
|
226
|
+
:from => (entry[:from] || (m.from ? m.from.indexable_content : "")),
|
227
|
+
:to => (entry[:to] || (m.to + m.cc + m.bcc).map { |x| x.indexable_content }.join(" ")),
|
228
|
+
:subject => (entry[:subject] || wrap_subj(Message.normalize_subj(m.subj))),
|
229
|
+
:refs => (entry[:refs] || (m.refs + m.replytos).uniq.join(" ")),
|
193
230
|
}
|
194
231
|
|
195
232
|
@index.delete docid if docid
|
@@ -250,6 +287,7 @@ EOS
|
|
250
287
|
searched = {}
|
251
288
|
num_queries = 0
|
252
289
|
|
290
|
+
pending = [m.id]
|
253
291
|
if $config[:thread_by_subject] # do subject queries
|
254
292
|
date_min = m.date - (SAME_SUBJECT_DATE_LIMIT * 12 * 3600)
|
255
293
|
date_max = m.date + (SAME_SUBJECT_DATE_LIMIT * 12 * 3600)
|
@@ -264,10 +302,13 @@ EOS
|
|
264
302
|
|
265
303
|
q = build_query :qobj => q
|
266
304
|
|
267
|
-
|
268
|
-
Redwood::log "found #{
|
269
|
-
|
270
|
-
|
305
|
+
p1 = @index.search(q).hits.map { |hit| @index[hit.doc][:message_id] }
|
306
|
+
Redwood::log "found #{p1.size} results for subject query #{q}"
|
307
|
+
|
308
|
+
p2 = @index.search(q.to_s, :limit => :all).hits.map { |hit| @index[hit.doc][:message_id] }
|
309
|
+
Redwood::log "found #{p2.size} results in string form"
|
310
|
+
|
311
|
+
pending = (pending + p1 + p2).uniq
|
271
312
|
end
|
272
313
|
|
273
314
|
until pending.empty? || (opts[:limit] && messages.size >= opts[:limit])
|
@@ -396,18 +437,7 @@ protected
|
|
396
437
|
def parse_user_query_string s
|
397
438
|
extraopts = {}
|
398
439
|
|
399
|
-
|
400
|
-
## its api. we parse the user query string with ferret twice: the first
|
401
|
-
## time we just turn the resulting object back into a string, which has
|
402
|
-
## the next effect of transforming the original string into a nice
|
403
|
-
## normalized form with + and - instead of AND, OR, etc. then we do some
|
404
|
-
## string substitutions which depend on this normalized form, re-parse
|
405
|
-
## the string with Ferret, and return the resulting query object.
|
406
|
-
|
407
|
-
norms = @qparser.parse(s).to_s
|
408
|
-
Redwood::log "normalized #{s.inspect} to #{norms.inspect}" unless s == norms
|
409
|
-
|
410
|
-
subs = norms.gsub(/\b(to|from):(\S+)\b/) do
|
440
|
+
subs = s.gsub(/\b(to|from):(\S+)\b/) do
|
411
441
|
field, name = $1, $2
|
412
442
|
if(p = ContactManager.contact_for(name))
|
413
443
|
[field, p.email]
|
@@ -477,7 +507,6 @@ protected
|
|
477
507
|
subs = nil if chronic_failure
|
478
508
|
end
|
479
509
|
|
480
|
-
Redwood::log "translated #{norms.inspect} to #{subs.inspect}" unless subs == norms
|
481
510
|
if subs
|
482
511
|
[@qparser.parse(subs), extraopts]
|
483
512
|
else
|
@@ -512,7 +541,7 @@ protected
|
|
512
541
|
File.chmod 0600, fn
|
513
542
|
FileUtils.mv fn, bakfn, :force => true unless File.exists?(bakfn) && File.size(fn) == 0
|
514
543
|
end
|
515
|
-
Redwood::save_yaml_obj
|
544
|
+
Redwood::save_yaml_obj sources.sort_by { |s| s.id.to_i }, fn, true
|
516
545
|
File.chmod 0600, fn
|
517
546
|
end
|
518
547
|
@sources_dirty = false
|