Commits

kanu_orz  committed fe28067 Draft

replace AccountManager from 0.11branch v0.3(r10429)

  • Participants
  • Parent commits f80425f
  • Tags 3.1.2beta2

Comments (0)

Files changed (25)

File acct_mgr/README.hashes

+== HowTo Verify Sources ==
+
+Starting with acct_mgr-0.3 the origin of release sources can be verified.
+If you care, and you really should, you can verify recorded md5 and sha1
+hashes against your copy (fresh checkout) of the TracAccountManager sources:
+
+Call `python ./contrib/signatures.py` from the top directory containing
+the *sums hash files and you should simply get back `Check passed.`
+
+Additional files are reported but ignored. The hash files are signed with
+maintainers public OpenPGP key for verification as well, i.e. as following:
+{{{
+gpg --verify ./acct_mgr-md5sums.sig
+gpg --verify ./acct_mgr-sha1sums.sig
+}}}
+provided you've downloaded and imported maintainers public key before.

File acct_mgr/acct_mgr-md5sums

+a1c73f10271f52154642e88fbdc8b7f7 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/htfile.py
+4706c9dec12a8319abc1a085ac6058f4 /data/trac/th_devel/acct_mgr_build/acct_mgr/guard.py
+f938ffa58aaa39bd8bd2f401c1b5e8cc /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/.placeholder
+17a9edd785c5d7b807a6380515cc7779 /data/trac/th_devel/acct_mgr_build/acct_mgr/svnserve.py
+13ee151b7309b8c96aebf68d62f69ed0 /data/trac/th_devel/acct_mgr_build/acct_mgr/web_ui.py
+7c15faa40a56b626d3800d5eb499fea3 /data/trac/th_devel/acct_mgr_build/contrib/sessionstore_convert.py
+a3cee8fdfc8eae33385f27737cb8a513 /data/trac/th_devel/acct_mgr_build/acct_mgr/util.py
+4f6fa961174acf9527f918f25ca8e5af /data/trac/th_devel/acct_mgr_build/setup.cfg
+c9ef45681efefb313e5bacd088cf1322 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/verify_email.txt
+01b4ae9b5297921b4ef96e7fa52dba8d /data/trac/th_devel/acct_mgr_build/README.hashes
+16b1087e20b7a35c070c3d53ef04e66a /data/trac/th_devel/acct_mgr_build/acct_mgr/htdocs/acct_mgr.css
+fc4f4ce0daf89d03510600f1a1cd5c00 /data/trac/th_devel/acct_mgr_build/acct_mgr/md5crypt.py
+dae1127195c2dcc63eaf27dac3b7ef42 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/fr/LC_MESSAGES/acct_mgr.po
+c8f5183f05d06e9cd746281ba597ab0d /data/trac/th_devel/acct_mgr_build/contrib/signatures.py
+9ac0388c55fe687759e7a7c0e29d5cbf /data/trac/th_devel/acct_mgr_build/acct_mgr/admin.py
+548c47106b3eb60ea6b2105827d11905 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/__init__.py
+423b8efec0be40962b9e2189f27f04b4 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/cs/LC_MESSAGES/acct_mgr.po
+275e57082b5ecf9ba47d946300b86ab6 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/ja/LC_MESSAGES/acct_mgr.po
+f5dbcb0b4fde1dda7edb5964da037ec1 /data/trac/th_devel/acct_mgr_build/acct_mgr/htdocs/time-locked.png
+594bb079ce478aad302888d21d965d60 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/messages.pot
+180fe8665db307320593e99b5c0e23bb /data/trac/th_devel/acct_mgr_build/acct_mgr/db.py
+7ba0adaf42999eb55e806d1a6e62dd15 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/testcases.py
+952c79b883fe6c8cf845f8a045f25887 /data/trac/th_devel/acct_mgr_build/acct_mgr/hashlib_compat.py
+b8422abe2bbcf281db17359dcd803270 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/tester.py
+99acb02b0d8ff7968e8f40f721f20828 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/es/LC_MESSAGES/acct_mgr.po
+86a3982c6a460aa7af2d7751a08ce117 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/verify_email.html
+2b405a856ca93482a29ac1768f1ed8df /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/nl/LC_MESSAGES/acct_mgr.po
+6dcaa767377049170d259577e392efa6 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/ru/LC_MESSAGES/acct_mgr.po
+a6b47d6b847dac2eb454e43b0d959018 /data/trac/th_devel/acct_mgr_build/acct_mgr/notification.py
+6e4de4f35bdcc9da281a7e99ee13afd0 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/db.py
+1fbce6790724050f30e03e78fc138fef /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/user_changes_email.txt
+5c2b90a26aac975e1fb14e8846db3ed9 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/it/LC_MESSAGES/acct_mgr.po
+7be32664b8f9016041c29d11e483ae64 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/prefs_account.html
+609f28dc323e901c04e1efe80c22b5f1 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/admin_accountsconfig.html
+8e9d8b002817e71f75a07d7403031c50 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/login.html
+f7d9848c659ee543c380cd624652582f /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/account_details.html
+894be80462f73dfaa3bddd114bf8211b /data/trac/th_devel/acct_mgr_build/acct_mgr/htdocs/locked.png
+80d33a38a41a4c55dd6c1bdfcba0139f /data/trac/th_devel/acct_mgr_build/contrib/style.css
+866abfd25e4bc0a591e457193497e86a /data/trac/th_devel/acct_mgr_build/README
+d41d8cd98f00b204e9800998ecf8427e /data/trac/th_devel/acct_mgr_build/acct_mgr/__init__.py
+9b15fd4fcb184b1e8bc0e1280f31f847 /data/trac/th_devel/acct_mgr_build/acct_mgr/pwhash.py
+845c27d0495176c4bf0e901c81c874ae /data/trac/th_devel/acct_mgr_build/acct_mgr/api.py
+fad19e9d27595300bff869730e8f01f0 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/testenv.py
+0bd248f9ffbab67bfacbb57521f2d219 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/admin_accountsnotification.html
+d51ccb147ca10e5c2fd3e76f45b6a79e /data/trac/th_devel/acct_mgr_build/setup.py
+751a75f9e223074c188c828643eeb81a /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/register.html
+2090b58e87ba577c88fcbf1b6440fc84 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/reset_password_email.txt
+9451eab60dc400d73f41e57b91b1f441 /data/trac/th_devel/acct_mgr_build/acct_mgr/http.py
+ca1a1029caa578049e88354f3aa61a54 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/reset_password.html
+17f3260e511a542f5649ffa26fa6f05d /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/smtpd.py
+c4a2b8dd3460a1ce8926a8e7e5f258f0 /data/trac/th_devel/acct_mgr_build/acct_mgr/htfile.py
+5217ccaa236f3ed2c29b84c256240e03 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/de/LC_MESSAGES/acct_mgr.po
+321b2a2050ad0745085c448a765bb7fc /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/__init__.py
+1233ff1bbdce74f62ee3b4fa66ae5811 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/admin_users.html
+5a32e33d2ff224053a4babcaa48c0381 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/sv/LC_MESSAGES/acct_mgr.po
+4049599fd06891b9b38aef5e5e904309 /data/trac/th_devel/acct_mgr_build/changelog

File acct_mgr/acct_mgr-md5sums.sig

Binary file added.

File acct_mgr/acct_mgr-sha1sums

