Add note field to Account/Domain and CLI v0.6.x
authormartin f. krafft <madduck@madduck.net>
Sat, 14 Apr 2012 13:29:01 +0200
branchv0.6.x
changeset 539 5806fb74130b
parent 538 1f9ea5658627
child 540 a8ee0328f908
Add note field to Account/Domain and CLI
VirtualMailManager/account.py
VirtualMailManager/cli/subcommands.py
VirtualMailManager/domain.py
VirtualMailManager/handler.py
--- a/VirtualMailManager/account.py	Sat Apr 14 12:58:20 2012 +0200
+++ b/VirtualMailManager/account.py	Sat Apr 14 13:29:01 2012 +0200
@@ -32,7 +32,7 @@
 class Account(object):
     """Class to manage e-mail accounts."""
     __slots__ = ('_addr', '_dbh', '_domain', '_mail', '_new', '_passwd',
-                 '_qlimit', '_services', '_transport', '_uid')
+                 '_qlimit', '_services', '_transport', '_note', '_uid')
 
     def __init__(self, dbh, address):
         """Creates a new Account instance.
@@ -63,6 +63,7 @@
         self._qlimit = None
         self._services = None
         self._transport = None
+        self._note = None
         self._passwd = None
         self._new = True
         self._load()
@@ -72,16 +73,16 @@
         return not self._new
 
     def _load(self):
-        """Load 'uid', 'mid', 'qid', 'ssid'  and 'tid' from the database and
-        set _new to `False` - if the user could be found. """
+        """Load 'uid', 'mid', 'qid', 'ssid', 'tid' and 'note' from the
+        database and set _new to `False` - if the user could be found. """
         dbc = self._dbh.cursor()
-        dbc.execute('SELECT uid, mid, qid, ssid, tid FROM users WHERE gid = '
-                    '%s AND local_part = %s', (self._domain.gid,
-                                               self._addr.localpart))
+        dbc.execute('SELECT uid, mid, qid, ssid, tid, note FROM users '
+                    'WHERE gid = %s AND local_part = %s',
+                    (self._domain.gid, self._addr.localpart))
         result = dbc.fetchone()
         dbc.close()
         if result:
-            self._uid, _mid, _qid, _ssid, _tid = result
+            self._uid, _mid, _qid, _ssid, _tid, _note = result
 
             def load_helper(ctor, own, field, dbresult):
                 cur = None if own is None else getattr(own, field)
@@ -96,6 +97,7 @@
             self._transport = load_helper(Transport, self._transport, 'tid',
                                           _tid)
             self._mail = MailLocation(self._dbh, mid=_mid)
+            self._note = _note
             self._new = False
 
     def _set_uid(self):
@@ -193,6 +195,11 @@
         return self._mail
 
     @property
+    def note(self):
+        """The Account's note."""
+        return self._note
+
+    @property
     def uid(self):
         """The Account's unique ID."""
         return self._uid
@@ -216,6 +223,17 @@
                        ACCOUNT_MISSING_PASSWORD)
         self._passwd = password
 
+    def set_note(self, note):
+        """Set the account's (optional) note.
+
+        Argument:
+
+        `note` : basestring or None
+          The note, or None to remove
+        """
+        assert note is None or isinstance(note, basestring)
+        self._note = note
+
     def save(self):
         """Save the new Account in the database."""
         if not self._new:
@@ -228,13 +246,15 @@
                                    directory=cfg_dget('mailbox.root')))
         dbc = self._dbh.cursor()
         dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, '
-                    'qid, ssid, tid) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
+                    'qid, ssid, tid, note) '
+                    'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)',
                     (self._addr.localpart,
                      pwhash(self._passwd, user=self._addr), self._uid,
                      self._domain.gid, self._mail.mid,
                      self._qlimit.qid if self._qlimit else None,
                      self._services.ssid if self._services else None,
-                     self._transport.tid if self._transport else None))
+                     self._transport.tid if self._transport else None,
+                     self._note))
         self._dbh.commit()
         dbc.close()
         self._new = False
