]> ToastFreeware Gitweb - philipp/winterrodeln/wradmin.git/blob - wradmin/wradmin/lib/mediawiki.py
30c8ba9aa638fb81b94e5b85db63dd551fe7ccfd
[philipp/winterrodeln/wradmin.git] / wradmin / wradmin / lib / mediawiki.py
1 #!/usr/bin/python2.6
2 # -*- coding: iso-8859-15 -*-
3 # $Id$
4 "MediaWiki communication functions"
5 import datetime
6 import re
7
8 from authkit.users import UsersReadOnly, md5
9 import formencode, formencode.national
10
11 import logging
12 log = logging.getLogger(__name__)
13
14 import wradmin.model as model
15 import wradmin.model.validators
16
17
18 # Converter functions
19 # -------------------
20
21 def to_title(value):
22     """Line 2237 of includes/Title.php says: $this->mTextform = str_replace( '_', ' ', $dbkey );
23     No not check for None because a missing title is an error"""
24     return value.replace(u'_', u' ')
25
26
27 # deprecated
28 def to_phone_info(value):
29     return model.validators.PhoneInfo(messages={'phoneInfo': u"Bitte verwenden Sie ein Format wie '0512/123456 (Schnee Alm)'."}).to_python(value)
30
31
32 def conv(fnct, value, fieldname):
33     "Like one of the to_xxx functions (e.g. to_bool), but adds the field name to the error message"
34     try: return fnct(value)
35     except formencode.Invalid, e: raise formencode.Invalid(u"Conversion error in field '%s': %s" % (fieldname, unicode_e(e)), e.value, e.state)
36
37
38 def unicode_e(exception):
39     """Does "unicode(exception)" as it should be. This is a workaround for bug http://bugs.python.org/issue2517
40     that is not fixed in python 2.5.2.
41     Details of bug: "unicode(Exception(u'\xe4'))" raises an UnicodeEncodeError exception."""
42     #if exception.message: return unicode(exception.message) # this bug is already fixed. TODO: Cleanup calls to unicode_e
43     return unicode(exception)
44
45
46 def wikipage_to_wrsleddingcache1_2(page_id, page_title, page_text):
47     """Converts a wiki page about a sledding route to a wradmin.model.WrSleddingCache1_2 class
48     that can be inserted to the wradmin.model.wrsleddingcache1_2_table.
49     It needs the wiki page id, the wiki page title and the page text ("old_text") as they come from the database."""
50     sl = model.WrSleddingCache1_2()
51     sl.page_id = page_id
52     sl.page_title = to_title(page_title)
53     
54     # Match Rodelbahnbox
55     wikitext = page_text
56     regexp = re.compile(u"\{\{(Rodelbahnbox[^\}]*)\}\}", re.DOTALL)
57     match = regexp.search(wikitext)
58     if not match:
59         raise Exception(u"No 'Rodelbahnbox' found")
60     box = match.group(1)
61     
62     # Process Rodelbahnbox
63     for property in box.split('|'):
64         property = property.strip()
65         if property == u'Rodelbahnbox': continue
66         key_value = property.split('=')
67         if len(key_value) != 2:
68             raise Exception(u"Property '%s' has unexpected format" % key_value)
69         key = key_value[0].strip()
70         value = key_value[1].strip()
71         if key == u'Rodelbahnnummer': pass
72         elif key == u'Länge': sl.length = conv(model.validators.Unsigned().to_python, value, u'Länge')
73         elif key == u'Gehzeit': sl.walktime = conv(model.validators.Unsigned().to_python, value, u'Gehzeit')
74         elif key == u'Höhe oben': sl.height_top = conv(model.validators.Unsigned().to_python, value, u'Höhe oben')
75         elif key == u'Höhe unten': sl.height_bottom = conv(model.validators.Unsigned().to_python, value, u'Höhe unten')
76         elif key == u'Aufstieg getrennt': sl.walkup_separate = conv(model.validators.GermanBoolNone().to_python, value, u'Aufstieg getrennt')
77         elif key == u'Lift': sl.lift = conv(model.validators.GermanBoolNone().to_python, value, u'Lift')
78         elif key == u'Beleuchtung': sl.night_light = conv(model.validators.GermanBoolNone().to_python, value, u'Beleuchtung')
79         elif key == u'Rodelverleih': sl.sledge_rental = conv(model.validators.GermanBoolNone().to_python, value, u'Rodelverleih')
80         elif key == u'Öffentliche Anreise': sl.public_transport = conv(model.validators.GermanBoolNone().to_python, value, u'Öffentliche Anreise')
81         elif key == u'Bild': sl.image = conv(model.validators.UnicodeNone().to_python, value, key)
82         elif key == u'Position': (sl.position_latitude, sl.position_longitude) = conv(model.validators.GeoNone().to_python, value, u'Position') # '47.583333 N 15.75 E'
83         elif key == u'Auskunft': sl.information = conv(model.validators.AustrianPhoneNumberCommentLoop().to_python, value, u'Auskunft')
84         elif key == u'In Übersichtskarte': sl.show_in_overview = conv(model.validators.GermanBoolNone().to_python, value, u'In Übersichtskarte')
85         elif key == u'Aufnahmedatum': sl.creation_date = conv(model.validators.DateNone().to_python, value, u'Aufnahmedatum') # '2006-03-15'
86         elif key == u'Lawinengefahr':
87             # sl.avalanches is not part of the 1.2 sleddingcache table. We store it in the WrSleddingCache1_2 anyway.
88             sl.avalanches = conv(model.validators.GermanAvalanches().to_python, value, key)
89         else: raise formencode.Invalid(u"Unbekannte Eigenschaft der Rodelbahnbox: '%s' (mit Wert '%s')" % (key, value), value, None)
90     sl.under_construction = None
91     
92     # Match Forumlink (e.g. {{Forumlink|68}})
93     match = re.search(u"\{\{Forumlink\|(\d+)\}\}", wikitext)
94     if match: sl.forum_id = match.group(1)
95     
96     return sl
97
98
99 def wikipage_to_wrsleddingcache(page_id, page_title, page_text):
100     """Converts a sled-route wiki page (wradmin.model.page_table) 
101     to a sledding route wrsleddingcache database record (wradmin.model.wrsleddingcache_table).
102     Raises a RuntimeError if the format is not OK
103     sledding_wiki is a column of tabe "page".
104     Returns the WrSleddingCache class"""
105     sl = model.WrSleddingCache()
106     sl.page_id = page_id
107     sl.page_title = to_title(page_title)
108     errors = [] # List of errors with localized messages
109     
110     # Match Rodelbahnbox
111     wikitext = page_text
112     regexp = re.compile(u"\{\{(Rodelbahnbox[^\}]*)\}\}", re.DOTALL)
113     match = regexp.search(wikitext)
114     if not match:
115         raise RuntimeError(u"Rodelbahnbox nicht gefunden")
116     box = match.group(1)
117     
118     # Process Rodelbahnbox
119     for property in box.split('|'):
120         property = property.strip()
121         if property == u'Rodelbahnbox': continue
122         equalsign_pos = property.find('=')
123         if equalsign_pos < 0:
124             raise RuntimeError(u"Die Eigenschaft '%s' hat ein unerwartetes Format." % property)
125         key = property[:equalsign_pos].strip()
126         value = property[equalsign_pos+1:].strip()
127         if key in [u'Rodelbahnnummer', u'Lift']:
128             errors.append("Eigenschaft '%s' wird nicht mehr unterstuetzt, siehe %s." % (key, 'http://www.winterrodeln.org/wiki/Vorlage:Rodelbahnbox'))
129         elif key == u'Position': sl.position_latitude, sl.position_longitude = conv(model.validators.GeoNone().to_python, value, key) # '47.583333 N 15.75 E'
130         elif key == u'Position oben': sl.top_latitude, sl.top_longitude = conv(model.validators.GeoNone().to_python, value, key) # '47.583333 N 15.75 E'
131         elif key == u'Höhe oben': sl.top_elevation = conv(model.validators.UnsignedNone().to_python, value, key) # '2000'
132         elif key == u'Position unten': sl.bottom_latitude, sl.bottom_longitude = conv(model.validators.GeoNone().to_python, value, key) # '47.583333 N 15.75 E'
133         elif key == u'Höhe unten': sl.bottom_elevation = conv(model.validators.UnsignedNone().to_python, value, key) # '1200'
134         elif key == u'Länge': sl.length = conv(model.validators.UnsignedNone().to_python, value, key) # 3500
135         elif key == u'Schwierigkeit': sl.difficulty = conv(model.validators.GermanDifficulty().to_python, value, key) # 'mittel'
136         elif key == u'Lawinen': sl.avalanches = conv(model.validators.GermanAvalanches().to_python, value, key) # 'kaum'
137         elif key == u'Betreiber': sl.operator = conv(model.validators.UnicodeNone().to_python, value, key) # 'Max Mustermann'
138         elif key == u'Öffentliche Anreise': sl.public_transport = conv(model.validators.GermanPublicTransport().to_python, value, key) # 'Mittelmäßig'
139         elif key == u'Gehzeit': sl.walkup_time = conv(model.validators.UnsignedNone().to_python, value, key) # 90
140         elif key == u'Aufstieg getrennt': sl.walkup_separate, sl.walkup_separate_comment = conv(model.validators.GermanTristateFloatComment().to_python, value, key) # 'Ja'
141         elif key == u'Aufstiegshilfe': sl.lift, sl.lift_details = conv(model.validators.GermanLift().to_python, value, key) # 'Gondel (unterer Teil)'
142         elif key == u'Beleuchtungsanlage': sl.night_light, sl.night_light_comment = conv(model.validators.GermanTristateFloatComment().to_python, value, key)
143         elif key == u'Beleuchtungstage': sl.night_light_days, sl.night_light_days_comment = conv(model.validators.UnsignedCommentNone(7).to_python, value, key) # '3 (Montag, Mittwoch, Freitag)'
144         elif key == u'Rodelverleih': sl.sled_rental, sl.sled_rental_comment = conv(model.validators.SledRental().to_python, value, key) # 'Talstation Serlesbahnan'
145         elif key == u'Gütesiegel': sl.cachet = conv(model.validators.GermanCachet().to_python, value, key) # 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'
146         elif key == u'Webauskunft': sl.information_web = conv(model.validators.UrlNeinNone().to_python, value, key) # 'http://www.nösslachhütte.at/page9.php'
147         elif key == u'Telefonauskunft': sl.information_phone = conv(model.validators.PhoneCommentListNeinLoopNone(comments_are_optional=False).to_python, value, key) # '+43-664-5487520 (Mitterer Alm)'
148         elif key == u'Bild': sl.image = conv(model.validators.UnicodeNone().to_python, value, key)
149         elif key == u'In Übersichtskarte': sl.show_in_overview = conv(model.validators.GermanBoolNone().to_python, value, key)
150         elif key == u'Forumid': sl.forum_id = conv(model.validators.UnsignedNeinNone().to_python, value, key)
151         else: raise formencode.Invalid(u"Unbekannte Eigenschaft der Rodelbahnbox: '%s' (mit Wert '%s')" % (key, value), value, None)
152     sl.under_construction = None
153     return sl
154
155
156 def wrSleddingCache1_2_to_WrSleddingCache(wrSleddingCache1_2):
157     """Converts the old WrSleddingCache format (1.2) WrSleddingCache1_2
158     to the new format (1.3) WrSleddingCache."""
159     wrSleddingCache = model.WrSleddingCache() # Create an object in the new format
160     wrSleddingCache.page_id = wrSleddingCache1_2.page_id
161     wrSleddingCache.page_title = wrSleddingCache1_2.page_id
162     wrSleddingCache.position_latitude = wrSleddingCache1_2.position_latitude
163     wrSleddingCache.position_longitude = wrSleddingCache1_2.position_longitude
164     wrSleddingCache.top_latitude = None
165     wrSleddingCache.top_longitude = None
166     wrSleddingCache.top_elevation = wrSleddingCache1_2.height_top
167     wrSleddingCache.bottom_latitude = None
168     wrSleddingCache.bottom_longitude = None
169     wrSleddingCache.bottom_elevation = wrSleddingCache1_2.height_bottom
170     wrSleddingCache.length = wrSleddingCache1_2.length
171     wrSleddingCache.difficulty = None
172     if 'avalanches' in dir(wrSleddingCache1_2): wrSleddingCache.avalanches = wrSleddingCache1_2.avalanches
173     else: wrSleddingCache.avalanches = None
174     wrSleddingCache.operator = None
175     if wrSleddingCache1_2.public_transport is None: wrSleddingCache.public_transport = None
176     else: wrSleddingCache.public_transport = 6 if wrSleddingCache1_2.public_transport else 5
177     wrSleddingCache.walkup_time = wrSleddingCache1_2.walktime
178     if wrSleddingCache1_2.walkup_separate is None: wrSleddingCache.walkup_separate = None
179     wrSleddingCache.walkup_separate = 1.0 if wrSleddingCache1_2.walkup_separate else 0.0
180     wrSleddingCache.walkup_separate_comment = None
181     wrSleddingCache.lift = wrSleddingCache1_2.lift
182     if wrSleddingCache1_2.lift is None: wrSleddingCache.lift_details = None
183     elif wrSleddingCache1_2.lift: wrSleddingCache.lift_details = "Sonstige"
184     else: wrSleddingCache.lift_details = None
185     if wrSleddingCache1_2.night_light is None: wrSleddingCache.night_light = None
186     else: wrSleddingCache.night_light = 1.0 if wrSleddingCache1_2.night_light else 0.0
187     wrSleddingCache.night_light_comment = None
188     wrSleddingCache.night_light_days = None
189     wrSleddingCache.night_light_days_comment = None
190     wrSleddingCache.sled_rental = wrSleddingCache1_2.sledge_rental
191     if wrSleddingCache.sled_rental: wrSleddingCache.sled_rental_comment = u'Ja'
192     else: wrSleddingCache.sled_rental_comment = None
193     wrSleddingCache.cachet = None
194     wrSleddingCache.information_web = None
195     if wrSleddingCache1_2.information is None: wrSleddingCache.information_phone = None
196     else:
197         m = re.match('^([-\d/\+]{5,}) \((.+)\)', wrSleddingCache1_2.information)
198         if m is None: raise formencode.Invalid('PhoneInfo is invalid', value, None)
199         (phone, info) = m.groups()
200         # check phone
201         phone = wradmin.model.validators.AustrianPhoneNumber().to_python(phone)
202         # convert phone
203         c = formencode.national.InternationalPhoneNumber(default_cc=lambda: 43)
204         phone = c.to_python(phone)
205         wrSleddingCache.information_phone = '%s (%s)' % (phone, info)
206     wrSleddingCache.image = wrSleddingCache1_2.image
207     wrSleddingCache.show_in_overview = wrSleddingCache1_2.show_in_overview
208     wrSleddingCache.forum_id = wrSleddingCache1_2.forum_id
209     wrSleddingCache.under_construction = wrSleddingCache1_2.under_construction
210     return wrSleddingCache
211
212
213 def wrSleddingCache_to_Rodelbahnbox(wrSleddingCache):
214     """Converts the WrSleddingCache class to the {{Rodelbahnbox}} representation."""
215     keys = []
216     values = []
217     keys.append(u'Position')
218     values.append(model.validators.GeoNone().from_python((wrSleddingCache.position_latitude, wrSleddingCache.position_longitude)))
219     keys.append(u'Position oben')
220     values.append(model.validators.GeoNone().from_python((wrSleddingCache.top_latitude, wrSleddingCache.top_longitude)))
221     keys.append(u'Höhe oben')
222     values.append(model.validators.UnsignedNone().from_python(wrSleddingCache.top_elevation))
223     keys.append(u'Position unten')
224     values.append(model.validators.GeoNone().from_python((wrSleddingCache.bottom_latitude, wrSleddingCache.bottom_longitude)))
225     keys.append(u'Höhe unten')
226     values.append(model.validators.UnsignedNone().from_python(wrSleddingCache.bottom_elevation))
227     keys.append(u'Länge')
228     values.append(model.validators.UnsignedNone().from_python(wrSleddingCache.length))
229     keys.append(u'Schwierigkeit')
230     values.append(model.validators.GermanDifficulty().from_python(wrSleddingCache.difficulty))
231     keys.append(u'Lawinen')
232     values.append(model.validators.GermanAvalanches().from_python(wrSleddingCache.avalanches))
233     keys.append(u'Betreiber')
234     values.append(model.validators.UnicodeNone().from_python(wrSleddingCache.operator))
235     keys.append(u'Öffentliche Anreise')
236     values.append(model.validators.GermanPublicTransport().from_python(wrSleddingCache.public_transport))
237     keys.append(u'Gehzeit')
238     values.append(model.validators.UnsignedNone().from_python(wrSleddingCache.walkup_time))
239     keys.append(u'Aufstieg getrennt')
240     values.append(model.validators.GermanTristateFloatComment().from_python((wrSleddingCache.walkup_separate, wrSleddingCache.walkup_separate_comment)))
241     keys.append(u'Aufstiegshilfe')
242     values.append(model.validators.GermanLift().from_python((wrSleddingCache.lift, wrSleddingCache.lift_details)))
243     keys.append(u'Beleuchtungsanlage')
244     values.append(model.validators.GermanTristateFloatComment().from_python((wrSleddingCache.night_light, wrSleddingCache.night_light_comment)))
245     keys.append(u'Beleuchtungstage')
246     values.append(model.validators.UnsignedCommentNone(max=7).from_python((wrSleddingCache.night_light_days, wrSleddingCache.night_light_days_comment)))
247     keys.append(u'Rodelverleih')
248     values.append(model.validators.SledRental().from_python((wrSleddingCache.sled_rental, wrSleddingCache.sled_rental_comment)))
249     keys.append(u'Gütesiegel')
250     values.append(model.validators.GermanCachet().from_python(wrSleddingCache.cachet))
251     keys.append(u'Webauskunft')
252     values.append(model.validators.UrlNeinNone().from_python(wrSleddingCache.information_web))
253     keys.append(u'Telefonauskunft')
254     values.append(model.validators.PhoneCommentListNeinLoopNone(comments_are_optional=False).from_python(wrSleddingCache.information_phone))
255     keys.append(u'Bild')
256     values.append(model.validators.UnicodeNone().from_python(wrSleddingCache.image))
257     keys.append(u'In Übersichtskarte')
258     values.append(model.validators.GermanBoolNone().from_python(wrSleddingCache.show_in_overview))
259     keys.append(u'Forumid')
260     values.append(model.validators.UnsignedNeinNone().from_python(wrSleddingCache.forum_id))
261     result = [u'{{Rodelbahnbox']
262     for i in xrange(len(keys)): result.append(u'| %-20s = %s' % (keys[i], values[i]))
263     result.append('}}')
264     return '\n'.join(result)
265
266
267 def wikipage_to_wrinncache1_2(page_id, page_title, page_text):
268     """Converts a wiki page about an inn to an wradmin.model.WrInnCache1_2 class
269     that can be inserted to the wradmin.model.wrinncache1_2_table.
270     It uses only text operations and does not query or update the database.
271     It needs the wiki page id, the wiki page title and the page text ("old_text") as they come from the database."""
272     inn = model.WrInnCache1_2()
273     inn.page_id = page_id
274     inn.page_title = to_title(page_title)
275     
276     # Match Gasthausbox
277     wikitext = page_text
278     regexp = re.compile(u"\{\{(Gasthausbox[^\}]*)\}\}", re.DOTALL)
279     match = regexp.search(wikitext)
280     if not match:
281         raise Exception(u"No 'Gasthausbox' found")
282     box = match.group(1)
283     
284     # Process Gashausbox
285     for property in box.split('|'):
286         property = property.strip()
287         if property == u'Gasthausbox': continue
288         key_value = property.split('=')
289         if len(key_value) != 2:
290             raise Exception(u"Property '%s' has unexpected format" % key_value)
291         key = key_value[0].strip()
292         value = key_value[1].strip()
293         if key == u'Gasthausnummer': pass
294         elif key == u'E-Mail': inn.email = conv(formencode.validators.Email().to_python, value, u'E-Mail')
295         elif key == u'Homepage': inn.homepage = conv(model.validators.UrlNeinNone().to_python, value, u'Homepage')
296         elif key == u'Höhe': inn.height = conv(model.validators.Unsigned().to_python, value, u'Höhe')
297         elif key == u'Bild': inn.image = conv(model.validators.UnicodeNone().to_python, value, key)
298         elif key == u'Position': inn.position_latitude, inn.position_longitude = conv(model.validators.GeoNone().to_python, value, u'Position') # '47.583333 N 15.75 E'
299         elif key == u'Telefon (Festnetz)': inn.phone = conv(model.validators.AustrianPhoneNumberNone().to_python, value, u'Telefon (Festnetz)')
300         elif key == u'Telefon (Mobil)': inn.mobile_phone = conv(model.validators.AustrianPhoneNumberNone().to_python, value, u'Telefon (Mobil)')
301         elif key == u'Rauchfrei': inn.nonsmoker_area, inn.smoker_area = conv(model.validators.GermanTristateTuple().to_python, value, u'Rauchfrei')
302         elif key == u'Aufnahmedatum': inn.creation_date = conv(model.validators.DateNone().to_python, value, u'Aufnahmedatum') # '2006-03-15'
303         else: raise formencode.Invalid(u"Unbekannte Eigenschaft der Gasthausbox: '%s' (mit Wert '%s')" % (key, value), value, None)
304     inn.under_construction = None
305     return inn
306
307
308 def wikipage_to_wrinncache(page_id, page_title, page_text):
309     """Converts a inn wiki page (wradmin.model.page_table) to a wrinncache database record
310     (wradmin.model.wrinncache_table)."""
311     inn = model.WrInnCache()
312     inn.page_id = page_id
313     inn.page_title = to_title(page_title)
314     
315     # Match Gasthausbox
316     wikitext = page_text
317     regexp = re.compile(u"\{\{(Gasthausbox[^\}]*)\}\}", re.DOTALL)
318     match = regexp.search(wikitext)
319     if not match:
320         raise Exception(u"No 'Gasthausbox' found")
321     box = match.group(1)
322     
323     # Process Gashausbox
324     for property in box.split('|'):
325         property = property.strip()
326         if property == u'Gasthausbox': continue
327         key_value = property.split('=')
328         if len(key_value) != 2:
329             raise Exception(u"Property '%s' has unexpected format" % key_value)
330         key = key_value[0].strip()
331         value = key_value[1].strip()
332         if key == u'Position': inn.position_latitude, inn.position_longitude = conv(model.validators.GeoNone().to_python, value, key) # '47.583333 N 15.75 E'
333         elif key == u'Höhe': inn.position_elevation = conv(model.validators.UnsignedNone().to_python, value, key)
334         elif key == u'Betreiber': inn.operator = conv(model.validators.UnicodeNone().to_python, value, key)
335         elif key == u'Sitzplätze': inn.seats = conv(model.validators.UnsignedNone().to_python, value, key)
336         elif key == u'Übernachtung': inn.overnight, inn.overnight_comment = conv(model.validators.BoolUnicodeTupleValidator().to_python, value, key)
337         elif key == u'Rauchfrei': inn.nonsmoker_area, inn.smoker_area = conv(model.validators.GermanTristateTuple().to_python, value, key)
338         elif key == u'Rodelverleih': inn.sled_rental, inn.sled_rental_comment = conv(model.validators.BoolUnicodeTupleValidator().to_python, value, key)
339         elif key == u'Handyempfang': inn.mobile_provider = conv(model.validators.ValueCommentListNeinLoopNone().to_python, value, key)
340         elif key == u'Homepage': inn.homepage = conv(model.validators.UrlNeinNone().to_python, value, key)
341         elif key == u'E-Mail': inn.email_list = conv(model.validators.EmailCommentListNeinLoopNone().to_python, value, key)
342         elif key == u'Telefon': inn.phone_list = conv(model.validators.PhoneCommentListNeinLoopNone(comments_are_optional=True).to_python, value, key)
343         elif key == u'Bild': inn.image = conv(model.validators.UnicodeNone().to_python, value, key)
344         elif key == u'Rodelbahnen': inn.sledding_list = conv(model.validators.WikiPageListLoopNone().to_python, value, key)
345         else: raise formencode.Invalid(u"Unbekannte Eigenschaft der Gasthausbox: '%s' (mit Wert '%s')" % (key, value), value, None)
346     inn.under_construction = None
347     return inn
348
349
350 def wrInnCache1_2_to_WrInnCache(wrInnCache1_2):
351     """Converts the old WrInnCache format to the new format."""
352     wrInnCache = model.WrInnCache() # Create an object in the new format
353     wrInnCache.page_id = wrInnCache1_2.page_id
354     wrInnCache.page_title = wrInnCache1_2.page_id
355     wrInnCache.position_latitude = wrInnCache1_2.position_latitude
356     wrInnCache.position_longitude = wrInnCache1_2.position_longitude
357     wrInnCache.position_elevation = wrInnCache1_2.height
358     wrInnCache.operator = None
359     wrInnCache.seats = None
360     wrInnCache.overnight = None
361     wrInnCache.overnight_comment = None
362     wrInnCache.smoker_area = wrInnCache1_2.smoker_area
363     wrInnCache.nonsmoker_area = wrInnCache1_2.nonsmoker_area
364     wrInnCache.sled_rental = None
365     wrInnCache.sled_rental_comment = None
366     wrInnCache.mobile_provider = None
367     wrInnCache.homepage = wrInnCache1_2.homepage
368     wrInnCache.email_list = wrInnCache1_2.email
369     phone_list = []
370     c = formencode.national.InternationalPhoneNumber(default_cc=lambda: 43)
371     if not wrInnCache1_2.phone is None: phone_list.append(c.to_python(wrInnCache1_2.phone))
372     if not wrInnCache1_2.mobile_phone is None: phone_list.append(c.to_python(wrInnCache1_2.mobile_phone))
373     if len(phone_list) >= 1: wrInnCache.phone_list = "; ".join(phone_list)
374     else: phone_list = None
375     wrInnCache.image = wrInnCache1_2.image
376     wrInnCache.sledding_list = None
377     wrInnCache.under_construction = wrInnCache1_2.under_construction
378     return wrInnCache
379
380
381 def wrInnCache_to_Gasthausbox(wrInnCache):
382     """Converts the WrInnCache class to the {{Gasthausbox}} representation."""
383     keys = []
384     values = []
385     keys.append(u'Position')
386     values.append(model.validators.GeoNone().from_python((wrInnCache.position_latitude, wrInnCache.position_longitude)))
387     keys.append(u'Höhe')
388     values.append(model.validators.UnsignedNone().from_python(wrInnCache.position_elevation))
389     keys.append(u'Betreiber')
390     values.append(model.validators.UnicodeNone().from_python(wrInnCache.operator))
391     keys.append(u'Sitzplätze')
392     values.append(model.validators.UnsignedNone().from_python(wrInnCache.seats))
393     keys.append(u'Übernachtung')
394     values.append(model.validators.BoolUnicodeTupleValidator().from_python((wrInnCache.overnight, wrInnCache.overnight_comment)))
395     keys.append(u'Rauchfrei')
396     values.append(model.validators.GermanTristateTuple().from_python((wrInnCache.nonsmoker_area, wrInnCache.smoker_area)))
397     keys.append(u'Rodelverleih')
398     values.append(model.validators.BoolUnicodeTupleValidator().from_python((wrInnCache.sled_rental, wrInnCache.sled_rental_comment)))
399     keys.append(u'Handyempfang')
400     values.append(model.validators.ValueCommentListNeinLoopNone().from_python(wrInnCache.mobile_provider))
401     keys.append(u'Homepage')
402     values.append(model.validators.UrlNeinNone().from_python(wrInnCache.homepage))
403     keys.append(u'E-Mail')
404     values.append(model.validators.EmailCommentListNeinLoopNone().from_python(wrInnCache.email_list))
405     keys.append(u'Telefon')
406     values.append(model.validators.PhoneCommentListNeinLoopNone(comments_are_optional=True).from_python(wrInnCache.phone_list))
407     keys.append(u'Bild')
408     values.append(model.validators.UnicodeNone().from_python(wrInnCache.image))
409     keys.append(u'Rodelbahnen')
410     values.append(model.validators.WikiPageListLoopNone().from_python(wrInnCache.sledding_list))
411     result = [u'{{Gasthausbox']
412     for i in xrange(len(keys)): result.append(u'| %-17s = %s' % (keys[i], values[i]))
413     result.append('}}\n')
414     return '\n'.join(result)
415
416
417
418 # User management
419 # ---------------
420
421 class MediaWikiUsers(UsersReadOnly):
422     def __init__(self, data=None, encrypt=None):
423         UsersReadOnly.__init__(self, data, encrypt)
424
425         # Initialize class fields
426         self.usernames = []
427         self.passwords = {}
428         self.roles = {}
429         self.groups = {}
430         self.user_ids = {} # MediaWiki user_id field of the database
431         self.real_names = {} # Real names of the users
432         self.emails = {} # E-Mail addresses of the users
433         
434         # Query database
435         con = model.meta.engine.connect()
436         sql = "SELECT user_id, user_name, user_real_name, user_password, user_email FROM user, user_groups WHERE ug_user=user_id AND ug_group='beauftragte'"
437         result = con.execute(sql)
438         for row in result:
439             user_id, username, real_name, password, email = row
440             username = username.lower()
441             role = []
442             group = None
443             
444             self.usernames.append(username)
445             self.passwords[username] = password
446             self.roles[username] = role
447             self.groups[username] = group
448             self.user_ids[username] = user_id
449             self.real_names[username] = real_name
450             self.emails[username] = email
451         con.close()
452         log.info("%d users loaded from the MediaWiki database" % len(self.usernames))
453     
454     
455     def user_has_password(self, username, password):
456         """
457         Passwords are case sensitive.
458         Returns ``True`` if the user has the password specified, ``False`` otherwise. 
459         Raises an exception if the user doesn't exist.
460         
461         See http://www.winterrodeln.org/trac/wiki/MediaWikiAuthorization
462         """
463         pwd = self.user_password(username)
464         # Example: pwd = ':B:d25b2886:41e46c952790b1b442aac4f24f7ea7a8'
465         pwd_parts = pwd.split(':') # password_parts = ['', 'B', 'd25b2886', '41e46c952790b1b442aac4f24f7ea7a8']
466         if len(pwd_parts) == 4 and pwd_parts[1] == 'B':
467             salt, pwd_md5 = tuple(pwd_parts[2:4]) # salt = 'd25b2886'; pwd_md5 = '41e46c952790b1b442aac4f24f7ea7a8'
468         else:
469             raise AuthKitError("Password in the MediaWiki database format has an unexpected format ('%s' instead of e.g. ':B:d25b2886:41e46c952790b1b442aac4f24f7ea7a8')" % pwd)
470         # log.info("user: '%s'; md5 of salt+' '+entered_pwd: '%s'; md5-part of DB-pwd: %s" % (username, md5(salt + '-' + md5(password)), pwd_md5))
471         return md5(salt + '-' + md5(password)) == pwd_md5