+04d310cecf5f465b79d0744d28f497b84547c53d /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/htfile.py
+bc77d6eb06a37bf20b3bc1a8734310301828dac2 /data/trac/th_devel/acct_mgr_build/acct_mgr/guard.py
+74c3cb21dfbf36f606be84041ff55c5be25aa9f5 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/.placeholder
+a711256816a71507cf028aeb7003348a898e80ae /data/trac/th_devel/acct_mgr_build/acct_mgr/svnserve.py
+7a0c11ea182467d809d9353a19354bcda771118a /data/trac/th_devel/acct_mgr_build/acct_mgr/web_ui.py
+264d930c5893f6d8d04d14b797a570ecbda1ccd9 /data/trac/th_devel/acct_mgr_build/contrib/sessionstore_convert.py
+acd4bfe5fc99b2b2d23377f0dd197a5fda397881 /data/trac/th_devel/acct_mgr_build/acct_mgr/util.py
+72c60d3102f50328c85f71fee35b6952a1149858 /data/trac/th_devel/acct_mgr_build/setup.cfg
+005dcc9438e6db7450541b74e8f9cb9a4419bd0e /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/verify_email.txt
+cfc406f6141694bfbd89a9895dd70d1adc9a58e6 /data/trac/th_devel/acct_mgr_build/README.hashes
+324c0d8c45827f1990977db6a28992fd0308f058 /data/trac/th_devel/acct_mgr_build/acct_mgr/htdocs/acct_mgr.css
+5302a28bb7f1cfc3af12de37a4b8af1f7790bb52 /data/trac/th_devel/acct_mgr_build/acct_mgr/md5crypt.py
+f0d6e7be302000d8db071cf976e4dfa25be3e8c3 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/fr/LC_MESSAGES/acct_mgr.po
+83e3f79db9fb4f1b7681325019662bce0f791e35 /data/trac/th_devel/acct_mgr_build/contrib/signatures.py
+bfe790d4d3f919bcf902999585a1e2205712ab56 /data/trac/th_devel/acct_mgr_build/acct_mgr/admin.py
+22230d96a66f68fd36598d23e163c6aa037d26fc /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/__init__.py
+b3e8332bee67a7c73d066e8776bc93e8ca6a1866 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/cs/LC_MESSAGES/acct_mgr.po
+d48bfb869ebd699e5f8dc2d83dfcb53899d46b0a /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/ja/LC_MESSAGES/acct_mgr.po
+106b764a92f61487f3e29479239077a5aa4e1725 /data/trac/th_devel/acct_mgr_build/acct_mgr/htdocs/time-locked.png
+c0e9e1c8feab01608fba7d4187f65f8f1e836902 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/messages.pot
+c827a37ae4ee6c9b67f74b21724c2b9dc11c978f /data/trac/th_devel/acct_mgr_build/acct_mgr/db.py
+d49122166162615f3bcf04b652f59e4690783bd4 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/testcases.py
+53949716003d7fc1f26b248e71c42b98ec6c18be /data/trac/th_devel/acct_mgr_build/acct_mgr/hashlib_compat.py
+77fa9e211c7e81e121f09fd470b624125906a22d /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/tester.py
+d083c62eb0306d1255325d3cfb2cb7572cd97e6a /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/es/LC_MESSAGES/acct_mgr.po
+6ce864148e75b606f83a59d3873169cfb38dc20a /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/verify_email.html
+74c4bdec1c5e70d01fe0c37ad553016d8f89b2bb /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/nl/LC_MESSAGES/acct_mgr.po
+9a9e21f7379cca05db06c1a07c57e6f0dc7a522b /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/ru/LC_MESSAGES/acct_mgr.po
+7515c53e568bb7de08538e160d8243952383cc94 /data/trac/th_devel/acct_mgr_build/acct_mgr/notification.py
+9e29cdbb8785b03b150ac557fef677eb7effb4d3 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/db.py
+ac92fd4c593bedbe1684c9e97c9fb3f9a0045a88 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/user_changes_email.txt
+cae89a98e8ddab340c04c9560bf7c46c3c2b9d16 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/it/LC_MESSAGES/acct_mgr.po
+959be704a76dfc6ff8780cca79d9cbb797871d58 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/prefs_account.html
+fafe73ba998c8fecaed9f321a9a4cac24cb1fb8e /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/admin_accountsconfig.html
+6047dd09e13ad891dc6eca7b56743a22bd1f4bf3 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/login.html
+4e802dde34a274f443c3790a329412ec07e74589 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/account_details.html
+29a8dd6869bd28db94ba45a59b8e3c10309ec4d1 /data/trac/th_devel/acct_mgr_build/acct_mgr/htdocs/locked.png
+57a21d4e1a5675859968ab20de64c91f0aaa8b88 /data/trac/th_devel/acct_mgr_build/contrib/style.css
+00ed1ee763bf5c0286870816a984a05a071555e2 /data/trac/th_devel/acct_mgr_build/README
+da39a3ee5e6b4b0d3255bfef95601890afd80709 /data/trac/th_devel/acct_mgr_build/acct_mgr/__init__.py
+156adeaef47bc163a4308e90bb82dd69c7bda547 /data/trac/th_devel/acct_mgr_build/acct_mgr/pwhash.py
+13e75ecbba10c83c618b89d2d04dfd94eeda503c /data/trac/th_devel/acct_mgr_build/acct_mgr/api.py
+18c66677654a4e2bfb3e0ef4943af4d0c5752fcb /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/testenv.py
+eb3095d21942fceed465f1187bafe71a96b88581 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/admin_accountsnotification.html
+c4584e8ea900fde5f00fccebb741fe424fca1f9e /data/trac/th_devel/acct_mgr_build/setup.py
+c5a954b93a978806b7b2c11db842ad565b8bde00 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/register.html
+f0438a6ba40376c45cea84c8c88da30b86540d75 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/reset_password_email.txt
+9f941c14d71c3ea2bcbbb264b17f0688623ebaad /data/trac/th_devel/acct_mgr_build/acct_mgr/http.py
+091764423fe5866423ae79a53b2b158a54c20b3a /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/reset_password.html
+d7c0d5edfb441245eeba0409b300ff4278cc361f /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/functional/smtpd.py
+4faba54ab1c28f4c43061cb891f5bb1e7d73ee94 /data/trac/th_devel/acct_mgr_build/acct_mgr/htfile.py
+d93b9ecd2cfbfeece2281d323f170123277d3e5e /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/de/LC_MESSAGES/acct_mgr.po
+0f3fbb8014b63336272a85ddf74b2dcafa4ca720 /data/trac/th_devel/acct_mgr_build/acct_mgr/tests/__init__.py
+36225d4887ee71b5aab803fb0f8d13ea9fb3f258 /data/trac/th_devel/acct_mgr_build/acct_mgr/templates/admin_users.html
+4a5d3fcc9021f873ea4d92f172c2aa4676db07a3 /data/trac/th_devel/acct_mgr_build/acct_mgr/locale/sv/LC_MESSAGES/acct_mgr.po
+656d6d2848dff5ab874c274952f8416a02ceafe9 /data/trac/th_devel/acct_mgr_build/changelog

File acct_mgr/acct_mgr-sha1sums.sig

Binary file added.

File acct_mgr/acct_mgr/admin.py