@@ -242,16 +262,16 @@
     def modify(self, field, value):
         """Update the Account's *field* to the new *value*.
 
-        Possible values for *field* are: 'name', 'password'.
+        Possible values for *field* are: 'name', 'password', 'note'.
 
         Arguments:
 
         `field` : basestring
-          The attribute name: 'name' or 'password'
+          The attribute name: 'name', 'password' or 'note'
         `value` : basestring
           The new value of the attribute.
         """
-        if field not in ('name', 'password'):
+        if field not in ('name', 'password', 'note'):
             raise AErr(_(u"Unknown field: '%s'") % field, INVALID_ARGUMENT)
         self._chk_state()
         dbc = self._dbh.cursor()
@@ -259,7 +279,7 @@
             dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s',
                         (pwhash(value, user=self._addr), self._uid))
         else:
-            dbc.execute('UPDATE users SET name = %s WHERE uid = %s',
+            dbc.execute('UPDATE users SET %s = %%s WHERE uid = %%s' % field,
                         (value, self._uid))
         if dbc.rowcount > 0:
             self._dbh.commit()
@@ -377,6 +397,7 @@
                 info['ql_messages'] = self._domain.quotalimit.messages
                 info['ql_domaindefault'] = True
             info['transport'] = self._get_info_transport()
+            info['note'] = self._note
             info['uid'] = self._uid
             return info
         # nearly impossibleā€½
--- a/VirtualMailManager/cli/subcommands.py	Sat Apr 14 12:58:20 2012 +0200
+++ b/VirtualMailManager/cli/subcommands.py	Sat Apr 14 13:29:01 2012 +0200
@@ -31,10 +31,11 @@
     'aliasdomain_switch', 'catchall_add', 'catchall_info', 'catchall_delete',
     'config_get', 'config_set', 'configure',
     'domain_add', 'domain_delete',  'domain_info', 'domain_quota',
-    'domain_services', 'domain_transport', 'get_user', 'help_', 'list_domains',
-    'list_pwschemes', 'relocated_add', 'relocated_delete', 'relocated_info',
-    'user_add', 'user_delete', 'user_info', 'user_name', 'user_password',
-    'user_quota', 'user_services', 'user_transport', 'version',
+    'domain_services', 'domain_transport', 'domain_note', 'get_user', 'help_',
+    'list_domains', 'list_pwschemes', 'relocated_add', 'relocated_delete',
+    'relocated_info', 'user_add', 'user_delete', 'user_info', 'user_name',
+    'user_password', 'user_quota', 'user_services', 'user_transport',
+    'user_note', 'version',
 )
 
 _ = lambda msg: msg
@@ -407,6 +408,18 @@
         ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3], force)
 
 
+def domain_note(ctx):
+    """update the note of the given domain"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing domain name.'),
+              ctx.scmd)
+    elif ctx.argc < 4:
+        note = None
+    else:
+        note = ' '.join(ctx.args[3:])
+    ctx.hdlr.domain_note(ctx.args[2].lower(), note)
+
+
 def get_user(ctx):
     """get the address of the user with the given UID"""
     if ctx.argc < 3:
@@ -611,6 +624,18 @@
     ctx.hdlr.user_password(ctx.args[2].lower(), password)
 
 
+def user_note(ctx):
+    """update the note of the given address"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'),
+              ctx.scmd)
+    elif ctx.argc < 4:
+        note = None
+    else:
+        note = ' '.join(ctx.args[3:])
+    ctx.hdlr.user_note(ctx.args[2].lower(), note)
+
+
 def user_quota(ctx):
     """update the quota limit for the given address"""
     if ctx.argc < 3:
@@ -731,6 +756,9 @@
     'usertransport': cmd('usertransport', 'ut', user_transport,
                          'address transport | address default',
                          _(u'update the transport of the given address')),
