Package muntjac :: Package ui :: Module login_form
[hide private]
[frames] | no frames]

Source Code for Module muntjac.ui.login_form

  1  # Copyright (C) 2012 Vaadin Ltd.  
  2  # Copyright (C) 2012 Richard Lincoln 
  3  #  
  4  # Licensed under the Apache License, Version 2.0 (the "License");  
  5  # you may not use this file except in compliance with the License.  
  6  # You may obtain a copy of the License at  
  7  #  
  8  #     http://www.apache.org/licenses/LICENSE-2.0  
  9  #  
 10  # Unless required by applicable law or agreed to in writing, software  
 11  # distributed under the License is distributed on an "AS IS" BASIS,  
 12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
 13  # See the License for the specific language governing permissions and  
 14  # limitations under the License. 
 15   
 16  try: 
 17      from cStringIO import StringIO 
 18  except ImportError, e: 
 19      from StringIO import StringIO 
 20   
 21  from muntjac.ui.component import Event 
 22  from muntjac.ui.embedded import Embedded 
 23  from muntjac.ui.custom_component import CustomComponent 
 24  from muntjac.terminal.application_resource import IApplicationResource 
 25  from muntjac.terminal.uri_handler import IUriHandler 
 26  from muntjac.terminal.download_stream import DownloadStream 
 27  from muntjac.terminal.parameter_handler import IParameterHandler 
 28   
 29  from muntjac.terminal.gwt.client.application_connection import \ 
 30      ApplicationConnection 
 31   
 32   
