diff --git a/gen/generator.py b/gen/generator.py
index f93ac6b..163e73c 100644
--- a/gen/generator.py
+++ b/gen/generator.py
@@ -487,6 +487,14 @@ class ZopeGenerator(Generator):
msg('enable_cookies', '', msg.ENABLE_COOKIES),
msg('page_previous', '', msg.PAGE_PREVIOUS),
msg('page_next', '', msg.PAGE_NEXT),
+ msg('forgot_password', '', msg.FORGOT_PASSWORD),
+ msg('ask_password_reinit', '', msg.ASK_PASSWORD_REINIT),
+ msg('reinit_mail_sent', '', msg.REINIT_MAIL_SENT),
+ msg('reinit_password', '', msg.REINIT_PASSWORD),
+ msg('reinit_password_body', '', msg.REINIT_PASSWORD_BODY),
+ msg('new_password', '', msg.NEW_PASSWORD),
+ msg('new_password_body', '', msg.NEW_PASSWORD_BODY),
+ msg('new_password_sent', '', msg.NEW_PASSWORD_SENT),
]
# Create a label for every role added by this application
for role in self.getAllUsedRoles():
diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py
index 2cf7149..ad38edb 100644
--- a/gen/mixins/ToolMixin.py
+++ b/gen/mixins/ToolMixin.py
@@ -4,11 +4,12 @@ from appy.shared import mimeTypes
from appy.shared.utils import getOsTempFolder, sequenceTypes
from appy.shared.data import languages
import appy.gen
-from appy.gen import Type, Search, Selection
+from appy.gen import Type, Search, Selection, String
from appy.gen.utils import SomeObjects, getClassName
from appy.gen.mixins import BaseMixin
from appy.gen.wrappers import AbstractWrapper
from appy.gen.descriptors import ClassDescriptor
+from appy.gen.mail import sendMail
try:
from AccessControl.ZopeSecurityPolicy import _noroles
except ImportError:
@@ -1042,4 +1043,68 @@ class ToolMixin(BaseMixin):
if hasattr(self.o.aq_base, 'pages') and self.o.pages:
return [self.getObject(uid) for uid in self.o.pages ]
return ()
+
+ def askPasswordReinit(self):
+ '''A user (anonymmous) does not remember its password. Here we will
+ send him a mail containing a link that will trigger password
+ re-initialisation.'''
+ login = self.REQUEST.get('login').strip()
+ appyTool = self.appy()
+ user = appyTool.search1('User', login=login, noSecurity=True)
+ msg = self.translate('reinit_mail_sent')
+ backUrl = self.REQUEST['HTTP_REFERER']
+ if not user:
+ # Return the message nevertheless. This way, malicious users can't
+ # deduce information about existing users.
+ return self.goto(backUrl, msg)
+ # If login is an email, use it. Else, use user.email instead.
+ email = user.login
+ if not String.EMAIL.match(email):
+ email = user.email
+ if not email:
+ # Impossible to re-initialise the password.
+ return self.goto(backUrl, msg)
+ # Create a temporary file whose name is the user login and whose
+ # content is a generated token.
+ f = file(os.path.join(getOsTempFolder(), login), 'w')
+ token = String().generatePassword()
+ f.write(token)
+ f.close()
+ # Send an email
+ initUrl = '%s/doPasswordReinit?login=%s&token=%s' % \
+ (self.absolute_url(), login, token)
+ subject = self.translate('reinit_password')
+ map = {'url':initUrl, 'siteUrl':self.getSiteUrl()}
+ body= self.translate('reinit_password_body', mapping=map, format='text')
+ sendMail(appyTool, email, subject, body)
+ return self.goto(backUrl, msg)
+
+ def doPasswordReinit(self):
+ '''Performs the password re-initialisation.'''
+ rq = self.REQUEST
+ login = rq['login']
+ token = rq['token']
+ # Check if such token exists in temp folder
+ tokenFile = os.path.join(getOsTempFolder(), login)
+ if os.path.exists(tokenFile):
+ f = file(tokenFile)
+ storedToken = f.read()
+ f.close()
+ if storedToken == token:
+ # Generate a new password for this user
+ appyTool = self.appy()
+ user = appyTool.search1('User', login=login, noSecurity=True)
+ newPassword = user.setPassword()
+ # Send the new password by email
+ email = login
+ if not String.EMAIL.match(email):
+ email = user.email
+ subject = self.translate('new_password')
+ siteUrl = self.getSiteUrl()
+ map = {'password': newPassword, 'siteUrl': siteUrl}
+ body = self.translate('new_password_body', mapping=map,
+ format='text')
+ sendMail(appyTool, email, subject, body)
+ os.remove(tokenFile)
+ return self.goto(siteUrl, self.translate('new_password_sent'))
# ------------------------------------------------------------------------------
diff --git a/gen/po.py b/gen/po.py
index b7906da..62e71d9 100644
--- a/gen/po.py
+++ b/gen/po.py
@@ -136,6 +136,21 @@ class PoMessage:
ENABLE_COOKIES = 'You must enable cookies before you can log in.'
PAGE_PREVIOUS = 'Previous page'
PAGE_NEXT = 'Next page'
+ FORGOT_PASSWORD = 'Forgot password?'
+ ASK_PASSWORD_REINIT = 'Ask new password'
+ REINIT_MAIL_SENT = 'A mail has been sent to you. Please follow the ' \
+ 'instructions from this email.'
+ REINIT_PASSWORD = 'Password re-initialisation'
+ REINIT_PASSWORD_BODY = 'Hello,
A password re-initialisation ' \
+ 'has been requested, tied to this email address, for the website ' \
+ '${siteUrl}. If you are not at the origin of this request, please ' \
+ 'ignore this email. Else, click on the link below to receive a new ' \
+ 'password.
${url}'
+ NEW_PASSWORD = 'Your new password'
+ NEW_PASSWORD_BODY = 'Hello,
The new password you have ' \
+ 'requested for website ${siteUrl} is ${password}
' \
+ '
Best regards.'
+ NEW_PASSWORD_SENT = 'Your new password has been sent to you by email.'
def __init__(self, id, msg, default, fuzzy=False, comments=[],
niceDefault=False):
diff --git a/gen/ui/appy.css b/gen/ui/appy.css
index ae4ddf6..1f20487 100644
--- a/gen/ui/appy.css
+++ b/gen/ui/appy.css
@@ -67,6 +67,7 @@ img { border: 0; vertical-align: middle}
border-radius: 2px 2px 2px 2px; box-shadow: 0 2px 4px #A9A9A9;}
.focus td { padding: 4px 0px 4px 4px }
.discreet { font-size: 90%; }
+.lostPassword a { font-size: 90%; color: white; padding-left: 1em;}
.portlet { width: 150px; border-right: 1px solid #5F7983;}
.portletContent { margin: 9px; }
.portletTitle { font-weight: bold; font-size: 110%; margin-bottom: 4px;}
diff --git a/gen/ui/appy.js b/gen/ui/appy.js
index 9f0444a..5baba0e 100644
--- a/gen/ui/appy.js
+++ b/gen/ui/appy.js
@@ -451,6 +451,19 @@ function doConfirm() {
}
}
+var wrongTextInput = '#ff934a none';
+// Function triggered when the user ask password reinitialisation
+function doAskPasswordReinit() {
+ // Check that the user has typed a login
+ var theForm = document.getElementById('askPasswordReinitForm');
+ var login = theForm.login.value.replace(' ', '');
+ if (!login) { theForm.login.style.background = wrongTextInput; }
+ else {
+ closePopup('askPasswordReinitPopup');
+ theForm.submit();
+ }
+}
+
// Function that finally posts the edit form after the user has confirmed that
// she really wants to post it.
function postConfirmedEditForm() {
diff --git a/gen/ui/template.pt b/gen/ui/template.pt
index dca54d3..8c02cac 100644
--- a/gen/ui/template.pt
+++ b/gen/ui/template.pt
@@ -78,7 +78,7 @@
- | + | +