summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--alot/buffers.py3
-rw-r--r--alot/commands/envelope.py59
-rw-r--r--alot/commands/search.py4
-rw-r--r--alot/commands/thread.py7
-rw-r--r--alot/completion.py5
-rw-r--r--docs/source/usage/modes/envelope.rst40
-rw-r--r--tests/commands/envelope_test.py47
8 files changed, 162 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 2b2855d0..11a98bb8 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,6 @@
next:
* Add command to reload configuration files in running session
+* new command "tag" (and friends) in EnvelopeBuffer to add additional tags after sending
0.5:
* save command prompt, recipient and sender history across program restarts
diff --git a/alot/buffers.py b/alot/buffers.py
index 80ec0855..09cf1fe8 100644
--- a/alot/buffers.py
+++ b/alot/buffers.py
@@ -178,6 +178,9 @@ class EnvelopeBuffer(Buffer):
description += key.uids[0].uid
lines.append(('GPG encrypt', description))
+ if self.envelope.tags:
+ lines.append(('Tags', ','.join(self.envelope.tags)))
+
# add header list widget iff header values exists
if lines:
key_att = settings.get_theming_attribute('envelope', 'header_key')
diff --git a/alot/commands/envelope.py b/alot/commands/envelope.py
index 82b87888..a2945fbe 100644
--- a/alot/commands/envelope.py
+++ b/alot/commands/envelope.py
@@ -141,7 +141,7 @@ class SaveCommand(Command):
ui.notify(msg + ' to %s' % path)
logging.debug('adding new mail to index')
try:
- ui.dbman.add_message(path, account.draft_tags)
+ ui.dbman.add_message(path, account.draft_tags + envelope.tags)
ui.apply_command(globals.FlushCommand())
ui.apply_command(commands.globals.BufferCloseCommand())
except DatabaseError as e:
@@ -570,3 +570,60 @@ class EncryptCommand(Command):
envelope.encrypt_keys = {}
# reload buffer
ui.current_buffer.rebuild()
+
+
+@registerCommand(
+ MODE, 'tag', forced={'action': 'add'},
+ arguments=[(['tags'], {'help': 'comma separated list of tags'})],
+ help='add tags to message',
+)
+@registerCommand(
+ MODE, 'retag', forced={'action': 'set'},
+ arguments=[(['tags'], {'help': 'comma separated list of tags'})],
+ help='set message tags.',
+)
+@registerCommand(
+ MODE, 'untag', forced={'action': 'remove'},
+ arguments=[(['tags'], {'help': 'comma separated list of tags'})],
+ help='remove tags from message',
+)
+@registerCommand(
+ MODE, 'toggletags', forced={'action': 'toggle'},
+ arguments=[(['tags'], {'help': 'comma separated list of tags'})],
+ help='flip presence of tags on message',
+)
+class TagCommand(Command):
+
+ """manipulate message tags"""
+ repeatable = True
+
+ def __init__(self, tags=u'', action='add', **kwargs):
+ """
+ :param tags: comma separated list of tagstrings to set
+ :type tags: unicode
+ :param action: adds tags if 'add', removes them if 'remove', adds tags
+ and removes all other if 'set' or toggle individually if
+ 'toggle'
+ :type action: str
+ """
+ assert isinstance(tags, unicode), 'tags should be a unicode string'
+ self.tagsstring = tags
+ self.action = action
+ Command.__init__(self, **kwargs)
+
+ def apply(self, ui):
+ ebuffer = ui.current_buffer
+ envelope = ebuffer.envelope
+ tags = {t for t in self.tagsstring.split(',') if t}
+ old = set(envelope.tags)
+ if self.action == 'add':
+ new = old.union(tags)
+ elif self.action == 'remove':
+ new = old.difference(tags)
+ elif self.action == 'set':
+ new = tags
+ elif self.action == 'toggle':
+ new = old.symmetric_difference(tags)
+ envelope.tags = sorted(new)
+ # reload buffer
+ ui.current_buffer.rebuild()
diff --git a/alot/commands/search.py b/alot/commands/search.py
index f304973d..49d95ebe 100644
--- a/alot/commands/search.py
+++ b/alot/commands/search.py
@@ -170,8 +170,8 @@ class TagCommand(Command):
and removes all other if 'set' or toggle individually if
'toggle'
:type action: str
- :param all: tag all messages in search result
- :type all: bool
+ :param allmessages: tag all messages in search result
+ :type allmessages: bool
:param flush: imediately write out to the index
:type flush: bool
"""
diff --git a/alot/commands/thread.py b/alot/commands/thread.py
index 8f1ae039..ce5da649 100644
--- a/alot/commands/thread.py
+++ b/alot/commands/thread.py
@@ -489,9 +489,14 @@ class EditNewCommand(Command):
if not self.message:
self.message = ui.current_buffer.get_selected_message()
mail = self.message.get_email()
+ # copy most tags to the envelope
+ tags = set(self.message.get_tags())
+ tags.difference_update({'inbox', 'sent', 'draft', 'killed', 'replied',
+ 'signed', 'encrypted', 'unread', 'attachment'})
+ tags = list(tags)
# set body text
mailcontent = self.message.accumulate_body()
- envelope = Envelope(bodytext=mailcontent)
+ envelope = Envelope(bodytext=mailcontent, tags=tags)
# copy selected headers
to_copy = ['Subject', 'From', 'To', 'Cc', 'Bcc', 'In-Reply-To',
diff --git a/alot/completion.py b/alot/completion.py
index 046f4571..69e622eb 100644
--- a/alot/completion.py
+++ b/alot/completion.py
@@ -464,6 +464,11 @@ class CommandCompleter(Completer):
'rmencrypt',
'toggleencrypt']:
res = self._publickeyscompleter.complete(params, localpos)
+ elif self.mode == 'envelope' and cmd in ['tag', 'toggletags',
+ 'untag', 'retag']:
+ localcomp = MultipleSelectionCompleter(self._tagcompleter,
+ separator=',')
+ res = localcomp.complete(params, localpos)
# thread
elif self.mode == 'thread' and cmd == 'save':
res = self._pathcompleter.complete(params, localpos)
diff --git a/docs/source/usage/modes/envelope.rst b/docs/source/usage/modes/envelope.rst
index 896bc7d0..8644a6c1 100644
--- a/docs/source/usage/modes/envelope.rst
+++ b/docs/source/usage/modes/envelope.rst
@@ -65,6 +65,26 @@ The following commands are available in envelope mode
:---spawn: spawn editor in new terminal.
:---refocus: refocus envelope after editing (Defaults to: 'True').
+.. _cmd.envelope.retag:
+
+.. describe:: retag
+
+ set message tags.
+
+ argument
+ comma separated list of tags
+
+
+.. _cmd.envelope.tag:
+
+.. describe:: tag
+
+ add tags to message
+
+ argument
+ comma separated list of tags
+
+
.. _cmd.envelope.send:
.. describe:: send
@@ -82,6 +102,16 @@ The following commands are available in envelope mode
which key id to use
+.. _cmd.envelope.untag:
+
+.. describe:: untag
+
+ remove tags from message
+
+ argument
+ comma separated list of tags
+
+
.. _cmd.envelope.attach:
.. describe:: attach
@@ -148,6 +178,16 @@ The following commands are available in envelope mode
mark mail not to be signed before sending
+.. _cmd.envelope.toggletags:
+
+.. describe:: toggletags
+
+ flip presence of tags on message
+
+ argument
+ comma separated list of tags
+
+
.. _cmd.envelope.unset:
.. describe:: unset
diff --git a/tests/commands/envelope_test.py b/tests/commands/envelope_test.py
index cd6684a8..67caef02 100644
--- a/tests/commands/envelope_test.py
+++ b/tests/commands/envelope_test.py
@@ -26,6 +26,7 @@ import unittest
import mock
from alot.commands import envelope
+from alot.db.envelope import Envelope
# When using an assert from a mock a TestCase method might not use self. That's
# okay.
@@ -110,3 +111,49 @@ class TestAttachCommand(unittest.TestCase):
cmd = envelope.AttachCommand(path=os.path.join(d, 'doesnt-exist'))
cmd.apply(ui)
ui.notify.assert_called()
+
+
+class TestTagCommands(unittest.TestCase):
+
+ def _test(self, tagstring, action, expected):
+ """Common steps for envelope.TagCommand tests
+
+ :param tagstring: the string to pass to the TagCommand
+ :type tagstring: str
+ :param action: the action to pass to the TagCommand
+ :type action: str
+ :param expected: the expected output to assert in the test
+ :type expected: list(str)
+ """
+ env = Envelope(tags=['one', 'two', 'three'])
+ ui = mock.Mock()
+ ui.current_buffer = mock.Mock()
+ ui.current_buffer.envelope = env
+ cmd = envelope.TagCommand(tags=tagstring, action=action)
+ cmd.apply(ui)
+ actual = env.tags
+ self.assertListEqual(sorted(actual), sorted(expected))
+
+ def test_add_new_tags(self):
+ self._test(u'four', 'add', ['one', 'two', 'three', 'four'])
+
+ def test_adding_existing_tags_has_no_effect(self):
+ self._test(u'one', 'add', ['one', 'two', 'three'])
+
+ def test_remove_existing_tags(self):
+ self._test(u'one', 'remove', ['two', 'three'])
+
+ def test_remove_non_existing_tags_has_no_effect(self):
+ self._test(u'four', 'remove', ['one', 'two', 'three'])
+
+ def test_set_tags(self):
+ self._test(u'a,b,c', 'set', ['a', 'b', 'c'])
+
+ def test_toggle_will_remove_existing_tags(self):
+ self._test(u'one', 'toggle', ['two', 'three'])
+
+ def test_toggle_will_add_new_tags(self):
+ self._test(u'four', 'toggle', ['one', 'two', 'three', 'four'])
+
+ def test_toggle_can_remove_and_add_in_one_run(self):
+ self._test(u'one,four', 'toggle', ['two', 'three', 'four'])