33 -class LoginEvent(Event):
34 """This event is sent when login form is submitted.""" 35
36 - def __init__(self, params, form):
37 super(LoginEvent, self).__init__(form) 38 self._params = params
39 40
41 - def getLoginParameter(self, name):
42 """Access method to form values by field names. 43 44 @return: value in given field 45 """ 46 if name in self._params: 47 return self._params.get(name) 48 else: 49 return None
50 51
52 -class ILoginListener(object):
53 """Login listener is a class capable to listen LoginEvents sent from 54 LoginBox 55 """ 56
57 - def onLogin(self, event):
58 """This method is fired on each login form post. 59 """ 60 raise NotImplementedError
61 62 63 _ON_LOGIN_METHOD = getattr(ILoginListener, 'onLogin') 64 65
66 -class LoginForm(CustomComponent):
67 """LoginForm is a Muntjac component to handle common problem among Ajax 68 applications: browsers password managers don't fill dynamically created 69 forms like all those UI elements created by Muntjac. 70 71 For developer it is easy to use: add component to a desired place in you 72 UI and add ILoginListener to validate form input. Behind the curtain 73 LoginForm creates an iframe with static html that browsers detect. 74 75 Login form is by default 100% width and height, so consider using it 76 inside a sized L{Panel} or L{Window}. 77 78 Login page html can be overridden by replacing protected getLoginHTML 79 method. As the login page is actually an iframe, styles must be handled 80 manually. By default component tries to guess the right place for theme 81 css. 82 83 Note, this is a new Ajax terminal specific component and is likely to 84 change. 85 """ 86
87 - def __init__(self):
88 self._usernameCaption = 'Username' 89 self._passwordCaption = 'Password' 90 self._loginButtonCaption = 'Login' 91 self._iframe = Embedded() 92 93 self._window = None 94 95 super(LoginForm, self).__init__() 96 97 self._iframe.setType(Embedded.TYPE_BROWSER) 98 self._iframe.setSizeFull() 99 self.setSizeFull() 100 self.setCompositionRoot(self._iframe) 101 self.addStyleName('v-loginform') 102 103 self.loginPage = LoginPage(self) 104 self.parameterHandler = ParameterHandler(self) 105 self.uriHandler = UriHandler(self)
106 107
108 - def getLoginHTML(self):
109 """Returns byte array containing login page html. If you need to 110 override the login html, use the default html as basis. Login page 111 sets its target with javascript. 112 113 @return: byte array containing login page html 114 """ 115 appUri = str(self.getApplication().getURL()) \ 116 + self.getWindow().getName() + '/' 117 118 return ('<!DOCTYPE html PUBLIC \"-//W3C//DTD ' 119 'XHTML 1.0 Transitional//EN\" ' 120 '\"http://www.w3.org/TR/xhtml1/' 121 'DTD/xhtml1-transitional.dtd\">\n' 122 '<html>' 123 '<head><script type=\'text/javascript\'>' 124 'var setTarget = function() {' 125 'var uri = \'' 126 + appUri + 127 'loginHandler' 128 '\'; var f = document.getElementById(\'loginf\');' 129 'document.forms[0].action = uri;document.forms[0].username.focus();};' 130 '' + 'var styles = window.parent.document.styleSheets;' 131 'for(var j = 0; j < styles.length; j++) {\n' 132 'if(styles[j].href) {' 133 'var stylesheet = document.createElement(\'link\');\n' 134 'stylesheet.setAttribute(\'rel\', \'stylesheet\');\n' 135 'stylesheet.setAttribute(\'type\', \'text/css\');\n' 136 'stylesheet.setAttribute(\'href\', styles[j].href);\n' 137 'document.getElementsByTagName(\'head\')[0].appendChild(stylesheet);\n' 138 '}' 139 '}\n' 140 'function submitOnEnter(e) { var keycode = e.keyCode || e.which;' 141 ' if (keycode == 13) {document.forms[0].submit();} } \n' 142 '</script>' 143 '</head><body onload=\'setTarget();\' style=\'margin:0;padding:0; background:transparent;\' class=\"' 144 + ApplicationConnection.GENERATED_BODY_CLASSNAME + 145 '\">' + '<div class=\'v-app v-app-loginpage\' style=\"background:transparent;\">' 146 '<iframe name=\'logintarget\' style=\'width:0;height:0;' 147 'border:0;margin:0;padding:0;\'></iframe>' 148 '<form id=\'loginf\' target=\'logintarget\' onkeypress=\"submitOnEnter(event)\" method=\"post\">' 149 '<div>' 150 + self._usernameCaption + 151 '</div><div >' 152 '<input class=\'v-textfield\' style=\'display:block;\' type=\'text\' name=\'username\'></div>' 153 '<div>' 154 + self._passwordCaption + 155 '</div>' 156 '<div><input class=\'v-textfield\' style=\'display:block;\' type=\'password\' name=\'password\'></div>' 157 '<div><div onclick=\"document.forms[0].submit();\" tabindex=\"0\" class=\"v-button\" role=\"button\" ><span class=\"v-button-wrap\"><span class=\"v-button-caption\">' 158 + self._loginButtonCaption + 159 '</span></span></div></div></form></div>' 160 '</body></html>').encode('utf-8')
161 162
163 - def attach(self):
164 super(LoginForm, self).attach() 165 self.getApplication().addResource(self.loginPage) 166 self.getWindow().addParameterHandler(self.parameterHandler) 167 self._iframe.setSource(self.loginPage)
168 169
170 - def detach(self):
171 self.getApplication().removeResource(self.loginPage) 172 self.getWindow().removeParameterHandler(self.parameterHandler) 173 # store window temporary to properly remove uri handler once 174 # response is handled. (May happen if login handler removes login 175 # form 176 self._window = self.getWindow() 177 if self._window.getParent() is not None: 178 self._window = self._window.getParent() 179 super(LoginForm, self).detach()
180 181 182 _UNDEFINED_HEIGHT = '140px' 183 _UNDEFINED_WIDTH = '200px' 184 185
186 - def addListener(self, listener, iface=None):
187 """Adds ILoginListener to handle login logic. 188 """ 189 if (isinstance(listener, ILoginListener) and 190 (iface is None or issubclass(iface, ILoginListener))): 191 self.registerListener(LoginEvent, listener, _ON_LOGIN_METHOD) 192 193 super(LoginForm, self).addListener(listener, iface)
194 195
196 - def addCallback(self, callback, eventType=None, *args):
197 if eventType is None: 198 eventType = callback._eventType 199 200 if issubclass(eventType, LoginEvent): 201 self.registerCallback(LoginEvent, callback, None, *args) 202 else: 203 super(LoginForm, self).addCallback(callback, eventType, *args)
204 205
206 - def removeListener(self, listener, iface=None):
207 """Removes ILoginListener. 208 """ 209 if (isinstance(listener, ILoginListener) and 210 (iface is None or issubclass(iface, ILoginListener))): 211 self.withdrawListener(LoginEvent, listener, _ON_LOGIN_METHOD) 212 213 super(LoginForm, self).removeListener(listener, iface)
214 215
216 - def removeCallback(self, callback, eventType=None):
217 if eventType is None: 218 eventType = callback._eventType 219 220 if issubclass(eventType, LoginEvent): 221 self.withdrawCallback(LoginEvent, callback) 222 else: 223 super(LoginForm, self).removeCallback(callback, eventType)
224 225
226 - def setWidth(self, width, unit=None):
227 if unit is not None: 228 super(LoginForm, self).setWidth(width, unit) 229 if self._iframe is not None: 230 if width < 0: 231 self._iframe.setWidth(self._UNDEFINED_WIDTH) 232 else: 233 self._iframe.setWidth('100%') 234 else: 235 super(LoginForm, self).setWidth(width)
236 237
238 - def setHeight(self, height, unit=None):
239 if unit is not None: 240 super(LoginForm, self).setHeight(height, unit) 241 if self._iframe is not None: 242 if height < 0: 243 self._iframe.setHeight(self._UNDEFINED_HEIGHT) 244 else: 245 self._iframe.setHeight('100%') 246 else: 247 super(LoginForm, self).setHeight(height)
248 249
250 - def getUsernameCaption(self):
251 """Returns the caption for the user name field. 252 """ 253 return self._usernameCaption
254 255
256 - def setUsernameCaption(self, usernameCaption):
257 """Sets the caption to show for the user name field. The caption 258 cannot be changed after the form has been shown to the user. 259 """ 260 self._usernameCaption = usernameCaption
261 262
263 - def getPasswordCaption(self):
264 """Returns the caption for the password field. 265 """ 266 return self._passwordCaption
267 268
269 - def setPasswordCaption(self, passwordCaption):
270 """Sets the caption to show for the password field. The caption 271 cannot be changed after the form has been shown to the user. 272 """ 273 self._passwordCaption = passwordCaption
274 275
276 - def getLoginButtonCaption(self):
277 """Returns the caption for the login button. 278 """ 279 return self._loginButtonCaption
280 281
282 - def setLoginButtonCaption(self, loginButtonCaption):
283 """Sets the caption (button text) to show for the login button. The 284 caption cannot be changed after the form has been shown to the user. 285 """ 286 self._loginButtonCaption = loginButtonCaption
287 288
289 -class LoginPage(IApplicationResource):
290
291 - def __init__(self, form):
292 self._form = form
293 294
295 - def getApplication(self):
296 return self._form.getApplication()
297 298
299 - def getBufferSize(self):
300 return len(self._form.getLoginHTML())
301 302
303 - def getCacheTime(self):
304 return -1
305 306
307 - def getFilename(self):
308 return "login"
309 310
311 - def getStream(self):
312 return DownloadStream(StringIO(self._form.getLoginHTML()), 313 self.getMIMEType(), self.getFilename())
314 315
316 - def getMIMEType(self):
317 return "text/html; charset=utf-8"
318 319
320 -class ParameterHandler(IParameterHandler):
321
322 - def __init__(self, form):
323 self._form = form
324 325
326 - def handleParameters(self, parameters):
327 if 'username' in parameters: 328 self._form.getWindow().addURIHandler(self._form.uriHandler) 329 params = dict() 330 # expecting single params 331 for key in parameters: 332 value = parameters.get(key) 333 params[key] = value 334 event = LoginEvent(params, self._form) 335 self._form.fireEvent(event)
336 337
338 -class UriHandler(IUriHandler):
339
340 - def __init__(self, form):
341 self._form = form 342 self._responce = ('<html><body>Login form handeled.' 343 + '<script type=\'text/javascript\'>top.vaadin.forceSync();' 344 + '</script></body></html>')
345 346
347 - def handleURI(self, context, relativeUri):
348 if relativeUri is not None and 'loginHandler' in relativeUri: 349 if self._form._window is not None: 350 self._form._window.removeURIHandler(self) 351 downloadStream = DownloadStream(StringIO(self._responce), 352 'text/html', 'loginSuccesfull') 353 downloadStream.setCacheTime(-1) 354 return downloadStream 355 else: 356 return None
357