-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2005 Matthew Good <trac@matt-good.net>
-#
-# "THE BEER-WARE LICENSE" (Revision 42):
-# <trac@matt-good.net> wrote this file.  As long as you retain this notice you
-# can do whatever you want with this stuff. If we meet some day, and you think
-# this stuff is worth it, you can buy me a beer in return.   Matthew Good
-#
-# Author: Matthew Good <trac@matt-good.net>
-
-import inspect
-
-from genshi.builder     import tag
-from genshi.core        import Markup
-from pkg_resources      import resource_filename
-
-from trac.core          import *
-from trac.config        import Option
-from trac.perm          import IPermissionRequestor, PermissionSystem
-from trac.util.datefmt  import format_datetime, to_datetime
-from trac.web.chrome    import ITemplateProvider, add_notice, \
-                               add_stylesheet, add_warning
-from trac.admin         import IAdminPanelProvider
-
-from acct_mgr.api       import _, gettext, tag_, AccountManager, \
-                               set_user_attribute
-from acct_mgr.guard     import AccountGuard
-from acct_mgr.web_ui    import _create_user, AccountModule, \
-                               EmailVerificationModule
-from acct_mgr.util      import is_enabled
-
-
-def _getoptions(cls):
-    if isinstance(cls, Component):
-        cls = cls.__class__
-    return [(name, value) for name, value in inspect.getmembers(cls)
-            if isinstance(value, Option)]
-
-def _setorder(req, stores):
-    """Pull the password store ordering out of the req object"""
-    for store in stores.get_all_stores():
-        stores[store] = int(req.args.get(store.__class__.__name__, 0))
-        continue
-
-
-class StoreOrder(dict):
-    """Keeps the order of the Password Stores"""
-
-    instance = 0
-
-    def __init__(self, d={}, stores=[], list=[]):
-        self.instance += 1
-        self.d = {}
-        self.sxref = {}
-        for store in stores:
-            self.d[store] = 0
-            self[0] = store
-            self.sxref[store.__class__.__name__] = store
-            continue
-        for i, s in enumerate(list):
-            self.d[s] = i + 1
-            self[i + 1] = s
-
-    def __getitem__(self, key):
-        """Lookup a store in the list"""
-        return self.d[key]
-
-    def __setitem__(self, key, value):
-        if isinstance(key, Component):
-            order = self.d[key]
-            self.d[key] = value
-            self.d[order].remove(key)
-            self[value] = key
-        elif isinstance(key, basestring):
-            self.d[self.sxref[key]] = value
-        elif isinstance(key, int):
-            self.d.setdefault(key, [])
-            self.d[key].append(value)
-        else:
-            raise KeyError(_("Invalid key type (%s) for StoreOrder")
-                             % str(type(key)))
-        pass
-
-    def get_enabled_stores(self):
-        """Return an ordered list of password stores
-
-        All stores that are order 0 are dropped from the list.
-        """
-        keys = [k for k in self.d.keys() if isinstance(k, int)]
-        keys.sort()
-        storelist = []
-        for k in keys[1:]:
-            storelist.extend(self.d[k])
-            continue
-        return storelist
-
-    def get_enabled_store_names(self):
-        """Returns the class names of the enabled password stores"""
-        stores = self.get_enabled_stores()
-        return [s.__class__.__name__ for s in stores]
-
-    def get_all_stores(self):
-        return [k for k in self.d.keys() if isinstance(k, Component)]
-
-    def numstores(self):
-        return len(self.get_all_stores())
-
-
-class AccountManagerAdminPages(Component):
-
-    implements(IPermissionRequestor, IAdminPanelProvider, ITemplateProvider)
-
-    def __init__(self):
-        self.acctmgr = AccountManager(self.env)
-        self.guard = AccountGuard(self.env)
-
-    # IPermissionRequestor
-    def get_permission_actions(self):
-        action = ['ACCTMGR_CONFIG_ADMIN', 'ACCTMGR_USER_ADMIN']
-        actions = [('ACCTMGR_ADMIN', action), action[0], action[1],]
-        return actions
-
-    # IAdminPanelProvider
-    def get_admin_panels(self, req):
-        if req.perm.has_permission('ACCTMGR_CONFIG_ADMIN'):
-            yield ('accounts', _("Accounts"), 'config', _("Configuration"))
-        if req.perm.has_permission('ACCTMGR_USER_ADMIN'):
-            yield ('accounts', _("Accounts"), 'users', _("Users"))
-            yield ('accounts', _("Accounts"), 'details', _("Account details"))
-
-    def render_admin_panel(self, req, cat, page, path_info):
-        if page == 'config':
-            return self._do_config(req)
-        elif page == 'users':
-            return self._do_users(req)
-        elif page == 'details':
-            return self._do_acct_details(req)
-
-    def _do_config(self, req):
-        stores = StoreOrder(stores=self.acctmgr.stores,
-                            list=self.acctmgr.password_store)
-        if req.method == 'POST':
-            _setorder(req, stores)
-            self.config.set('account-manager', 'password_store',
-                            ','.join(stores.get_enabled_store_names()))
-            for store in stores.get_all_stores():
-                for attr, option in _getoptions(store):
-                    cls_name = store.__class__.__name__
-                    newvalue = req.args.get('%s.%s' % (cls_name, attr))
-                    self.log.debug("%s.%s: %s" % (cls_name, attr, newvalue))
-                    if newvalue is not None:
-                        self.config.set(option.section, option.name, newvalue)
-                        self.config.save()
-            self.config.set('account-manager', 'force_passwd_change',
-                            req.args.get('force_passwd_change', False))
-            self.config.set('account-manager', 'persistent_sessions',
-                            req.args.get('persistent_sessions', False))
-            self.config.set('account-manager', 'verify_email',
-                            req.args.get('verify_email', False))
-            self.config.set('account-manager', 'refresh_passwd',
-                            req.args.get('refresh_passwd', False))
-            self.config.save()
-        sections = []
-        for store in self.acctmgr.stores:
-            if store.__class__.__name__ == "ResetPwStore":
-                # Exclude special store, that is used strictly internally and
-                # inherits configuration from SessionStore anyway.
-                continue
-            options = []
-            for attr, option in _getoptions(store):
-                opt_val = option.__get__(store, store)
-                opt_val = isinstance(opt_val, Component) and \
-                          opt_val.__class__.__name__ or opt_val
-                options.append(
-                            {'label': attr,
-                            'name': '%s.%s' % (store.__class__.__name__, attr),
-                            'value': opt_val,
-                            'doc': gettext(option.__doc__)
-                            })
-                continue
-            sections.append(
-                        {'name': store.__class__.__name__,
-                        'classname': store.__class__.__name__,
-                        'order': stores[store],
-                        'options' : options,
-                        })
-            continue
-        sections = sorted(sections, key=lambda i: i['name'])
-        numstores = range(0, stores.numstores() + 1)
-        data = {
-            'sections': sections,
-            'numstores': numstores,
-            'force_passwd_change': self.acctmgr.force_passwd_change,
-            'persistent_sessions': self.acctmgr.persistent_sessions,
-            'verify_email': self.acctmgr.verify_email,
-            'refresh_passwd': self.acctmgr.refresh_passwd,
-            }
-        return 'admin_accountsconfig.html', data
-
-    def _do_users(self, req):
-        env = self.env
-        perm = PermissionSystem(env)
-        acctmgr = self.acctmgr
-        acctmod = AccountModule(env)
-        guard = self.guard
-        listing_enabled = acctmgr.supports('get_users')
-        create_enabled = acctmgr.supports('set_password')
-        password_change_enabled = acctmgr.supports('set_password')
-        password_reset_enabled = acctmod.reset_password_enabled
-        delete_enabled = acctmgr.supports('delete_user')
-
-        data = {
-            'listing_enabled': listing_enabled,
-            'create_enabled': create_enabled,
-            'delete_enabled': delete_enabled,
-            'password_change_enabled': password_change_enabled,
-            'password_reset_enabled': password_reset_enabled,
-            'account' : { 'username' : None,
-                          'name' : None,
-                          'email' : None,
-                        }
-        }
-
-        if req.method == 'POST':
-            if req.args.get('add'):
-                if create_enabled:
-                    try:
-                        _create_user(req, env, check_permissions=False)
-                    except TracError, e:
-                        data['editor_error'] = e.message
-                        data['account'] = getattr(e, 'account', '')
-                else:
-                    data['editor_error'] = _(
-                        "The password store does not support creating users.")
-            elif req.args.get('reset') and req.args.get('sel'):
-                if password_reset_enabled:
-                    sel = req.args.get('sel')
-                    sel = isinstance(sel, list) and sel or [sel]
-                    for username, name, email in env.get_known_users():
-                        if username in sel:
-                            acctmod._reset_password(username, email)
-                else:
-                    data['deletion_error'] = _(
-                        "The password reset procedure is not enabled.")
-            elif req.args.get('remove') and req.args.get('sel'):
-                if delete_enabled:
-                    sel = req.args.get('sel')
-                    sel = isinstance(sel, list) and sel or [sel]
-                    for account in sel:
-                        acctmgr.delete_user(account)
-                else:
-                    data['deletion_error'] = _(
-                        "The password store does not support deleting users.")
-            elif req.args.get('change'):
-                attributes = {
-                    'email': _("Email Address"),
-                    'name': _("Pre-/Surname (Nickname)"),
-                    'password': _("Password")
-                    }
-                data['success'] = []
-                error = TracError('')
-                username = acctmgr.handle_username_casing(
-                                   req.args.get('username').strip())
-                try:
-                    if not username:
-                        error.account = {'username' : username}
-                        error.message = _("Username cannot be empty.")
-                        raise error
-
-                    if not acctmgr.has_user(username):
-                        error.account = {'username' : username}
-                        error.message = _("Unknown user %(user)s.",
-                                          user=username)
-                        raise error
-
-                    password = req.args.get('password')
-                    if password and (password.strip() != ''):
-                        if password_change_enabled:
-                            if password != req.args.get('password_confirm'):
-                                error.message = _("The passwords must match.")
-                                raise error
-                            acctmgr.set_password(username, password)
-                            data['success'].append(attributes.get('password'))
-                        else:
-                            data['editor_error'] = _(
-                                """The password store does not support
-                                changing passwords.
-                                """)
-                    for attribute in ('name', 'email'):
-                        value = req.args.get(attribute).strip()
-                        if value:
-                            set_user_attribute(env, username,
-                                               attribute, value)
-                            data['success'].append(attributes.get(attribute))
-                except TracError, e:
-                    data['editor_error'] = e.message
-                    data['account'] = getattr(e, 'account', '')
-
-        if listing_enabled:
-            accounts = {}
-            for username in acctmgr.get_users():
-                accounts[username] = {'username': username}
-                if guard.user_locked(username):
-                    accounts[username]['locked'] = True
-                    url = req.href.admin('accounts', 'details', user=username)
-                    accounts[username]['review_url'] = url
-                    t_lock = guard.lock_time(username)
-                    if t_lock > 0:
-                        t_release = guard.pretty_release_time(req, username)
-                        accounts[username]['release_hint'] = _(
-                            "Locked until %(t_release)s",
-                            t_release=t_release)
-
-            for username, name, email in env.get_known_users():
-                account = accounts.get(username)
-                if account:
-                    account['name'] = name
-                    account['email'] = email
-
-            cursor = acctmgr.last_seen()
-            for username, last_visit in cursor:
-                account = accounts.get(username)
-                if account and last_visit:
-                    account['last_visit'] = format_datetime(last_visit, 
-                                                            tzinfo=req.tz)
-            data['accounts'] = sorted(accounts.itervalues(),
-                                      key=lambda acct: acct['username'])
-        add_stylesheet(req, 'acct_mgr/acct_mgr.css')
-        return 'admin_users.html', data
-
-    def _do_acct_details(self, req):
-        username = req.args.get('user')
-        if not username:
-            # Accessing user account details directly is not useful,
-            # so we revert such request immediately. 
-            add_warning(req, Markup(tag.span(tag_(
-                "Please choose account by username from list to proceed."
-                ))))
-            req.redirect(req.href.admin('accounts', 'users'))
-
-        acctmgr = self.acctmgr
-        guard = self.guard
-
-        if req.method == 'POST':
-            if req.args.get('update'):
-                req.redirect(req.href.admin('accounts', 'details',
-                                            user=username))
-            if req.args.get('delete') or req.args.get('release'):
-                # delete failed login attempts, evaluating attempts count
-                if guard.failed_count(username, reset=True) > 0:
-                    add_notice(req, Markup(tag.span(tag_(
-                        "Failed login attempts for user %(user)s deleted",
-                        user=tag.b(username)
-                        ))))
-            req.redirect(req.href.admin('accounts', 'users'))
-
-        data = {'user': username,}
-        stores = StoreOrder(stores=acctmgr.stores,
-                            list=acctmgr.password_store)
-        user_store = acctmgr.find_user_store(username)
-        if not user_store is None:
-            data['user_store'] = user_store.__class__.__name__
-            data['store_order_num'] = stores[user_store]
-        data['ignore_auth_case'] = \
-            self.config.getbool('trac', 'ignore_auth_case')
-
-        for username_, name, email in self.env.get_known_users():
-            if username_ == username:
-                data['name'] = name
-                if email:
-                    data['email'] = email
-                break
-        ts_seen = None
-        for row in acctmgr.last_seen(username):
-            if row[0] == username and row[1]:
-                data['last_visit'] = format_datetime(row[1], tzinfo=req.tz)
-                break
-
-        attempts = []
-        attempts_count = guard.failed_count(username, reset = None)
-        if attempts_count > 0:
-            for attempt in guard.get_failed_log(username):
-                t = format_datetime(to_datetime(
-                                         attempt['time']), tzinfo=req.tz)
-                attempts.append({'ipnr': attempt['ipnr'], 'time': t})
-        data['attempts'] = attempts
-        data['attempts_count'] = attempts_count
-        data['pretty_lock_time'] = guard.pretty_lock_time(username, next=True)
-        data['lock_count'] = guard.lock_count(username)
-        if guard.user_locked(username) is True:
-            data['user_locked'] = True
-            data['release_time'] = guard.pretty_release_time(req, username)
-
-        if is_enabled(self.env, EmailVerificationModule) and \
-                acctmgr.verify_email is True:
-            data['verification'] = 'enabled'
-            data['email_verified'] = acctmgr.email_verified(username, email)
-            self.log.debug('AcctMgr:admin:_do_acct_details for user \"' + \
-                username + '\", email \"' + str(email) + '\": ' + \
-                str(data['email_verified']))
-
-        add_stylesheet(req, 'acct_mgr/acct_mgr.css')
-        #req.href.admin('accounts', 'details', user=username)
-        return 'account_details.html', data
-
-    # ITemplateProvider methods
-
-    def get_htdocs_dirs(self):
-        """Return the absolute path of a directory containing additional
-        static resources (such as images, style sheets, etc).
-        """
-        return [('acct_mgr', resource_filename(__name__, 'htdocs'))]
-
-    def get_templates_dirs(self):
-        """Return the absolute path of the directory containing the provided
-        Genshi templates.
-        """
-        return [resource_filename(__name__, 'templates')]
-
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2005 Matthew Good <trac@matt-good.net>
+#
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <trac@matt-good.net> wrote this file.  As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return.   Matthew Good
+#
+# Author: Matthew Good <trac@matt-good.net>
+
+import inspect
+
+from genshi.builder     import tag
+from genshi.core        import Markup
+from pkg_resources      import resource_filename
+
+from trac.core          import *
+from trac.config        import Option
+from trac.perm          import IPermissionRequestor, PermissionSystem
+from trac.util.datefmt  import format_datetime, to_datetime
+from trac.web.chrome    import ITemplateProvider, add_notice, \
+                               add_stylesheet, add_warning
+from trac.admin         import IAdminPanelProvider
+
+from acct_mgr.api       import AccountManager, _, dgettext, gettext, tag_, \
+                               set_user_attribute
+from acct_mgr.guard     import AccountGuard
+from acct_mgr.web_ui    import _create_user, AccountModule, \
+                               EmailVerificationModule
+from acct_mgr.util      import is_enabled
+
+
+def _getoptions(cls):
+    if isinstance(cls, Component):
+        cls = cls.__class__
+    return [(name, value) for name, value in inspect.getmembers(cls)
+            if isinstance(value, Option)]
+
+def _setorder(req, stores):
+    """Pull the password store ordering out of the req object"""
+    for store in stores.get_all_stores():
+        stores[store] = int(req.args.get(store.__class__.__name__, 0))
+        continue
+
+
+class StoreOrder(dict):
+    """Keeps the order of the Password Stores"""
+
+    instance = 0
+
+    def __init__(self, d={}, stores=[], list=[]):
+        self.instance += 1
+        self.d = {}
+        self.sxref = {}
+        for store in stores:
+            self.d[store] = 0
+            self[0] = store
+            self.sxref[store.__class__.__name__] = store
+            continue
+        for i, s in enumerate(list):
+            self.d[s] = i + 1
+            self[i + 1] = s
+
+    def __getitem__(self, key):
+        """Lookup a store in the list"""
+        return self.d[key]
+
+    def __setitem__(self, key, value):
+        if isinstance(key, Component):
+            order = self.d[key]
+            self.d[key] = value
+            self.d[order].remove(key)
+            self[value] = key
+        elif isinstance(key, basestring):
+            self.d[self.sxref[key]] = value
+        elif isinstance(key, int):
+            self.d.setdefault(key, [])
+            self.d[key].append(value)
+        else:
+            raise KeyError(_("Invalid key type (%s) for StoreOrder")
+                             % str(type(key)))
+        pass
+
+    def get_enabled_stores(self):
+        """Return an ordered list of password stores
+
+        All stores that are order 0 are dropped from the list.
+        """
+        keys = [k for k in self.d.keys() if isinstance(k, int)]
+        keys.sort()
+        storelist = []
+        for k in keys[1:]:
+            storelist.extend(self.d[k])
+            continue
+        return storelist
+
+    def get_enabled_store_names(self):
+        """Returns the class names of the enabled password stores"""
+        stores = self.get_enabled_stores()
+        return [s.__class__.__name__ for s in stores]
+
+    def get_all_stores(self):
+        return [k for k in self.d.keys() if isinstance(k, Component)]
+
+    def numstores(self):
+        return len(self.get_all_stores())
+
+
+class AccountManagerAdminPages(Component):
+
+    implements(IPermissionRequestor, IAdminPanelProvider, ITemplateProvider)
+
+    def __init__(self):
+        self.acctmgr = AccountManager(self.env)
+        self.guard = AccountGuard(self.env)
+
+    # IPermissionRequestor
+    def get_permission_actions(self):
+        action = ['ACCTMGR_CONFIG_ADMIN', 'ACCTMGR_USER_ADMIN']
+        actions = [('ACCTMGR_ADMIN', action), action[0], action[1],]
+        return actions
+
+    # IAdminPanelProvider
+    def get_admin_panels(self, req):
+        if req.perm.has_permission('ACCTMGR_CONFIG_ADMIN'):
+            yield ('accounts', _("Accounts"), 'config', _("Configuration"))
+        if req.perm.has_permission('ACCTMGR_USER_ADMIN'):
+            yield ('accounts', _("Accounts"), 'users', _("Users"))
+            yield ('accounts', _("Accounts"), 'details', _("Account details"))
+
+    def render_admin_panel(self, req, cat, page, path_info):
+        if page == 'config':
+            return self._do_config(req)
+        elif page == 'users':
+            return self._do_users(req)
+        elif page == 'details':
+            return self._do_acct_details(req)
+
+    def _do_config(self, req):
+        stores = StoreOrder(stores=self.acctmgr.stores,
+                            list=self.acctmgr.password_store)
+        if req.method == 'POST':
+            _setorder(req, stores)
+            self.config.set('account-manager', 'password_store',
+                            ','.join(stores.get_enabled_store_names()))
+            for store in stores.get_all_stores():
+                for attr, option in _getoptions(store):
+                    cls_name = store.__class__.__name__
+                    newvalue = req.args.get('%s.%s' % (cls_name, attr))
+                    self.log.debug("%s.%s: %s" % (cls_name, attr, newvalue))
+                    if newvalue is not None:
+                        self.config.set(option.section, option.name, newvalue)
+                        self.config.save()
+            self.config.set('account-manager', 'force_passwd_change',
+                            req.args.get('force_passwd_change', False))
+            self.config.set('account-manager', 'persistent_sessions',
+                            req.args.get('persistent_sessions', False))
+            self.config.set('account-manager', 'verify_email',
+                            req.args.get('verify_email', False))
+            self.config.set('account-manager', 'refresh_passwd',
+                            req.args.get('refresh_passwd', False))
+            self.config.save()
+        sections = []
+        for store in self.acctmgr.stores:
+            if store.__class__.__name__ == "ResetPwStore":
+                # Exclude special store, that is used strictly internally and
+                # inherits configuration from SessionStore anyway.
+                continue
+            options = []
+            for attr, option in _getoptions(store):
+                opt_val = option.__get__(store, store)
+                opt_val = isinstance(opt_val, Component) and \
+                          opt_val.__class__.__name__ or opt_val
+                options.append(
+                            {'label': attr,
+                            'name': '%s.%s' % (store.__class__.__name__, attr),
+                            'value': opt_val,
+                            'doc': gettext(option.__doc__)
+                            })
+                continue
+            sections.append(
+                        {'name': store.__class__.__name__,
+                        'classname': store.__class__.__name__,
+                        'order': stores[store],
+                        'options' : options,
+                        })
+            continue
+        sections = sorted(sections, key=lambda i: i['name'])
+        numstores = range(0, stores.numstores() + 1)
+        data = {
+            '_dgettext': dgettext,
+            'sections': sections,
+            'numstores': numstores,
+            'force_passwd_change': self.acctmgr.force_passwd_change,
+            'persistent_sessions': self.acctmgr.persistent_sessions,
+            'verify_email': self.acctmgr.verify_email,
+            'refresh_passwd': self.acctmgr.refresh_passwd,
+            }
+        return 'admin_accountsconfig.html', data
+
+    def _do_users(self, req):
+        env = self.env
+        perm = PermissionSystem(env)
+        acctmgr = self.acctmgr
+        acctmod = AccountModule(env)
+        guard = self.guard
+        listing_enabled = acctmgr.supports('get_users')
+        create_enabled = acctmgr.supports('set_password')
+        password_change_enabled = acctmgr.supports('set_password')
+        password_reset_enabled = acctmod.reset_password_enabled
+        delete_enabled = acctmgr.supports('delete_user')
+
+        data = {
+            '_dgettext': dgettext,
+            'listing_enabled': listing_enabled,
+            'create_enabled': create_enabled,
+            'delete_enabled': delete_enabled,
+            'password_change_enabled': password_change_enabled,
+            'password_reset_enabled': password_reset_enabled,
+            'account' : { 'username' : None,
+                          'name' : None,
+                          'email' : None,
+                        }
+        }
+
+        if req.method == 'POST':
+            if req.args.get('add'):
+                if create_enabled:
+                    try:
+                        _create_user(req, env, check_permissions=False)
+                    except TracError, e:
+                        data['editor_error'] = e.message
+                        data['account'] = getattr(e, 'account', '')
+                else:
+                    data['editor_error'] = _(
+                        "The password store does not support creating users.")
+            elif req.args.get('reset') and req.args.get('sel'):
+                if password_reset_enabled:
+                    sel = req.args.get('sel')
+                    sel = isinstance(sel, list) and sel or [sel]
+                    for username, name, email in env.get_known_users():
+                        if username in sel:
+                            acctmod._reset_password(username, email)
+                else:
+                    data['deletion_error'] = _(
+                        "The password reset procedure is not enabled.")
+            elif req.args.get('remove') and req.args.get('sel'):
+                if delete_enabled:
+                    sel = req.args.get('sel')
+                    sel = isinstance(sel, list) and sel or [sel]
+                    for account in sel:
+                        acctmgr.delete_user(account)
+                else:
+                    data['deletion_error'] = _(
+                        "The password store does not support deleting users.")
+            elif req.args.get('change'):
+                attributes = {
+                    'email': _("Email Address"),
+                    'name': _("Pre-/Surname (Nickname)"),
+                    'password': _("Password")
+                    }
+                data['success'] = []
+                error = TracError('')
+                username = acctmgr.handle_username_casing(
+                                   req.args.get('username').strip())
+                try:
+                    if not username:
+                        error.account = {'username' : username}
+                        error.message = _("Username cannot be empty.")
+                        raise error
+
+                    if not acctmgr.has_user(username):
+                        error.account = {'username' : username}
+                        error.message = _("Unknown user %(user)s.",
+                                          user=username)
+                        raise error
+
+                    password = req.args.get('password')
+                    if password and (password.strip() != ''):
+                        if password_change_enabled:
+                            if password != req.args.get('password_confirm'):
+                                error.message = _("The passwords must match.")
+                                raise error
+                            acctmgr.set_password(username, password)
+                            data['success'].append(attributes.get('password'))
+                        else:
+                            data['editor_error'] = _(
+                                """The password store does not support
+                                changing passwords.
+                                """)
+                    for attribute in ('name', 'email'):
+                        value = req.args.get(attribute).strip()
+                        if value:
+                            set_user_attribute(env, username,
+                                               attribute, value)
+                            data['success'].append(attributes.get(attribute))
+                except TracError, e:
+                    data['editor_error'] = e.message
+                    data['account'] = getattr(e, 'account', '')
+
+        if listing_enabled:
+            accounts = {}
+            for username in acctmgr.get_users():
+                accounts[username] = {'username': username}
+                if guard.user_locked(username):
+                    accounts[username]['locked'] = True
+                    url = req.href.admin('accounts', 'details', user=username)
+                    accounts[username]['review_url'] = url
+                    t_lock = guard.lock_time(username)
+                    if t_lock > 0:
+                        t_release = guard.pretty_release_time(req, username)
+                        accounts[username]['release_hint'] = _(
+                            "Locked until %(t_release)s",
+                            t_release=t_release)
+
+            for username, name, email in env.get_known_users():
+                account = accounts.get(username)
+                if account:
+                    account['name'] = name
+                    account['email'] = email
+
+            cursor = acctmgr.last_seen()
+            for username, last_visit in cursor:
+                account = accounts.get(username)
+                if account and last_visit:
+                    account['last_visit'] = format_datetime(last_visit, 
+                                                            tzinfo=req.tz)
+            data['accounts'] = sorted(accounts.itervalues(),
+                                      key=lambda acct: acct['username'])
+        add_stylesheet(req, 'acct_mgr/acct_mgr.css')
+        return 'admin_users.html', data
+
+    def _do_acct_details(self, req):
+        username = req.args.get('user')
+        if not username:
+            # Accessing user account details directly is not useful,
+            # so we revert such request immediately. 
+            add_warning(req, Markup(tag.span(tag_(
+                "Please choose account by username from list to proceed."
+                ))))
+            req.redirect(req.href.admin('accounts', 'users'))
+
+        acctmgr = self.acctmgr
+        guard = self.guard
+
+        if req.method == 'POST':
+            if req.args.get('update'):
+                req.redirect(req.href.admin('accounts', 'details',
+                                            user=username))
+            if req.args.get('delete') or req.args.get('release'):
+                # delete failed login attempts, evaluating attempts count
+                if guard.failed_count(username, reset=True) > 0:
+                    add_notice(req, Markup(tag.span(tag_(
+                        "Failed login attempts for user %(user)s deleted",
+                        user=tag.b(username)
+                        ))))
+            req.redirect(req.href.admin('accounts', 'users'))
+
+        data = {'_dgettext': dgettext,
+                'user': username,
+               }
+        stores = StoreOrder(stores=acctmgr.stores,
+                            list=acctmgr.password_store)
+        user_store = acctmgr.find_user_store(username)
+        if not user_store is None:
+            data['user_store'] = user_store.__class__.__name__
+            data['store_order_num'] = stores[user_store]
+        data['ignore_auth_case'] = \
+            self.config.getbool('trac', 'ignore_auth_case')
+
+        for username_, name, email in self.env.get_known_users():
+            if username_ == username:
+                data['name'] = name
+                if email:
+                    data['email'] = email
+                break
+        ts_seen = None
+        for row in acctmgr.last_seen(username):
+            if row[0] == username and row[1]:
+                data['last_visit'] = format_datetime(row[1], tzinfo=req.tz)
+                break
+
+        attempts = []
+        attempts_count = guard.failed_count(username, reset = None)
+        if attempts_count > 0:
+            for attempt in guard.get_failed_log(username):
+                t = format_datetime(to_datetime(
+                                         attempt['time']), tzinfo=req.tz)
+                attempts.append({'ipnr': attempt['ipnr'], 'time': t})
+        data['attempts'] = attempts
+        data['attempts_count'] = attempts_count
+        data['pretty_lock_time'] = guard.pretty_lock_time(username, next=True)
+        data['lock_count'] = guard.lock_count(username)
+        if guard.user_locked(username) is True:
+            data['user_locked'] = True
+            data['release_time'] = guard.pretty_release_time(req, username)
+
+        if is_enabled(self.env, EmailVerificationModule) and \
+                acctmgr.verify_email is True:
+            data['verification'] = 'enabled'
+            data['email_verified'] = acctmgr.email_verified(username, email)
+            self.log.debug('AcctMgr:admin:_do_acct_details for user \"' + \
+                username + '\", email \"' + str(email) + '\": ' + \
+                str(data['email_verified']))
+
+        add_stylesheet(req, 'acct_mgr/acct_mgr.css')
+        #req.href.admin('accounts', 'details', user=username)
+        return 'account_details.html', data
+
+    # ITemplateProvider methods
+
+    def get_htdocs_dirs(self):
+        """Return the absolute path of a directory containing additional
+        static resources (such as images, style sheets, etc).
+        """
+        return [('acct_mgr', resource_filename(__name__, 'htdocs'))]
+
+    def get_templates_dirs(self):
+        """Return the absolute path of the directory containing the provided
+        Genshi templates.
+        """
+        return [resource_filename(__name__, 'templates')]
+

