changeset 5939:24054d620fc1

moin account inactive - find, show, disable, remove inactive users
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Fri, 25 Jan 2013 23:01:33 +0100
parents f4e0854b1604
children 1960726cd356
files MoinMoin/script/account/inactive.py MoinMoin/user.py docs/CHANGES
diffstat 3 files changed, 140 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/account/inactive.py	Fri Jan 25 23:01:33 2013 +0100
@@ -0,0 +1,132 @@
+# -*- coding: iso-8859-1 -*-
+"""
+MoinMoin - find inactive users (and disable / remove them)
+
+@copyright: 2013 MoinMoin:ThomasWaldmann,
+@license: GNU GPL, see COPYING for details.
+"""
+
+import sys, os
+
+from MoinMoin.script import MoinScript, log
+from MoinMoin.user import getUserList, User
+from MoinMoin.logfile import editlog
+
+class PluginScript(MoinScript):
+    """\
+Purpose:
+========
+This tool allows you to find inactive users on your wiki via a command line interface.
+
+Inactive user means: a user profile with a specific userid exists, but there is not
+any edit logged for that userid.
+
+But please review the list before removing or disabling users, there are legitimate
+users who just read and never edit. If your wiki has strict ACLs, they might need
+to be able to log in to read. Use --show.
+
+Usage:
+    For all your wikis sharing a single user_dir, run:
+        moin ... account inactive --py-append keep-users.py
+    Then, run (for one of the wikis sharing this user_dir):
+        moin ... account inactive --py-exec keep-users.py --show
+    If you want to keep some user profiles that are shown there, add the userids to
+    the keep-users.py file in the same way as all the other userids you see there.
+    Finally run the command with --disable or --remove instead of --show.
+
+Detailed Instructions:
+======================
+General syntax: moin [options] account inactive [inactive-options]
+
+[options] usually should be:
+    --config-dir=/path/to/my/cfg/ --wiki-url=http://wiki.example.org/
+"""
+
+    def __init__(self, argv, def_values):
+        MoinScript.__init__(self, argv, def_values)
+        self.parser.add_option(
+            "--py-append", metavar="FILENAME", dest="py_append_file",
+            help="Append python code with editlog user ids."
+        )
+        self.parser.add_option(
+            "--py-exec", metavar="FILENAME", dest="py_exec_file",
+            help="Execute python code to read editlog user ids."
+        )
+        self.parser.add_option(
+            "--show", dest="show", action="store_true",
+            help="Show all inactive users."
+        )
+        self.parser.add_option(
+            "--disable", dest="disable", action="store_true",
+            help="Disable all inactive users."
+        )
+        self.parser.add_option(
+            "--remove", dest="remove", action="store_true",
+            help="Remove all inactive users."
+        )
+        self.parser.add_option(
+            "--interactive", dest="interactive", action="store_true",
+            help="Show data and ask before deleting/disabling users."
+        )
+
+    def mainloop(self):
+        argc = len(self.args)
+
+        self.init_request()
+        request = self.request
+
+        if self.options.py_append_file:
+            def logs(request):
+                """ generator for log objects """
+                # global edit-log
+                yield editlog.EditLog(request)
+                # local edit-log of every page
+                for pn in request.rootpage.getPageList(exists=False):
+                    yield editlog.EditLog(request, rootpagename=pn)
+
+            def uids(log):
+                """ generator for userids found in a log """
+                for line in log:
+                    uid = line.userid
+                    if uid:
+                        yield uid
+
+            fn = self.options.py_append_file
+            editlog_uids = set()
+            for log in logs(request):
+                editlog_uids |= set(uids(log))
+            with open(fn, "a") as f:
+                for uid in editlog_uids:
+                    u = User(request, uid)
+                    code = u'editlog_uids.add(%r)  # %r %r %r\n' % (
+                               uid, u.name, u.email, u.jid)
+                    f.write(code.encode('utf-8'))
+
+        elif self.options.py_exec_file:
+            def check_interactive(u):
+                if self.options.interactive:
+                    prompt = "%s %r %r %r disabled=%r (y/N)? " % (u.id, u.name, u.email, u.jid, u.disabled)
+                    return raw_input(prompt).strip() in ['y', 'Y', ]
+                else:
+                    return True
+
+            fn = self.options.py_exec_file
+            locs = dict(editlog_uids=set())
+            execfile(fn, {}, locs)
+            editlog_uids = locs.get('editlog_uids')
+
+            profile_uids = set(getUserList(request))
+
+            inactive_uids = profile_uids - editlog_uids
+            for uid in inactive_uids:
+                u = User(request, uid)
+                if self.options.show:
+                    print "%s\t%r\t%r\t%r" % (uid, u.name, u.email, u.disabled)
+                if self.options.disable:
+                    if check_interactive(u):
+                        u.disabled = 1
+                        u.save()
+                elif self.options.remove:
+                    if check_interactive(u):
+                        u.remove()
+
--- a/MoinMoin/user.py	Fri Jan 25 16:34:37 2013 +0100
+++ b/MoinMoin/user.py	Fri Jan 25 23:01:33 2013 +0100
@@ -451,6 +451,10 @@
         """
         return os.path.exists(self.__filename())
 
+    def remove(self):
+        """ Remove user profile from disk """
+        os.remove(self.__filename())
+
     def load_from_id(self, password=None):
         """ Load user account data from disk.
 
--- a/docs/CHANGES	Fri Jan 25 16:34:37 2013 +0100
+++ b/docs/CHANGES	Fri Jan 25 23:01:33 2013 +0100
@@ -99,6 +99,10 @@
 
     password_checker = lambda cfg, request, name, pw: multiconfig._default_password_checker(cfg, request, name, pw, min_length=10, min_different=7)
 
+  * Removing/disabling inactive users (moin ... acount inactive)
+    Many wikis have a lot of inactive users, that never ever made a single edit.
+    See help of the command for more details, be careful.
+
   Fixes:
   * logging: if the logging config file can't be read, give a helpful error msg
   * logging: use info loglevel (not warning) for telling about using the builtin