Contents
The request: I like to send out invitations to a wiki
I think it would be nice if I do not have to send people a mail that tells them to register. I rather would like to preregister them (give them a username) or to send them an email with an activation key.
This would also allow the superuser maybe to restrict registration to only those that were invited by him. Maybe it would also be possible to send many invitations at once to different mail addresses.
Yes, please, this is an extremely useful feature! -- AlvaroTejero
- This should make the registration process easier.
related requests
FeatureRequests/LoginHintToSendPasswordonlyWithoutUserCreation
FeatureRequests/DisableUserCreation
No Solution for v. 1.8.1-1
user.py and action/newaccount.py need patching. I think a new action (invite) would be better (still need to change user.py), That way actions_excluded can be used to exclude newaccount (see FeatureRequests/DisableUserCreation). -- Ro 2009-02-03 00:38:08
Solution for v. 1.7.2
Here is the step-by-step-instruction:
Create "MakeInvitation" page by using this example MakeInvitation_v1.7.2.txt. You can use ACL on this page to restrict rights, but MakeInvitation macro have additional check.
Copy "MakeInvitation.py" macro (MakeInvitation_v1.7.2.py) to macro folder of your moin instance
Logon as superuser, load "MakeInvitation" page and follow instructions on page. Here is screenshot of this page:
I haven't change anything in user.py and userform.py (in 1.7.2 there isn't such file) and I haven't tested mailing part of this stuff because I haven't SMTP right now (sorry). But according to changes that have been made in 1.7.2 it seems to be work correctly without changing of user.py.
-- Renard 2008-11-20
Patch for Moin 1.5.4
For a solution using the command line for invitations see FeatureRequests/MoinSendMailAndCreateShouldCreateAlwaysAPassword and the given patch there.
For an integrated solution in the wiki see as follows:
Here are some patches for Moin1.5.4-1 - as far as I could do that with my limited knowledge on the Moin code, python (maybe the code is not PEP8 compatible)... Moin has already some built-in-function to create-account-and-email-data but this part of the code has never been used. So I tried to turn this part into a create-account-and-make-invitation feature. It worked on my system but is not heavily tested. Here is the step-by-step-instruction for patching:
Step 1: Download the macro MakeInvitation.py and install it in Moin's macro directory.
Step 2: Create a new page MakeInvitation in the underlay_dir as follows:
## Please edit system and help pages ONLY in the moinmaster wiki! For more ## information, please see MoinMaster:MoinPagesEditorGroup. ##master-page:Unknown-Page ##master-date:Unknown-Date #acl MoinPagesEditorGroup:read,write,delete,revert All:read #format wiki #language en [[MakeInvitation]] = Making an invitation = Please fill out '''[[GetText(Name)]]''' and '''[[GetText(Email)]]''' to create an account for a new user a send him an invitation to join the Wiki community. (!) It is best to choose a WikiName (like Firstname``Lastname) as username to get changes and signatures linked back to the user's Wiki``Homepage. (!) Moin generates automatically a 6-digit random password for the user. Please do overwrite it if you want to provide some other password for the user. If you click on '''[[GetText(Create Profile)]] + [[GetText(Email)]] ''', a new user profile will be created and an email will be sent to the user. If this button is missing, email notification is not enabled on your system. If you click on '''[[GetText(Cancel)]] ''', the input form will be cleared and a new password will be generated.
You may also change the acls of this page so that only superusers can view it. But this is not done here, but might be better.
Step 3 Patch user.py
1 --- user_orig.py 2006-07-01 20:56:00.000000000 +0200
2 +++ user.py 2006-11-25 16:38:42.000000000 +0100
3 @@ -936,7 +936,7 @@
4 markup = '%s:%s' % (wikiname, pagename.replace(" ","_"))
5 return markup
6
7 - def mailAccountData(self, cleartext_passwd=None):
8 + def mailAccountData(self, cleartext_passwd=None, invitation=False):
9 from MoinMoin.util import mail
10 from MoinMoin.wikiutil import getSysPage
11 _ = self._request.getText
12 @@ -955,7 +955,9 @@
13 self.enc_password = pwd
14 self.save()
15
16 - text = '\n' + _("""\
17 + if not invitation:
18 +
19 + text = '\n' + _("""\
20 Login Name: %s
21
22 Login Password: %s
23 @@ -964,7 +966,7 @@
24 """, formatted=False) % (
25 self.name, self.enc_password, self._request.getBaseURL(), getSysPage(self._request, 'UserPreferences').page_name)
26
27 - text = _("""\
28 + text = _("""\
29 Somebody has requested to submit your account data to this email address.
30
31 If you lost your password, please use the data below and just enter the
32 @@ -974,6 +976,32 @@
33 After successfully logging in, it is of course a good idea to set a new and known password.
34 """, formatted=False) + text
35
36 + else:
37 +
38 + text = '\n' + _("""\
39 +Login Name: %s
40 +
41 +Login Password: %s
42 +
43 +Login URL: %s/%s?action=login
44 +""", formatted=False) % (
45 + self.name, cleartext_passwd, self._request.getBaseURL(), getSysPage(self._request, 'UserPreferences').page_name)
46 +
47 + text = _("""\
48 +Welcome to the %(sitename)s Wiki!
49 +
50 +You are invited to join our wiki community and share your ideas with us.
51 +Please use the login name and password below to log in. To ease login, you can
52 +also use copy and paste.
53 +Please do also make sure that in your browser's settings cookies are enabled
54 +for the login url so that authentications does really work and is kept while
55 +navigating in the wiki.
56 +
57 +After successfully logging in, it is of course a good idea to set a new password
58 +of your choice.
59 +""", formatted=False) % {'sitename': self._cfg.sitename or "Wiki"} + text
60 +
61 +
62
63 subject = _('[%(sitename)s] Your wiki account data',
64 formatted=False) % {'sitename': self._cfg.sitename or "Wiki"}
Step 4 Patch userform.py
1 --- userform_orig.py 2006-04-25 01:19:00.000000000 +0200
2 +++ userform.py 2006-11-25 16:42:46.000000000 +0100
3 @@ -154,9 +154,12 @@
4 # save data
5 theuser.save()
6 if form.has_key('create_and_mail'):
7 - theuser.mailAccountData()
8 -
9 - result = _("User account created! You can use this account to login now...")
10 + result1 = _("User account created.") # ToDo: Add translation for this!!
11 + result2 = theuser.mailAccountData(form.get('password', [''])[0],True)
12 + result = "%s %s" % (result1, _(result2)) # This assumes that all msg from mailAccountData have translations
13 + else:
14 + result = _("User account created! You can use this account to login now...")
15 +
16 if _debug:
17 result = result + util.dumpFormData(form)
18 return result
19 @@ -477,7 +480,7 @@
20 _ = self._
21 self.make_form()
22
23 - if self.request.user.isSuperUser():
24 + if self.request.user.isSuperUser() and create_only == False:
25 ticket = wikiutil.createTicket()
26 self.make_row(_('Select User'), [self._user_select()])
27 self._form.append(html.INPUT(type="hidden", name="ticket", value="%s" % ticket))
28 @@ -588,22 +591,50 @@
29 ('create', _('Create Profile')),
30 ('cancel', _('Cancel')),
31 ]
32 - for key, label, type, length, textafter in self.cfg.user_form_fields:
33 - if key in ('name', 'password', 'password2', 'email'):
34 - self.make_row(_(label),
35 - [ html.INPUT(type=type, size=length, name=key,
36 - value=''),
37 - ' ', _(textafter), ])
38 + if create_only == False:
39 + for key, label, type, length, textafter in self.cfg.user_form_fields:
40 + if key in ('name', 'password', 'password2', 'email'):
41 + self.make_row(_(label),
42 + [ html.INPUT(type=type, size=length, name=key,
43 + value=''),
44 + ' ', _(textafter), ])
45 + else:
46 + # We are in invitation mode; create some dummy password
47 + from random import choice
48 + letters = "abcdefghijklmnopqrstuvwxyz"
49 + letters += "0123456789"
50 + pwd = ''
51 + for i in range(6):
52 + pwd += choice(letters)
53 +
54 + for key, label, type, length, textafter in self.cfg.user_form_fields:
55 + if key in ('name', 'password', 'password2', 'email'):
56 +
57 + if key == 'password' or key == 'password2':
58 + self.make_row(_(label),
59 + [ html.INPUT(type=type, size=length, name=key,
60 + value='%s' % pwd),
61 + ' ', _(textafter), ])
62 + else:
63 + self.make_row(_(label),
64 + [ html.INPUT(type=type, size=length, name=key,
65 + value=''),
66 + ' ', _(textafter), ])
67 +
68
69 if self.cfg.mail_enabled:
70 buttons.append(("account_sendmail", _('Mail me my account data')))
71
72 if create_only:
73 - buttons = [("create_only", _('Create Profile'))]
74 + # We don't want to have a create profile button in invitation mode
75 + #buttons = [("create_only", _('Create Profile'))]
76 + buttons = []
77 if self.cfg.mail_enabled:
78 buttons.append(("create_and_mail", "%s + %s" %
79 (_('Create Profile'), _('Email'))))
80
81 + buttons.append(('cancel', _('Cancel')))
82 +
83 # Add buttons
84 button_cell = []
85 for name, label in buttons:
userform_moin1-5-4-1_patched.py
Step 5 For full multilang support you have to add some translations, see comments in MakeInvitation.py
That's it! Maybe you are also interested in some related patch of mine: FeatureRequests/PossibilityToSetQuicklinksAndSubscriptionsOnAccountCreation. Both work nicely together.
-- OliverSiemoneit 2006-11-25 20:10:10
So you are stull using this version? Would be interested if this will work on 1.5.6? -- ThiloPfennig 2006-11-26 11:38:50
Yes, I will be interested in that, too. Try it out. The basic structure of Moin hasn't changed, so if the above patches are applied correctly, this will also work on 1.5.6. Take the commandline tool "diff3" to produce out of the original 1.5.4 files, my patched 1.5.4 files and the new files form 1.5.6 new patched 1.5.6 files. There shouldn't be any conflicts in merging these changes. -- OliverSiemoneit 2006-11-26 16:48:08
To the main developers: is this in time to enter 1.6.0?
Patch for moin-1-6-main-7c58e8af1a97
For Moin 1.6 I have moved a lot of stuff in the macro itself. To userform.py only minor changes to the eventhandling have to be done:
1 --- userform_old.py 2006-12-09 13:32:00.000000000 +0100
2 +++ userform.py 2006-12-10 22:32:04.000000000 +0100
3 @@ -142,11 +142,14 @@
4 return _("This email already belongs to somebody else.")
5
6 # save data
7 - theuser.save()
8 if form.has_key('create_and_mail'):
9 - theuser.mailAccountData()
10 -
11 - result = _("User account created! You can use this account to login now...")
12 + # ToDo: Add translation for _("User account created.")
13 + result1 = _("User account created.")
14 + result2 = theuser.mailAccountData(form.get('password', [''])[0], True)
15 + # This assumes that all msg from mailAccountData (result2) have translations
16 + result = "%s %s" % (result1, _(result2))
17 + else:
18 + result = _("User account created! You can use this account to login now...")
19 if _debug:
20 result = result + util.dumpFormData(form)
21 return result
The diff for the user.py looks complicated but changes are only made to the mailAccountData function. And these changes are mainly the mail text:
1 def mailAccountData(self, cleartext_passwd=None, invitation=False):
2 from MoinMoin.mail import sendmail
3 from MoinMoin.wikiutil import getSysPage
4 _ = self._request.getText
5
6 if not self.enc_password: # generate pw if there is none yet
7 from random import randint
8 import base64
9
10 charset = 'utf-8'
11 pwd = "%s%d" % (str(time.time()), randint(0, 65535))
12 pwd = pwd.encode(charset)
13
14 pwd = sha.new(pwd).digest()
15 pwd = '{SHA}%s' % base64.encodestring(pwd).rstrip()
16
17 self.enc_password = pwd
18 self.save()
19
20 if not invitation:
21 text = '\n' + _("""\
22 Login Name: %s
23
24 Login Password: %s
25
26 Login URL: %s/%s?action=login
27 """, formatted=False) % (
28 self.name, self.enc_password, self._request.getBaseURL(), getSysPage(self._request, 'UserPreferences').page_name)
29
30 text = _("""\
31 Somebody has requested to submit your account data to this email address.
32
33 If you lost your password, please use the data below and just enter the
34 password AS SHOWN into the wiki's password form field (use copy and paste
35 for that).
36
37 After successfully logging in, it is of course a good idea to set a new and known password.
38 """, formatted=False) + text
39
40 else:
41 text = '\n' + _("""\
42 Login Name: %s
43
44 Login Password: %s
45
46 Login URL: %s/%s?action=login
47 """, formatted=False) % (
48 self.name, cleartext_passwd, self._request.getBaseURL(), getSysPage(self._request, 'UserPreferences').page_name)
49
50 text = _("""\
51 Welcome to the %(sitename)s Wiki!
52
53 You are invited to join our wiki community and share your ideas with us.
54 Please use the login name and password below to log in. To ease login, you can
55 also use copy and paste.
56 Please do also make sure that in your browser's settings cookies are enabled
57 for the login url so that authentications does really work and is kept while
58 navigating in the wiki.
59
60 After successfully logging in, it is of course a good idea to set a new password
61 of your choice.
62 """, formatted=False) % {'sitename': self._cfg.sitename or "Wiki"} + text
63
64
65 subject = _('[%(sitename)s] Your wiki account data',
66 formatted=False) % {'sitename': self._cfg.sitename or "Wiki"}
67 mailok, msg = sendmail.sendmail(self._request, [self.email], subject,
68 text, mail_from=self._cfg.mail_from)
69 return msg
And here is the new macro, which does the whole display stuff now..
1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - MakeInvitation macro
4
5 Syntax:
6 [[MakeInvitation]]
7
8 Lot of code take from userform.py
9 MoinMoin - userform.py
10 @copyright: 2001-2004 by Jürgen Hermann <jh@web.de>
11 @license: GNU GPL, see COPYING for details.
12
13 MoinMoin - MakeInvitation Macro
14 @copyright: 2006 by Oliver Siemoneit
15 @license: GNU GPL, see COPYING for details.
16 """
17
18 from MoinMoin.widget import html
19
20
21 def make_row(table, label, cell, **kw):
22 """ Create a row in the form table.
23 """
24 table.append(html.TR().extend([
25 html.TD(**kw).extend([html.B().append(label), ' ']),
26 html.TD().extend(cell),
27 ]))
28 return table
29
30 def execute(macro, args):
31 request = macro.request
32 _ = request.getText
33 formatter = macro.formatter
34
35 # Check if user is superuser. If not: return with error msg
36 if not request.user.isSuperUser():
37 err = _('You are not allowed to perform this action.')
38 return err
39
40 sn = request.getScriptname()
41 pi = request.getPathinfo()
42 action = u"%s%s" % (sn, pi)
43 form = html.FORM(action=action)
44 table = html.TABLE(border="0")
45
46 # Create 6 digits dummy password
47 from random import choice
48 letters = "abcdefghijklmnopqrstuvwxyz"
49 letters += "0123456789"
50 pwd = ''
51 for i in range(6):
52 pwd += choice(letters)
53
54 # Add form fields
55 for key, label, type, length, textafter in request.cfg.user_form_fields:
56 if key in ('name', 'password', 'password2', 'email'):
57
58 if key == 'password' or key == 'password2':
59 table = make_row(table, _(label),
60 [ html.INPUT(type=type, size=length, name=key,
61 value='%s' % pwd),
62 ' ',])
63 else:
64 table = make_row(table, _(label),
65 [ html.INPUT(type=type, size=length, name=key,
66 value=''),
67 ' ', _(textafter), ])
68
69 # Add buttons
70 buttons = []
71 if request.cfg.mail_enabled:
72 buttons.append(("create_and_mail", "%s + %s" %
73 (_('Create Profile'), _('Email'))))
74
75 buttons.append(('cancel', _('Cancel')))
76
77 button_cell = []
78 for name, label in buttons:
79 if not name in request.cfg.user_form_remove:
80 button_cell.extend([
81 html.INPUT(type="submit", name=name, value=label),
82 ' ',
83 ])
84 make_row(table,'', button_cell)
85
86
87 # Use the user interface language and direction
88 lang_attr = request.theme.ui_lang_attr()
89 form.append(html.Raw('<div class="userprefs"%s>' % lang_attr))
90 form.append(html.INPUT(type="hidden", name="action", value="userform"))
91 form.append(table)
92 form.append(html.Raw("</div>"))
93
94 return unicode(form)
The new underlay page form where the marco is called from is unchanged. You can get it for download here
Please note: I could not these the changes since at the moment I have only a moin standalone version with no mail functionality. These changes are ports of the given patch above and should theoretically work
This patch goes also nicely together with some others patches where I have tried to modularize the log in process:
FeatureRequests/WelcomeNewUserPage: a patch for a welcome screen after account creation which gives further information what a user can do now (create homepage, read the WikiQuicky...).
FeatureRequests/SeparateCreateAccountPage: a new create account page
FeatureRequests/LoginHintToSendPasswordonlyWithoutUserCreation: a new forgot-my-password-and-send-me-an-email page
Open problems:
- Patches work only nice if an automatic log in after account creation is there. But this is planned for 1.6
MakeInvitations and UserWelcomeScreen don't work together yet. Often you do send out emails for a precreated account when you have a lot of inexperienced users. Just in this case you would like when this user logs in for the first time, that he gets the welcome screen displayed giving him some guideline and orientation. Up to now WelcomeNewUser does onyl work if the user creates the account himself! I don't know how to implement the welcome screen on first log in. Maybe someone else can do this. I think this is also urgently needed.
-- OliverSiemoneit 2006-12-10 22:18:05
This Macro is great! I have a feature request for a future version of this macro. I currently have an ACL group called InvitedGroup. I use it to distiguish between people I specifically invited to our wiki verse those who register themselves. I Manually need to add those to InvitedGroup that I send an invite using MakeInvitation. I would be great if the macro did it automatically. Thanks again for a great extension! -- MauriceRabb 2007-01-14 17:47:44
see also FeatureRequests/DifferentMessageFromSuperuserMailAccountData -- ReimarBauer 2008-11-20 08:27:23