File acct_mgr/acct_mgr/api.py

     add_domain, _, N_, gettext, ngettext, tag_ = \
         domain_functions('acct_mgr', ('add_domain', '_', 'N_', 'gettext',
                                       'ngettext', 'tag_'))
+    dgettext = None
 except ImportError:
     from  genshi.builder         import  tag as tag_
     from  trac.util.translation  import  gettext
     _ = gettext
     N_ = lambda text: text
-    ngettext = _
     def add_domain(a,b,c=None):
         pass
+    def dgettext(domain, string, **kwargs):
+        return safefmt(string, kwargs)
+    def ngettext(singular, plural, num, **kwargs):
+        string = singular if num == 1 else plural
+        kwargs.setdefault('num', num)
+        return safefmt(string, kwargs)
+    def safefmt(string, kwargs):
+        if kwargs:
+            try:
+                return string % kwargs
+            except KeyError:
+                pass
+        return string
 
 
 class IPasswordStore(Interface):
         if user:
             sql = "%s AND sid='%s'" % (sql, user)
         cursor.execute(sql)
-        return cursor or None
+        # Don't pass over the cursor (outside of scope), only it's content.
+        res = []
+        for row in cursor:
+            res.append(row)
+        return not len(res) == 0 and res or None
 
     def set_password(self, user, password, old_password = None):
         user = self.handle_username_casing(user)