+    'usernote': cmd('usernote', 'uo', user_note,
+                    'address note',
+                    _(u'update the note of the given address')),
     # Alias commands
     'aliasadd': cmd('aliasadd', 'aa', alias_add, 'address destination ...',
                     _(u'create a new alias e-mail address with one or more '
@@ -780,6 +808,9 @@
     'domaintransport': cmd('domaintransport', 'dt', domain_transport,
                            'fqdn transport [force]',
                            _(u'update the transport of the specified domain')),
+    'domainnote': cmd('domainnote', 'do', domain_note,
+                      'fqdn note',
+                      _(u'update the note of the given domain')),
     'listdomains': cmd('listdomains', 'ld', list_domains, '[pattern]',
                       _(u'list all domains or search for domains by pattern')),
     # Relocated commands
--- a/VirtualMailManager/domain.py	Sat Apr 14 12:58:20 2012 +0200
+++ b/VirtualMailManager/domain.py	Sat Apr 14 13:29:01 2012 +0200
@@ -31,7 +31,7 @@
 class Domain(object):
     """Class to manage e-mail domains."""
     __slots__ = ('_directory', '_gid', '_name', '_qlimit', '_services',
-                 '_transport', '_dbh', '_new')
+                 '_transport', '_note', '_dbh', '_new')
 
     def __init__(self, dbh, domainname):
         """Creates a new Domain instance.
@@ -57,6 +57,7 @@
         self._services = None
         self._transport = None
         self._directory = None
+        self._note = None
         self._new = True
         self._load()
 
@@ -68,7 +69,8 @@
         domain.
         """
         dbc = self._dbh.cursor()
-        dbc.execute('SELECT dd.gid, qid, ssid, tid, domaindir, is_primary '
+        dbc.execute('SELECT dd.gid, qid, ssid, tid, domaindir, is_primary, '
+                    'note '
                     'FROM domain_data dd, domain_name dn WHERE domainname = '
                     '%s AND dn.gid = dd.gid', (self._name,))
         result = dbc.fetchone()
@@ -81,6 +83,7 @@
             self._qlimit = QuotaLimit(self._dbh, qid=result[1])
             self._services = ServiceSet(self._dbh, ssid=result[2])
             self._transport = Transport(self._dbh, tid=result[3])
+            self._note = result[6]
             self._new = False
 
     def _set_gid(self):
@@ -127,7 +130,16 @@
             raise DomErr(_(u"The domain '%s' already exists.") % self._name,
                          DOMAIN_EXISTS)
 
-    def _update_tables(self, column, value, force=False):
+    def _update_tables(self, column, value):
+        """Update table columns in the domain_data table."""
+        dbc = self._dbh.cursor()
+        dbc.execute('UPDATE domain_data SET %s = %%s WHERE gid = %%s' % column,
+                    (value, self._gid))
+        if dbc.rowcount > 0:
+            self._dbh.commit()
+        dbc.close()
+
+    def _update_tables_ref(self, column, value, force=False):
         """Update various columns in the domain_data table. When *force* is
         `True`, the corresponding column in the users table will be reset to
         NULL.
@@ -143,17 +155,14 @@
         """
         if column not in ('qid', 'ssid', 'tid'):
             raise ValueError('Unknown column: %r' % column)
-        dbc = self._dbh.cursor()
-        dbc.execute('UPDATE domain_data SET %s = %%s WHERE gid = %%s' % column,
-                    (value, self._gid))
-        if dbc.rowcount > 0:
-            self._dbh.commit()
+        self._update_tables(column, value)
         if force:
+            dbc = self._dbh.cursor()
             dbc.execute('UPDATE users SET %s = NULL WHERE gid = %%s' % column,
                         (self._gid,))
             if dbc.rowcount > 0:
                 self._dbh.commit()
-        dbc.close()
+            dbc.close()
 
     @property
     def gid(self):
@@ -185,6 +194,11 @@
         """The Domain's transport."""
         return self._transport
 
+    @property
+    def note(self):
+        """The Domain's note."""
+        return self._note
+
     def set_directory(self, basedir):
         """Set the path value of the Domain's directory, inside *basedir*.
 
@@ -235,16 +249,29 @@
         assert isinstance(transport, Transport)
         self._transport = transport
 
+    def set_note(self, note):
+        """Set the domain's (optional) note.
+
+        Argument:
+
+        `note` : basestring or None
+          The note, or None to remove
+        """
+        self._chk_state(False)
+        assert note is None or isinstance(note, basestring)
+        self._note = note
+
     def save(self):
         """Stores the new domain in the database."""
         self._chk_state(False)
         assert all((self._directory, self._qlimit, self._services,
                     self._transport))
         dbc = self._dbh.cursor()
-        dbc.execute('INSERT INTO domain_data (gid, qid, ssid, tid, domaindir) '
-                    'VALUES (%s, %s, %s, %s, %s)', (self._gid,
+        dbc.execute('INSERT INTO domain_data (gid, qid, ssid, tid, domaindir, '
+                    'note) '
+                    'VALUES (%s, %s, %s, %s, %s, %s)', (self._gid,
                     self._qlimit.qid, self._services.ssid, self._transport.tid,
-                    self._directory))
+                    self._directory, self._note))
         dbc.execute('INSERT INTO domain_name (domainname, gid, is_primary) '
                     'VALUES (%s, %s, TRUE)', (self._name, self._gid))
         self._dbh.commit()
@@ -299,7 +326,7 @@
         assert isinstance(quotalimit, QuotaLimit)
         if not force and quotalimit == self._qlimit:
             return
-        self._update_tables('qid', quotalimit.qid, force)
+        self._update_tables_ref('qid', quotalimit.qid, force)
         self._qlimit = quotalimit
 
     def update_serviceset(self, serviceset, force=False):
@@ -319,7 +346,7 @@
         assert isinstance(serviceset, ServiceSet)
         if not force and serviceset == self._services:
             return
-        self._update_tables('ssid', serviceset.ssid, force)
+        self._update_tables_ref('ssid', serviceset.ssid, force)
         self._services = serviceset
 
     def update_transport(self, transport, force=False):
@@ -340,9 +367,24 @@
         assert isinstance(transport, Transport)
         if not force and transport == self._transport:
             return
-        self._update_tables('tid', transport.tid, force)
+        self._update_tables_ref('tid', transport.tid, force)
         self._transport = transport
 
+    def update_note(self, note):
+        """Sets a new note for the Domain.
+
+        Arguments:
+
+        `transport` : basestring or None
+          the new note
+        """
+        self._chk_state()
+        assert note is None or isinstance(note, basestring)
+        if note == self._note:
+            return
+        self._update_tables('note', note)
+        self._note = note
+
     def get_info(self):
         """Returns a dictionary with information about the domain."""
         self._chk_state()
@@ -368,6 +410,7 @@
         else:
             services.append('None')
         info['active services'] = ' '.join(services)
+        info['note'] = self._note
         return info
 
     def get_accounts(self):
--- a/VirtualMailManager/handler.py	Sat Apr 14 12:58:20 2012 +0200
+++ b/VirtualMailManager/handler.py	Sat Apr 14 13:29:01 2012 +0200
@@ -484,17 +484,10 @@
         serviceset = ServiceSet(self._dbh, **kwargs)
         dom.update_serviceset(serviceset, (True, False)[not force])
 
-    def domain_transport(self, domainname, transport, force=None):
+    def domain_note(self, domainname, note):
         """Wrapper around Domain.update_transport()"""
-        if force is not None and force != 'force':
-            raise DomainError(_(u"Invalid argument: '%s'") % force,
-                              INVALID_ARGUMENT)
         dom = self._get_domain(domainname)
-        trsp = Transport(self._dbh, transport=transport)
-        if force is None:
-            dom.update_transport(trsp)
-        else:
-            dom.update_transport(trsp, force=True)
+        dom.update_note(note)
 
     def domain_delete(self, domainname, force=False):
         """Wrapper around Domain.delete()"""
@@ -758,6 +751,14 @@
                            acc.address, NO_SUCH_ACCOUNT)
         acc.modify('name', name)
 
+    def user_note(self, emailaddress, note):
+        """Wrapper for Account.modify('note', ...)."""
+        acc = self._get_account(emailaddress)
+        if not acc:
+            raise VMMError(_(u"The account '%s' does not exist.") %
+                           acc.address, NO_SUCH_ACCOUNT)
+        acc.modify('note', note)
+
     def user_quotalimit(self, emailaddress, bytes_, messages=0):
         """Wrapper for Account.update_quotalimit(QuotaLimit)."""
         acc = self._get_account(emailaddress)