# HG changeset patch # User martin f. krafft # Date 1334402941 -7200 # Node ID 5806fb74130bd82effdf0a769b7cfe701578fad9 # Parent 1f9ea565862749e3757949289d178e9467a1275c Add note field to Account/Domain and CLI diff -r 1f9ea5658627 -r 5806fb74130b VirtualMailManager/account.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ā€½ diff -r 1f9ea5658627 -r 5806fb74130b VirtualMailManager/cli/subcommands.py --- 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 diff -r 1f9ea5658627 -r 5806fb74130b VirtualMailManager/domain.py --- 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): diff -r 1f9ea5658627 -r 5806fb74130b VirtualMailManager/handler.py --- 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)