File acct_mgr/acct_mgr/guard.py

 from trac.config        import Configuration, IntOption, Option
 from trac.core          import Component
 from trac.util.datefmt  import format_datetime, pretty_timedelta, \
-                               to_datetime, to_utimestamp
+                               to_datetime
+try:
+    from trac.util.datefmt  import to_utimestamp
+except ImportError:
+    # Fallback for Trac 0.11 compatibility
+    from trac.util.datefmt  import to_timestamp as to_utimestamp
 
 from acct_mgr.api       import AccountManager
 

File acct_mgr/acct_mgr/htfile.py

     def get_users(self):
         filename = str(self.filename)
         if not os.path.exists(filename):
-            self.log.debug('acct_mgr: get_users() -- '
-                           'Can\'t locate "%s"' % filename)
+            self.log.error('acct_mgr: get_users() -- '
+                           'Can\'t locate password file "%s"' % filename)
             return []
         return self._get_users(filename)
 
     def check_password(self, user, password):
         filename = str(self.filename)
         if not os.path.exists(filename):
-            self.log.debug('acct_mgr: check_password() -- '
-                           'Can\'t locate "%s"' % filename)
+            self.log.error('acct_mgr: check_password() -- '
+                           'Can\'t locate password file "%s"' % filename)
             return False
         user = user.encode('utf-8')
         password = password.encode('utf-8')
         # DEVEL: Better use new 'finally' statement here, but
         #   still need to care for Python 2.4 (RHEL5.x) for now
         except:
-            self.log.debug('acct_mgr: check_password() -- '
-                           'Can\'t read "%s"' % filename)
+            self.log.error('acct_mgr: check_password() -- '
+                           'Can\'t read password file "%s"' % filename)
             pass
         if isinstance(f, file):
             f.close()
             f.close()
             if not f.closed:
                 self.log.debug('acct_mgr: _update_file() -- '
-                               'Closing file "%s" failed' % filename)
+                               'Closing password file "%s" failed' % filename)
         return matched
 
 

File acct_mgr/acct_mgr/locale/sv/LC_MESSAGES/acct_mgr.po

