1#!/usr/bin/env python
2"""
3Migration from moin--main--1.3 pre patch-332 to post patch-332.
4
5In patch-332 we changed the format of page lists in user data file. They
6are now tab separated instead of comma separated, and page names are not
7quoted using file system quoting.
8
9You can run the script multiple times with no damage.
10
11
12Steps for a successful migration:
13
14 1. Stop your wiki
15
16 2. Make a backup of your wiki 'data' directory
17
18    WARNING: THIS SCRIPT MIGHT CORRUPT YOUR 'DATA' DIRECTORY. DON'T
19    COMPLAIN LATER, MAKE BACKUP NOW!
20
21 3. Move the wiki's 'data' directory to your working dir
22
23 4. Run this script from your working dir
24
25 5. If there was no error, you will find:
26    data.pre-mig9   - backup of original data directory
27    data            - converted data dir
28
29 6. Verify conversion results (number of pages, size of logs,
30    attachments, number of backup copies) - everything should be
31    reasonable before you proceed.
32
33    NOTE: THE CACHE DIRECTORY IS NOT COPIED - DO NOT COPY IT, IT WILL BE
34    CREATED AND FILLED BY THE WIKI AUTOMATICALLY.
35
36 7. Move the converted data directory into your wiki. Do not simply copy
37    the converted stuff into the original or you will duplicate pages
38    and create chaos!
39
40 8. Fix permissions on your data directory, see HelpOnInstalling.
41
42 9. Test it - if something has gone wrong, you still have your backup.
43
44
45@copyright: 2004 Thomas Waldmann
46@license: GPL, see COPYING for details
47"""
48
49import os, sys, codecs
50join = os.path.join
51
52# Insert THIS moin dir first into sys path, or you might run another
53# version of moin and get unpredicted results!
54sys.path.insert(0, '../../../..')
55
56from MoinMoin import wikiutil, user
57from MoinMoin.script.migration import migutil
58
59
60def convert_quicklinks(string):
61    """ Convert quicklinks from pre patch-332 to new format """
62    # No need to convert new style list
63    if '\t' in string:
64        return string
65
66    names = [name.strip() for name in string.split(',')]
67    names = [wikiutil.unquoteWikiname(name) for name in names if name != '']
68    string = user.encodeList(names)
69    return string
70
71
72def convert_subscribed_pages(string):
73    """ Convert subscribed pages from pre patch-332 to new format """
74    # No need to convert new style list
75    if '\t' in string:
76        return string
77
78    # This might break pages that contain ',' in the name, we can't do
79    # anything about it. This was the reason we changed the format.
80    names = [name.strip() for name in string.split(',')]
81    string = user.encodeList(names)
82    return string
83
84
85def convertUserData(text):
86    """ Convert user data
87
88    @param text: text of user file, unicode
89    @rtype: unicode
90    @return: convected user data
91    """
92    lines = text.splitlines()
93    for i in range(len(lines)):
94        line = lines[i]
95        try:
96            key, value = line.split('=', 1)
97        except ValueError:
98            continue
99        if key == u'quicklinks':
100            value = convert_quicklinks(value)
101        elif key == u'subscribed_pages':
102            value = convert_subscribed_pages(value)
103        lines[i] = u'%s=%s' % (key, value)
104
105    # Join back, append newline to last line
106    text = u'\n'.join(lines) + u'\n'
107    return text
108
109
110def convertUsers(srcdir, dstdir):
111    """ Convert users files
112
113    @param srcdir: old users dir
114    @param dstdir: new users dir
115    """
116    charset = 'utf-8'
117
118    # Create dstdir
119    if not os.path.exists(dstdir):
120        try:
121            os.mkdir(dstdir)
122        except OSError:
123            migutil.fatalError("can't create user directory at '%s'" % dstdir)
124
125    if not os.path.isdir(srcdir):
126        migutil.fatalError("can't find user directory at '%s'" % srcdir)
127
128    for name in migutil.listdir(srcdir):
129        if name == 'README' or name.endswith('.trail'):
130            # Copy as is
131            migutil.copy_file(join(srcdir, name), join(dstdir, name))
132        else:
133            srcfile = join(srcdir, name)
134            f = codecs.open(srcfile, 'rb', charset)
135            text = f.read()
136            f.close()
137            text = convertUserData(text)
138            dstfile = join(dstdir, name)
139            f = codecs.open(dstfile, 'wb', charset)
140            f.write(text)
141            f.close()
142            print "Converted '%s' to '%s'" % (srcfile, dstfile)
143
144
145if __name__ == '__main__':
146
147    # Backup original dir
148    datadir = 'data'
149    origdir = 'data.pre-mig9'
150    migutil.backup(datadir, origdir)
151
152    # Copy ALL stuff from original dir into new data dir. Don't change
153    # or drop anything from the original directory expect cache files.
154    names = ['edit-log', 'event-log', 'intermap.txt', 'pages', 'plugin']
155    migutil.copy(names, origdir, datadir)
156
157    # Convert user directory
158    convertUsers(join(origdir, 'user'), join(datadir, 'user'))
159
160