+# Swedish translations for TracAccountManager.
+# Copyright (C) 2011
+# This file is distributed under the same license as the
+# TracAccountManager project.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: TracAccountManager 0.3\n"
+"Report-Msgid-Bugs-To: hoff.st@shaas.net\n"
+"POT-Creation-Date: 2011-06-20 22:09+0200\n"
+"PO-Revision-Date: 2011-07-06 19:44+0000\n"
+"Last-Translator: mrelbe <mikael@relbe.se>\n"
+"Language-Team: Swedish <trac-dev@googlegroups.com>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 1.0dev-r482\n"
+
+#: acct_mgr/admin.py:81
+#, python-format
+msgid "Invalid key type (%s) for StoreOrder"
+msgstr "Ogiltig nyckeltyp (%s) för lösenordsförrådet"
+
+#: acct_mgr/admin.py:127 acct_mgr/admin.py:129 acct_mgr/admin.py:130
+#: acct_mgr/notification.py:171 acct_mgr/templates/admin_users.html:11
+msgid "Accounts"
+msgstr "Konton"
+
+#: acct_mgr/admin.py:127
+msgid "Configuration"
+msgstr "Inställningar"
+
+#: acct_mgr/admin.py:129
+msgid "Users"
+msgstr "Användare"
+
+#: acct_mgr/admin.py:130
+msgid "Account details"
+msgstr "Kontodetaljer"
+
+#: acct_mgr/admin.py:234
+msgid "The password store does not support creating users."
+msgstr "Lösenordsförrådet medger ej att användare läggs till."
+
+#: acct_mgr/admin.py:244
+msgid "The password reset procedure is not enabled."
+msgstr "Återställning av lösenord ej aktiverat."
+
+#: acct_mgr/admin.py:253
+msgid "The password store does not support deleting users."
+msgstr "Lösenordsförrådet medger ej borttagning av användare."
+
+#: acct_mgr/admin.py:257
+msgid "Email Address"
+msgstr "E-postadress"
+
+#: acct_mgr/admin.py:258
+msgid "Pre-/Surname (Nickname)"
+msgstr "Förnamn/efternamn (Pseudonym)"
+
+#: acct_mgr/admin.py:259
+msgid "Password"
+msgstr "Lösenord"
+
+#: acct_mgr/admin.py:268 acct_mgr/web_ui.py:58
+msgid "Username cannot be empty."
+msgstr "Användarnamn måste anges."
+
+#: acct_mgr/admin.py:273
+#, python-format
+msgid "Unknown user %(user)s."
+msgstr "Okänd användare %(user)s."
+
+#: acct_mgr/admin.py:281 acct_mgr/web_ui.py:124 acct_mgr/web_ui.py:349
+msgid "The passwords must match."
+msgstr "Lösenorden måste överensstämma."
+
+#: acct_mgr/admin.py:286
+msgid ""
+"The password store does not support\n"
+"                                changing passwords.\n"
+"                                "
+msgstr "Lösenordsförrådet medger ej ändring av lösenord."
+
+#: acct_mgr/admin.py:311
+#, python-format
+msgid "Locked until %(t_release)s"
+msgstr "Låst till och med %(t_release)s"
+
+#: acct_mgr/admin.py:337
+msgid "Please choose account by username from list to proceed."
+msgstr "Välj konto i listan med användarnamn för att fortsätta."
+
+#: acct_mgr/admin.py:354
+#, python-format
+msgid "Failed login attempts for user %(user)s deleted"
+msgstr ""
+"Tog bort registrering av misslyckade inloggningsförsök för användaren "
+"%(user)s"
+
+#: acct_mgr/api.py:265
+#, python-format
+msgid ""
+"The authentication backend for user %s does not support\n"
+"                setting the password.\n"
+"                "
+msgstr "Autentiseringsmekanismen för användare %s medger ej att lösenordet sätts."
+
+#: acct_mgr/api.py:277
+msgid ""
+"None of the IPasswordStore components listed in the\n"
+"                trac.ini supports setting the password or creating users."
+"\n"
+"                "
+msgstr ""
+"Ingen av IPasswordStore-komponenterna som angivits i trac.ini medger att "
+"lösenord sätts, eller att användare skapas.                "
+
+#: acct_mgr/db.py:24 acct_mgr/htfile.py:197
+msgid "Default hash type of new/updated passwords"
+msgstr "Standardhash-typ för nya/uppdatera lösenord"
+
+#: acct_mgr/htfile.py:36
+msgid ""
+"Path relative to Trac environment or full host machine\n"
+"                path to password file"
+msgstr ""
+"Sökväg till lösenordsfilen, relativt Trac-miljön eller absolut sökväg "
+"från systemets rotkatalog"
+
+#: acct_mgr/htfile.py:143
+msgid ""
+"The password file could not be read. Trac requires\n"
+"                    read and write access to both the password file\n"
+"                    and its parent directory."
+msgstr ""
+"Lösenordsfilen kunde ej läsas. Trac kräver läs- och skrivrättigheter till"
+" både lösenordsfilen samt katalogen den befinner sig i."
+
+#: acct_mgr/htfile.py:161
+msgid ""
+"The password file could not be updated. Trac requires\n"
+"                    read and write access to both the password file\n"
+"                    and its parent directory."
+msgstr ""
+"Lösenordsfilen kunde ej uppdateras. Trac kräver läs- och skrivrättigheter"
+" till både lösenordsfilen samt katalogen den befinner sig i."
+
+#: acct_mgr/htfile.py:235
+msgid "Realm to select relevant htdigest file entries"
+msgstr "Namnrymd (realm) för val av relevanta poster i htdigest-filen"
+
+#: acct_mgr/http.py:25
+msgid "URL of the HTTP authentication service"
+msgstr "URL för den HTTP-baserade autentiseringstjänsten"
+
+#: acct_mgr/notification.py:55
+msgid "The email and username do not match a known account."
+msgstr "E-post och användarnamn överensstämmer ej med något känt konto."
+
+#: acct_mgr/notification.py:171
+msgid "Notification"
+msgstr "Avisering"
+
+#: acct_mgr/pwhash.py:97
+msgid ""
+"The \"crypt\" module is unavailable\n"
+"                                    on this platform."
+msgstr "\"crypt\"-modulen ej tillgänglig på denna plattform."
+
+#: acct_mgr/svnserve.py:29
+msgid ""
+"Path to the users file; leave blank to locate\n"
+"                the users file by reading svnserve.conf"
+msgstr ""
+"Sökväg till lösenordsfilen (lämna blank för att lokalisera den genom "
+"svnserve.conf)"
+
+#: acct_mgr/web_ui.py:64
+#, python-format
+msgid "Username %s is not allowed."
+msgstr "Användarnamnet %s är ej tillåtet."
+
+#: acct_mgr/web_ui.py:74 acct_mgr/web_ui.py:94
+#, python-format
+msgid "Another account or group named %s already exists."
+msgstr "Konto eller grupp %s existerar redan."
+
+#: acct_mgr/web_ui.py:111
+msgid "The username must not contain any of these characters:"
+msgstr "Användarnamnet får ej innehålla något av följande tecken:"
+
+#: acct_mgr/web_ui.py:120 acct_mgr/web_ui.py:346 acct_mgr/web_ui.py:359
+msgid "Password cannot be empty."
+msgstr "Lösenordet får ej vara tomt."
+
+#: acct_mgr/web_ui.py:131
+msgid "You must specify a valid email address."
+msgstr "Du måste ange en giltig e-postadress."
+
+#: acct_mgr/web_ui.py:135
+msgid ""
+"The email address specified appears to be\n"
+"                              invalid. Please specify a valid email "
+"address.\n"
+"                              "
+msgstr ""
+"Den angivna e-postadressen verkar vara ogiltig. Var vänlig ange giltig "
+"e-postadress."
+
+#: acct_mgr/web_ui.py:140
+msgid ""
+"The email address specified is already in\n"
+"                              use. Please specify a different one.\n"
+"                              "
+msgstr ""
+"Den angivna e-postadressen är redan registrerad. Var vänlig ange en annan"
+" adress."
+
+#: acct_mgr/web_ui.py:220 acct_mgr/templates/admin_users.html:80
+#: acct_mgr/templates/prefs_account.html:42
+msgid "Account"
+msgstr "Konto"
+
+#: acct_mgr/web_ui.py:259 acct_mgr/templates/login.html:42
+#: acct_mgr/templates/login.html:63 acct_mgr/templates/login.html:66
+msgid "Forgot your password?"
+msgstr "Glömt ditt lösenord?"
+
+#: acct_mgr/web_ui.py:275
+msgid "Are you sure you want to delete your account?"
+msgstr "Är du säker på att du vill radera ditt konto?"
+
+#: acct_mgr/web_ui.py:285
+msgid "Thank you for taking the time to update your password."
+msgstr "Tack för att du tog dig tid och uppdaterade ditt lösenord."
+
+#: acct_mgr/web_ui.py:294
+msgid ""
+"You are required to change password because of a recent password change "
+"request. "
+msgstr "Du måste byta lösenord då begäran om detta nyligen gjorts."
+
+#: acct_mgr/web_ui.py:297
+msgid "Please change your password now."
+msgstr "Var vänlig och ändra ditt lösenord nu."
+
+#: acct_mgr/web_ui.py:308
+msgid "Username is required"
+msgstr "Användarnamn krävs"
+
+#: acct_mgr/web_ui.py:310
+msgid "Email is required"
+msgstr "E-postadress krävs"
+
+#: acct_mgr/web_ui.py:316
+msgid "The email and username must match a known account."
+msgstr ""
+"E-postadress och användarnamn måste överensstämma med ett registrerat "
+"konto."
+
+#: acct_mgr/web_ui.py:340
+msgid "Old Password cannot be empty."
+msgstr "Gammalt lösenord får ej vara tomt."
+
+#: acct_mgr/web_ui.py:342
+msgid "Old Password is incorrect."
+msgstr "Gammalt lösenord är inkorrekt."
+
+#: acct_mgr/web_ui.py:352
+msgid "Password successfully updated."
+msgstr "Lösenordet har uppdaterats."
+
+#: acct_mgr/web_ui.py:361
+msgid "Password is incorrect."
+msgstr "Lösenordet är felaktigt."
+
+#: acct_mgr/web_ui.py:420 acct_mgr/templates/login.html:55
+#: acct_mgr/templates/login.html:58 acct_mgr/templates/register.html:11
+msgid "Register"
+msgstr "Registrering"
+
+#: acct_mgr/web_ui.py:449
+#, python-format
+msgid ""
+"Registration has been finished successfully.\n"
+"                     You may login as user %(user)s now."
+msgstr "Registrering genomförd. Du kan nu logga in med användarnamnet %(user)s."
+
+#. TRANSLATOR: Intentionally obfuscated login error
+#: acct_mgr/web_ui.py:558
+msgid "Invalid username or password"
+msgstr "Ogiltigt användarnamn eller lösenord"
+
+#: acct_mgr/web_ui.py:563
+#, python-format
+msgid ""
+"Account locked, please try again after\n"
+"                            %(release_time)s\n"
+"                            "
+msgstr ""
+"Konto är låst, vänligen försök igen efter %(release_time)s"
+"                            "
+
+#: acct_mgr/web_ui.py:568
+msgid "Account locked"
+msgstr "Kontot låst"
+
+#: acct_mgr/web_ui.py:573
+#, python-format
+msgid "Login after %(attempts)s failed attempt"
+msgid_plural "Login after %(attempts)s failed attempts"
+msgstr[0] "Inloggad efter %(attempts)s misslyckad inloggning"
+msgstr[1] "Inloggad efter %(attempts)s misslyckade inloggningar"
+
+#. TRANSLATOR: Your permissions have been limited until you ...
+#: acct_mgr/web_ui.py:787
+msgid "verify your email address"
+msgstr "bekräftat din e-postadress"
+
+#. TRANSLATOR: ... verify your email address
+#: acct_mgr/web_ui.py:790
+#, python-format
+msgid "Your permissions have been limited until you %(link)s."
+msgstr "Dina rättigheter har begränsats till dess att du%(link)s."
+
+#. TRANSLATOR: An email has been sent to %(email)s
+#. with a token to ... (the link label for following message)
+#: acct_mgr/web_ui.py:816
+msgid "verify your new email address"
+msgstr "bekräftande av din nya e-postadress"
+
+#. TRANSLATOR: ... verify your new email address
+#: acct_mgr/web_ui.py:819
+#, python-format
+msgid ""
+"An email has been sent to %(email)s with a token to\n"
+"                %(link)s."
+msgstr "E-post har skickats till %(email)s med uppgifter för %(link)s."
+
+#: acct_mgr/web_ui.py:832
+msgid "Please log in to finish email verification procedure."
+msgstr "Vänligen logga in för att fullborda bekräftning av e-postadressen."
+
+#: acct_mgr/web_ui.py:836
+msgid "Your email is already verified."
+msgstr "Din e-postadress är redan bekräftad."
+
+#: acct_mgr/web_ui.py:844
+#, python-format
+msgid "A notification email has been resent to <%s>."
+msgstr "En e-postavisering har åter skickats till <%s>."
+
+#: acct_mgr/web_ui.py:851
+msgid "Thank you for verifying your email address."
+msgstr "Tack för att du bekräftade din e-postadress."
+
+#: acct_mgr/web_ui.py:854
+msgid "Invalid verification token"
+msgstr "Ogiltiga bekräftelseuppgifter"
+
+#: acct_mgr/templates/account_details.html:11
+msgid "Account Details"
+msgstr "Kontodetaljer"
+
+#: acct_mgr/templates/account_details.html:17
+msgid "Review User Account Details"
+msgstr "Granska kontodetaljer"
+
+#: acct_mgr/templates/account_details.html:19
+#, python-format
+msgid "for [1:%(name)s] ([2:%(user)s])"
+msgstr "för [1:%(name)s] ([2:%(user)s])"
+
+#: acct_mgr/templates/account_details.html:22
+#, python-format
+msgid "for [1:%(user)s]"
+msgstr "för [1:%(user)s]"
+
+#: acct_mgr/templates/account_details.html:31
+msgid "Account Status"
+msgstr "Kontostatus"
+
+#: acct_mgr/templates/account_details.html:35
+#, python-format
+msgid ""
+"Credentials for this user are stored in AuthStore number\n"
+"              [1:%(order_num)s] (%(store)s)."
+msgstr ""
+"Uppgifter för denna användare är lagrade i AuthStore nummer "
+"[1:%(order_num)s] (%(store)s)."
+
+#: acct_mgr/templates/account_details.html:39
+msgid "Username matching is set to [1:not case-sensitive]."
+msgstr "Användarnamn är inställda att vara [1:okänsliga för skiftläge]."
+
+#: acct_mgr/templates/account_details.html:41
+msgid "Username matching is set to [1:case-sensitive]."
+msgstr "Användarnamn är inställda att vara [1:känsliga för skiftläge]."
+
+#: acct_mgr/templates/account_details.html:46
+msgid ""
+"No store provides credentials for this user,\n"
+"              so the user currently can't be authenticated and\n"
+"              access to this [1:account is effectively blocked],\n"
+"              while account details may still be available."
+msgstr ""
+"Inget lösenordsförråd innehåller uppgifter om denna användare som därför "
+"ej kan autentiseras, tillgång till detta [1:konto är blockerat], men "
+"kontoinformation kan fortfarande finnas tillgängligt."
+
+#: acct_mgr/templates/account_details.html:58
+#, python-format
+msgid ""
+"[1:]\n"
+"                This account has been locked until %(time)s[2:]\n"
+"                and even valid login attempts are rejected meanwhile."
+msgstr ""
+"[1:]Detta konto är låst till %(time)s[2:]och även giltiga "
+"inloggningsförsök förhindras tills dess."
+
+#: acct_mgr/templates/account_details.html:63
+msgid "This account has been locked permanently."
+msgstr "Detta konto har låsts för gott."
+
+#: acct_mgr/templates/account_details.html:67
+msgid "Release account lock"
+msgstr "Ta bort kontolåset"
+
+#: acct_mgr/templates/account_details.html:67
+msgid "Unlock"
+msgstr "Lås upp"
+
+#: acct_mgr/templates/account_details.html:79
+#, python-format
+msgid ""
+"Lock condition has been met\n"
+"                    %(count)s time by now."
+msgid_plural ""
+"Lock condition has been met\n"
+"                    %(count)s times by now."
+msgstr[0] "Låsvillkor har uppfyllts %(count)s gång tills nu."
+msgstr[1] "Låsvillkor har uppfyllts %(count)s gånger tills nu."
+
+#: acct_mgr/templates/account_details.html:85
+#, python-format
+msgid ""
+"Therefore after another failed login attempt authentication\n"
+"                  for this account would be retarded by %(time)s."
+msgstr ""
+"Ytterligare ett till misslyckat inloggningsförsök kommer därför medföra "
+"att autentisering för detta konto nekas under %(time)s."
+
+#: acct_mgr/templates/account_details.html:92
+msgid "Lock condition has not been met yet."
+msgstr "Låsvillkor har ej uppfyllts än."
+
+#: acct_mgr/templates/account_details.html:98
+msgid "No constraints are set for this account."
+msgstr "Inga begränsningar är uppsatta för detta konto."
+
+#: acct_mgr/templates/account_details.html:107
+msgid "Verification"
+msgstr "Bekräftelse"
+
+#: acct_mgr/templates/account_details.html:111
+#, python-format
+msgid "Current email address: <%(email)s>"
+msgstr "Nuvarande e-postadress: <%(email)s>"
+
+#: acct_mgr/templates/account_details.html:114
+msgid "This address has been verified successfully."
+msgstr "Denna adress har bekräftats."
+
+#: acct_mgr/templates/account_details.html:116
+#, python-format
+msgid ""
+"Verification is pending\n"
+"                  ([1:token: '%(token)s' ])."
+msgstr "Utestående bekräftelse ([1:uppgifter: '%(token)s' ])."
+
+#: acct_mgr/templates/account_details.html:119
+msgid "This address has not been verified yet."
+msgstr "Denna adress har ej ännu bekräftats."
+
+#: acct_mgr/templates/account_details.html:124
+msgid "No email address is registered for this account."
+msgstr "Ingen e-postadress är registrerad för detta konto."
+
+#: acct_mgr/templates/account_details.html:132
+msgid "Access History"
+msgstr "Inloggningshistorik"
+
+#: acct_mgr/templates/account_details.html:135
+#, python-format
+msgid "Last login: %(time)s"
+msgstr "Senast inloggad: %(time)s"
+
+#: acct_mgr/templates/account_details.html:137
+msgid "The user has not logged in before."
+msgstr "Användaren har ej loggat in förut."
+
+#: acct_mgr/templates/account_details.html:141
+#, python-format
+msgid "Total failed attempts: %(count)s"
+msgstr "Antal misslyckade inloggningar: %(count)s"
+
+#: acct_mgr/templates/account_details.html:142
+msgid "Table: Last failed login attempts log view"
+msgstr "Tabell: Senast registrerade misslyckade inloggningsförsök"
+
+#: acct_mgr/templates/account_details.html:147
+msgid "IP address"
+msgstr "IP-adress"
+
+#: acct_mgr/templates/account_details.html:148
+msgid "Log time"
+msgstr "Registrerad tid"
+
+#: acct_mgr/templates/account_details.html:159
+msgid "Delete login failure log"
+msgstr "Radera registreringar av misslyckade inloggningar"
+
+#: acct_mgr/templates/account_details.html:159
+msgid "Delete Log"
+msgstr "Radera logg"
+
+#: acct_mgr/templates/account_details.html:166
+msgid "There is currently no failed login attempt logged."
+msgstr "För närvarande finns inga registrerade misslyckade inloggningar."
+
+#: acct_mgr/templates/account_details.html:172
+msgid "Update"
+msgstr "Uppdatera"
+
+#: acct_mgr/templates/admin_accountsconfig.html:11
+#: acct_mgr/templates/admin_accountsconfig.html:15
+msgid "Accounts: Configuration"
+msgstr "Konton: Inställningar"
+
+#: acct_mgr/templates/admin_accountsconfig.html:41
+msgid "Password Refresh"
+msgstr "Uppdatera lösenord"
+
+#: acct_mgr/templates/admin_accountsconfig.html:44
+msgid "Silently update password hashes on next successful login."
+msgstr "Uppdatera lösenordshasharna vid nästa lyckade inloggning."
+
+#: acct_mgr/templates/admin_accountsconfig.html:49
+msgid "Persistent Sessions"
+msgstr "Beständiga sessioner"
+
+#: acct_mgr/templates/admin_accountsconfig.html:52
+msgid ""
+"Allow the user to be remembered across sessions without needing to\n"
+"          re-authenticate."
+msgstr "Kom ihåg användaren mellan sessioner utan att åter kräva autentisering."
+
+#: acct_mgr/templates/admin_accountsconfig.html:56
+msgid ""
+"This is, user checks a \"Remember Me\"\n"
+"          [1:checkbox] and, next time he visits the site,\n"
+"          he'll be remembered and automatically authenticated."
+msgstr ""
+"När användaren aktiverar [1:kryssrutan] \"Kom ihåg mig\" blir denna "
+"automatiskt autentiserad vid nästa besök."
+
+#: acct_mgr/templates/admin_accountsconfig.html:63
+msgid "Password Reset"
+msgstr "Återställ lösenord"
+
+#: acct_mgr/templates/admin_accountsconfig.html:66
+msgid "Force users to change passwords after a password reset."
+msgstr "Tvinga användarna att byta lösenord vid återställning av lösenord."
+
+#: acct_mgr/templates/admin_accountsconfig.html:72
+msgid "Verify email"
+msgstr "Bekräfta e-post"
+
+#: acct_mgr/templates/admin_accountsconfig.html:75
+msgid "Force users to verify their email addresses."
+msgstr "Tvinga användare att bekräfta sin e-postadress."
+
+#: acct_mgr/templates/admin_accountsconfig.html:80
+#: acct_mgr/templates/admin_accountsnotification.html:48
+msgid "Save"
+msgstr "Spara"
+
+#: acct_mgr/templates/admin_accountsnotification.html:11
+#: acct_mgr/templates/admin_accountsnotification.html:15
+msgid "Accounts: Notification Configuration"
+msgstr "Konton: Konfigurering av aviseringar"
+
+#: acct_mgr/templates/admin_accountsnotification.html:19
+msgid "Account Notification"
+msgstr "Kontoavisering"
+
+#: acct_mgr/templates/admin_accountsnotification.html:20
+msgid ""
+"Set the following options in order to be notified of\n"
+"          account creation, password reset and account deletion."
+msgstr ""
+"Ställ in följande för att få aviseringar om nya konton, återställning av "
+"lösenord och vid radering av konton."
+
+#: acct_mgr/templates/admin_accountsnotification.html:23
+msgid "Notification Actions"
+msgstr "Aviseringsåtgärder"
+
+#: acct_mgr/templates/admin_accountsnotification.html:24
+msgid ""
+"This is a list of actions which you can\n"
+"          enable or disable by [1:checking] the [2:checkboxes]."
+msgstr ""
+"Här är en lista av åtgärder som du kan aktivera eller avaktivera genom "
+"att [1:kryssa] i [2:kryssrutorna]."
+
+#: acct_mgr/templates/admin_accountsnotification.html:28
+msgid "Get notified of new account creation"
+msgstr "Erhåll avisering när konto skapas"
+
+#: acct_mgr/templates/admin_accountsnotification.html:32
+msgid "Get notified of password reset"
+msgstr "Erhåll avisering vid återställning av lösenord"
+
+#: acct_mgr/templates/admin_accountsnotification.html:36
+msgid "Get notified of account deletion"
+msgstr "Erhåll avisering vid radering av konto"
+
+#: acct_mgr/templates/admin_accountsnotification.html:39
+msgid "Notification Recipient Addresses"
+msgstr "Mottagaradresser vid avisering"
+
+#: acct_mgr/templates/admin_accountsnotification.html:40
+msgid ""
+"Space-separated list of email addresses and/or\n"
+"          usernames that get notified of the above actions:"
+msgstr ""
+"Lista av mailadresser och/eller användarnamn, separerade med mellanslag, "
+"som ska aviseras vid ovanstående händelser."
+
+#: acct_mgr/templates/admin_users.html:15
+msgid "Manage User Accounts"
+msgstr "Hantera användarkonton"
+
+#: acct_mgr/templates/admin_users.html:24
+#, python-format
+msgid "Successfully updated: %(success)s"
+msgstr "Uppdatering genomförd: %(success)s"
+
+#: acct_mgr/templates/admin_users.html:29
+msgid "Add/Edit Account:"
+msgstr "Lägg till/redigera konto"
+
+#: acct_mgr/templates/admin_users.html:31 acct_mgr/templates/login.html:30
+#: acct_mgr/templates/register.html:33
+#: acct_mgr/templates/reset_password.html:50
+msgid "Username:"
+msgstr "Användarnamn:"
+
+#: acct_mgr/templates/admin_users.html:36 acct_mgr/templates/login.html:35
+#: acct_mgr/templates/prefs_account.html:27 acct_mgr/templates/register.html:39
+msgid "Password:"
+msgstr "Lösenord:"
+
+#: acct_mgr/templates/admin_users.html:41
+#: acct_mgr/templates/prefs_account.html:71 acct_mgr/templates/register.html:45
+msgid "Confirm Password:"
+msgstr "Konfirmera lösenord:"
+
+#: acct_mgr/templates/admin_users.html:46
+msgid "Pre-/Surname (Nickname):"
+msgstr "Förnamn/efternamn (Pseudonym):"
+
+#: acct_mgr/templates/admin_users.html:51
+#: acct_mgr/templates/reset_password.html:55
+msgid "Email Address:"
+msgstr "E-postadress:"
+
+#: acct_mgr/templates/admin_users.html:55
+msgid "Add a new user account or edit an existing one."
+msgstr "Lägg till nytt konto eller redigera ett existerade."
+
+#: acct_mgr/templates/admin_users.html:57
+msgid " Add "
+msgstr "Lägg till"
+
+#: acct_mgr/templates/admin_users.html:60
+msgid " Change "
+msgstr "Ändra"
+
+#: acct_mgr/templates/admin_users.html:70
+msgid "This password store does not support listing users."
+msgstr "Lösenordsförrådet medger ej listning av användare."
+
+#: acct_mgr/templates/admin_users.html:80
+msgid "Name"
+msgstr "Namn"
+
+#: acct_mgr/templates/admin_users.html:80
+msgid "Email"
+msgstr "E-post"
+
+#: acct_mgr/templates/admin_users.html:80
+msgid "Last Login"
+msgstr "Senaste inloggad"
+
+#: acct_mgr/templates/admin_users.html:96
+msgid "Permanently locked"
+msgstr "Permanent blockerad"
+
+#: acct_mgr/templates/admin_users.html:110
+msgid "Reset passwords"
+msgstr "Återställ lösenord"
+
+#: acct_mgr/templates/admin_users.html:113
+msgid "Remove selected accounts"
+msgstr "Radera valda konton"
+
+#: acct_mgr/templates/login.html:11 acct_mgr/templates/login.html:23
+#: acct_mgr/templates/login.html:51
+msgid "Login"
+msgstr "Logga in"
+
+#: acct_mgr/templates/login.html:48
+msgid "Remember me"
+msgstr "Kom ihåg mig"
+
+#: acct_mgr/templates/login.html:76 acct_mgr/templates/prefs_account.html:20
+#: acct_mgr/templates/prefs_account.html:47
+#: acct_mgr/templates/prefs_account.html:54 acct_mgr/templates/register.html:24
+#: acct_mgr/templates/reset_password.html:42
+msgid "Error"
+msgstr "Fel"
+
+#: acct_mgr/templates/prefs_account.html:17
+msgid "Delete Account"
+msgstr "Radera konto"
+
+#: acct_mgr/templates/prefs_account.html:34
+msgid "Delete account"
+msgstr "Radera konto"
+
+#: acct_mgr/templates/prefs_account.html:51
+msgid "Change Password"
+msgstr "Ändra lösenord"
+
+#: acct_mgr/templates/prefs_account.html:59
+msgid "Old Password:"
+msgstr "Gammalt lösenord:"
+
+#: acct_mgr/templates/prefs_account.html:65
+msgid "New Password:"
+msgstr "Nytt lösenord:"
+
+#: acct_mgr/templates/register.html:21
+msgid "Register an account"
+msgstr "Regstrera ett konto"
+
+#: acct_mgr/templates/register.html:30
+msgid "Required"
+msgstr "Nödvändiga uppgifter"
+
+#: acct_mgr/templates/register.html:51 acct_mgr/templates/register.html:73
+msgid "Email:"
+msgstr "E-post:"
+
+#: acct_mgr/templates/register.html:55
+msgid ""
+"The email address is required for Trac to send you a\n"
+"              verification token."
+msgstr ""
+"E-postadressen behövs för att Trac ska kunna skicka dig "
+"verifieringsinformation."
+
+#: acct_mgr/templates/register.html:58
+msgid ""
+"Entering your email address will\n"
+"              also enable you to reset your password if you ever forget "
+"it."
+msgstr ""
+"Anger du din e-postadress kommer du att kunna återställa ditt lösenord "
+"ifall du glömmer det."
+
+#: acct_mgr/templates/register.html:65
+msgid "Optional"
+msgstr "Valfritt"
+
+#: acct_mgr/templates/register.html:67
+msgid "Name:"
+msgstr "Namn:"
+
+#: acct_mgr/templates/register.html:77
+msgid ""
+"Entering your email address\n"
+"              will enable you to reset your password if you ever forget "
+"it."
+msgstr ""
+"Anger du din e-postadress kommer du att kunna återställa ditt lösenord "
+"ifall du glömmer det."
+
+#: acct_mgr/templates/register.html:82
+msgid "Create account"
+msgstr "Skapa konto"
+
+#: acct_mgr/templates/reset_password.html:11
+#: acct_mgr/templates/reset_password.html:21
+msgid "Reset Password"
+msgstr "Återställ lösenord"
+
+#: acct_mgr/templates/reset_password.html:25
+msgid "Already logged in"
+msgstr "Redan inloggad"
+
+#: acct_mgr/templates/reset_password.html:26
+msgid ""
+"You're already logged in. If you need to change\n"
+"          your password please use the\n"
+"          [1:Account Preferences] page."
+msgstr ""
+"Du är redan inloggad. Se [1:kontoinställningar] om du vill ändra ditt "
+"lösenord."
+
+#: acct_mgr/templates/reset_password.html:32
+#, python-format
+msgid ""
+"A new password\n"
+"        has been emailed to you at %(email)s ."
+msgstr "Ett nytt lösenord har skickats till din e-postadress %(email)s."
+
+#: acct_mgr/templates/reset_password.html:37
+msgid ""
+"If you've forgotten your password, enter your username and\n"
+"        email address below and you'll be emailed a new password."
+msgstr ""
+"Om du glömt ditt lösenord, ange ditt användarnamn och e-postadress nedan "
+"för att erhålla nytt lösenord via e-post."
+
+#: acct_mgr/templates/reset_password.html:59
+msgid "Reset password"
+msgstr "Återställ lösenord"
+
+#: acct_mgr/templates/verify_email.html:11