[FIX] Upload pics (confs) fails when conflict
[cds-indico.git] / indico / MaKaC / webinterface / rh / base.py
blob5fad3fa7ff21fc768c91642d8740c0d286c4e050
1 # -*- coding: utf-8 -*-
2 ##
3 ##
4 ## This file is part of CDS Indico.
5 ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
6 ##
7 ## CDS Indico is free software; you can redistribute it and/or
8 ## modify it under the terms of the GNU General Public License as
9 ## published by the Free Software Foundation; either version 2 of the
10 ## License, or (at your option) any later version.
12 ## CDS Indico is distributed in the hope that it will be useful, but
13 ## WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ## General Public License for more details.
17 ## You should have received a copy of the GNU General Public License
18 ## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
19 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20 from urlparse import urljoin
22 """Base definitions for the request handlers (rh). A rh is a class which
23 complies to a well defined interface and which from a mod_python request knows
24 which action to carry out in order to handle the request made. This means that
25 each of the possible HTTP ports of the system will have a rh which will know
26 what to do depending on the parameter values received, etc.
27 """
28 import copy, time, os, sys, random, re, socket
29 import StringIO
30 from datetime import datetime, timedelta
32 try:
33 from indico.web.wsgi.indico_wsgi_handler_utils import Field
34 from indico.web.wsgi import webinterface_handler_config as apache
35 except ImportError:
36 pass
37 from ZODB.POSException import ConflictError, POSKeyError
38 from ZEO.Exceptions import ClientDisconnected
40 from MaKaC.common import fossilize
41 from MaKaC.webinterface.pages.conferences import WPConferenceModificationClosed
43 import MaKaC.webinterface.session as session
44 import MaKaC.webinterface.urlHandlers as urlHandlers
45 import MaKaC.webinterface.pages.errors as errors
46 from MaKaC.common.general import *
48 from MaKaC.accessControl import AccessWrapper
49 from MaKaC.common import DBMgr, Config, security
50 from MaKaC.errors import MaKaCError, ModificationError, AccessError, KeyAccessError, TimingError, ParentTimingError, EntryTimingError, FormValuesError, NoReportError, NotFoundError, HtmlScriptError, HtmlForbiddenTag, ConferenceClosedError, HostnameResolveError
51 from MaKaC.webinterface.mail import GenericMailer, GenericNotification
52 from xml.sax.saxutils import escape
54 from MaKaC.common.utils import truncate
55 from MaKaC.common.logger import Logger
56 from MaKaC.common.contextManager import ContextManager
57 from indico.util.i18n import _, availableLocales
59 from MaKaC.plugins import PluginsHolder
60 from MaKaC.user import Group, Avatar
61 from MaKaC.accessControl import AdminList
63 from MaKaC.plugins.base import OldObservable
66 class RequestHandlerBase(OldObservable):
68 _uh = None
70 def __init__(self, req):
71 if req == None:
72 raise Exception("Request object not initialised")
73 self._req = req
75 def _checkProtection( self ):
76 """
77 """
78 pass
80 def _getAuth(self):
81 """
82 Returns True if current user is a user or has either a modification
83 or access key in their session.
84 auth_keys is the set of permissible session keys which do not require user login.
85 """
86 auth_keys = ["modifKeys"]#, "accessKeys"] Cookie would stay forever and force https
88 for key in auth_keys:
89 if self._websession.getVar(key):
90 return True
92 return self._getUser()
94 def getAW( self ):
95 """
96 Returns the access wrapper related to this session/user
97 """
98 return self._aw
100 def _getUser(self):
102 Returns the current user
104 return self._aw.getUser()
106 def _setUser(self, newUser=None):
108 Sets the current user
110 self._aw.setUser(newUser)
112 def getCurrentURL( self ):
114 Gets the "current URL", through the URL handler
116 if self._uh == None:
117 return ""
118 return self._uh.getURL( self._target )
120 def getRequestURL( self, secure=False ):
122 Reconstructs the request URL
124 if secure:
125 return urljoin(Config.getInstance().getBaseSecureURL(), self._req.unparsed_uri)
126 else:
127 return self._req.construct_url(self._req.unparsed_uri)
129 def use_https(self):
131 If the RH must be HTTPS and there is a BaseSecurURL, then use it!
133 return self._tohttps and Config.getInstance().getBaseSecureURL()
135 def getRequestParams( self ):
136 return self._params
138 def getRequestHTTPHeaders( self ):
139 return self._req.headers_in
141 def _setLang(self, params=None):
143 # allow to choose the lang from params
144 if params and 'lang' in params:
145 newLang = params.get('lang', '')
146 for lang in availableLocales:
147 if newLang.lower() == lang.lower():
148 self._websession.setLang(lang)
149 break
151 lang=self._websession.getLang()
152 Logger.get('i18n').debug("lang:%s"%lang)
153 if lang is None:
154 lang = "en_GB"
155 from indico.util.i18n import setLocale
156 setLocale(lang)
158 def getHostIP(self):
159 hostIP = str(self._req.get_remote_ip())
161 minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
162 if minfo.useProxy():
163 # if we're behind a proxy, use X-Forwarded-For
164 return self._req.headers_in.get("X-Forwarded-For", hostIP).split(", ")[-1]
165 else:
166 return hostIP
168 accessWrapper = property( getAW )
171 class RH(RequestHandlerBase):
172 """This class is the base for request handlers of the application. A request
173 handler will be instantiated when a web request arrives to mod_python;
174 the mp layer will forward the request to the corresponding request
175 handler which will know which action has to be performed (displaying a
176 web page or performing some operation and redirecting to another page).
177 Request handlers will be responsible for parsing the parameters coming
178 from a mod_python request, handle the errors which occurred during the
179 action to perform, managing the sessions, checking security for each
180 operation (thus they implement the access control system of the web
181 interface).
182 It is important to encapsulate all this here as in case of changing
183 the web application framework we'll just need to adapt this layer (the
184 rest of the system wouldn't need any change).
186 Attributes:
187 _uh - (URLHandler) Associated URLHandler which points to the
188 current rh.
189 _req - (mod_python.Request) mod_python request received for the
190 current rh.
191 _requestStarted - (bool) Flag which tells whether a DB transaction
192 has been started or not.
193 _websession - ( webinterface.session.sessionManagement.PSession )
194 Web session associated to the HTTP request.
195 _aw - (AccessWrapper) Current access information for the rh.
196 _target - (Locable) Reference to an object which is the destination
197 of the operations needed to carry out the rh. If set it must
198 provide (through the standard Locable interface) the methods
199 to get the url parameters in order to reproduce the access to
200 the rh.
201 _reqParams - (dict) Dictionary containing the received HTTP
202 parameters (independently of the method) transformed into
203 python data types. The key is the parameter name while the
204 value should be the received paramter value (or values).
206 _tohttps = False # set this value to True for the RH that must be HTTPS when there is a BaseSecureURL
207 _doNotSanitizeFields = []
209 def __init__( self, req ):
210 """Constructor. Initialises the rh setting up basic attributes so it is
211 able to process the request.
213 Parameters:
214 req - (mod_python.Request) mod_python request received for the
215 current rh.
217 RequestHandlerBase.__init__(self, req)
218 self._requestStarted = False
219 self._websession = None
220 self._aw = AccessWrapper() #Fill in the aw instance with the current information
221 self._target = None
222 self._reqParams = {}
223 self._startTime = None
224 self._endTime = None
225 self._tempFilesToDelete = []
226 self._doProcess = True #Flag which indicates whether the RH process
227 # must be carried out; this is useful for
228 # the checkProtection methods when they
229 # detect that an inmediate redirection is
230 # needed
232 # Methods =============================================================
234 def getTarget( self ):
235 return self._target
237 def _setSession( self ):
238 """Sets up a reference to the corresponding web session. It uses the
239 session manager to retrieve the session corresponding to the
240 received request and makes sure it is a valid one. In case of having
241 an invalid session it reset client settings and creates a new one.
243 if not self._websession:
244 sm = session.getSessionManager()
245 try:
246 self._websession = sm.get_session( self._req )
247 except session.SessionError, e:
248 sm.revoke_session_cookie( self._req )
249 self._websession = sm.get_session( self._req )
250 sm.maintain_session( self._req, self._websession )
252 def _getSession( self ):
253 """Returns the web session associated to the received mod_python
254 request.
256 if not self._websession:
257 self._setSession()
258 return self._websession
260 def _setSessionUser( self ):
263 self._aw.setUser( self._getSession().getUser() )
265 def _getRequestParams( self ):
266 return self._reqParams
268 def getRequestParams( self ):
269 return self._getRequestParams()
271 def _disableCaching(self):
273 Disables caching, i.e. for materials
276 # IE doesn't seem to like 'no-cache' Cache-Control headers...
277 if (re.match(r'.*MSIE.*', self._req.headers_in.get("User-Agent",""))):
278 # actually, the only way to safely disable caching seems to be this one
279 self._req.headers_out["Cache-Control"] = "private"
280 self._req.headers_out["Expires"] = "-1"
281 else:
282 self._req.headers_out["Cache-Control"] = "no-store, no-cache, must-revalidate"
283 self._req.headers_out["Pragma"] = "no-cache"
285 def _redirect( self, targetURL, noCache=False ):
286 """Utility for redirecting the client web browser to the specified
287 URL.
288 Params:
289 newURL - Target URL of the redirection
291 #check if there is no \r\n character to avoid http header injection
293 if str(targetURL):
294 if "\r" in str(targetURL) or "\n" in str(targetURL):
295 raise MaKaCError(_("http header CRLF injection detected"))
296 self._req.headers_out["Location"] = str(targetURL)
298 if noCache:
299 self._disableCaching()
300 try:
301 self._req.status = apache.HTTP_SEE_OTHER
302 except NameError:
303 pass
305 def _checkHttpsRedirect(self):
307 If HTTPS must be used but it is not, redirect!
309 if self.use_https() and not self._req.is_https():
310 self._redirect(self.getRequestURL(secure=True))
311 return True
312 else:
313 return False
315 def _normaliseListParam( self, param ):
316 if not isinstance(param, list):
317 return [ param ]
318 return param
320 def _processError( self, ex ):
323 raise
325 def _checkParams( self, params ):
328 pass
330 def _process( self ):
333 pass
335 def _processGeneralError(self,e):
336 """Treats general errors occured during the process of a RH.
339 Logger.get('requestHandler').info('Request %s finished with: "%s"' % (id(self._req), e))
341 p=errors.WPGenericError(self)
342 return p.display()
345 def _getTruncatedParams(self):
346 """ Truncates params, so that file objects do not show up in the logs """
348 params = {}
350 for key,value in self._reqParams.iteritems():
351 if isinstance(value, Field):
352 params[key] = "<FILE>"
353 elif type(value) == str:
354 params[key] = truncate(value, 1024)
355 else:
356 params[key] = value
358 return params
360 def _processUnexpectedError(self,e):
361 """Unexpected errors
364 Logger.get('requestHandler').exception('Request %s failed: "%s"\n\nurl: %s\n\nparameters: %s\n\n' % (id(self._req), e,self.getRequestURL(), self._getTruncatedParams()))
365 p=errors.WPUnexpectedError(self)
366 return p.display()
368 def _processHostnameResolveError(self,e):
369 """Unexpected errors
372 Logger.get('requestHandler').exception('Request %s failed: "%s"\n\nurl: %s\n\nparameters: %s\n\n' % (id(self._req), e,self.getRequestURL(), self._getTruncatedParams()))
373 p=errors.WPHostnameResolveError(self)
374 return p.display()
377 def _processAccessError(self,e):
378 """Treats access errors occured during the process of a RH.
380 Logger.get('requestHandler').info('Request %s finished with AccessError: "%s"' % (id(self._req), e))
382 self._req.status = apache.HTTP_FORBIDDEN
383 p=errors.WPAccessError(self)
384 return p.display()
386 def _processKeyAccessError(self,e):
387 """Treats access errors occured during the process of a RH.
389 Logger.get('requestHandler').info('Request %s finished with KeyAccessError: "%s"' % (id(self._req), e))
391 self._req.status = apache.HTTP_FORBIDDEN
392 # We are going to redirect to the page asking for access key
393 # and so it must be https if there is a BaseSecureURL. And that's
394 # why we set _tohttps to True.
395 self._tohttps = True
396 if self._checkHttpsRedirect():
397 return
398 p=errors.WPKeyAccessError(self)
399 return p.display()
401 def _processModificationError(self,e):
402 """Treats modification errors occured during the process of a RH.
405 Logger.get('requestHandler').info('Request %s finished with ModificationError: "%s"' % (id(self._req), e))
407 p=errors.WPModificationError(self)
408 return p.display()
410 def _processConferenceClosedError(self,e):
411 """Treats access to modification pages for conferences when they are closed.
413 p = WPConferenceModificationClosed( self, e._conf )
414 return p.display()
416 def _processTimingError(self,e):
417 """Treats timing errors occured during the process of a RH.
420 Logger.get('requestHandler').info('Request %s finished with TimingError: "%s"' % (id(self._req), e))
422 p=errors.WPTimingError(self,e)
423 return p.display()
425 def _processNoReportError(self,e):
426 """Process errors without reporting
429 Logger.get('requestHandler').info('Request %s finished with NoReportError: "%s"' % (id(self._req), e))
431 p=errors.WPNoReportError(self,e)
432 return p.display()
434 def _processNotFoundError(self,e):
435 """Process not found error; uses NoReportError template
438 Logger.get('requestHandler').info('Request %s finished with NotFoundError: "%s"' % (id(self._req), e))
440 try:
441 self._req.status = apache.HTTP_NOT_FOUND
442 except NameError:
443 pass
445 p=errors.WPNoReportError(self,e)
446 return p.display()
448 def _processParentTimingError(self,e):
449 """Treats timing errors occured during the process of a RH.
452 Logger.get('requestHandler').info('Request %s finished with ParentTimingError: "%s"' % (id(self._req), e))
454 p=errors.WPParentTimingError(self,e)
455 return p.display()
457 def _processEntryTimingError(self,e):
458 """Treats timing errors occured during the process of a RH.
461 Logger.get('requestHandler').info('Request %s finished with EntryTimingError: "%s"' % (id(self._req), e))
463 p=errors.WPEntryTimingError(self,e)
464 return p.display()
466 def _processFormValuesError(self,e):
467 """Treats user input related errors occured during the process of a RH.
470 Logger.get('requestHandler').info('Request %s finished with FormValuesError: "%s"' % (id(self._req), e))
472 p=errors.WPFormValuesError(self,e)
473 return p.display()
475 def _processHtmlScriptError(self, e):
477 Logger.get('requestHandler').info('Request %s finished with ProcessHtmlScriptError: "%s"' % (id(self._req), e))
479 p=errors.WPHtmlScriptError(self, escape(str(e)))
480 return p.display()
482 def _processRestrictedHTML(self, e):
484 Logger.get('requestHandler').info('Request %s finished with ProcessRestrictedHTMLError: "%s"' % (id(self._req), e))
486 p=errors.WPRestrictedHTML(self, escape(str(e)))
487 return p.display()
489 def process( self, params ):
492 profile = Config.getInstance().getProfile()
493 proffilename = ""
494 res = ""
495 retry = 10
496 textLog = []
497 self._startTime = datetime.now()
499 # clear the context
500 ContextManager.destroy()
501 ContextManager.set('currentRH', self)
503 #redirect to https if necessary
504 if self._checkHttpsRedirect():
505 return
507 DBMgr.getInstance().startRequest()
508 self._startRequestSpecific2RH() # I.e. implemented by Room Booking request handlers
509 textLog.append("%s : Database request started"%(datetime.now() - self._startTime))
510 Logger.get('requestHandler').info('[pid=%s] Request %s started (%s)' % (os.getpid(),id(self._req), self._req.unparsed_uri))
512 # notify components that the request has started
513 self._notify('requestStarted', self._req)
515 try:
516 while retry>0:
518 if retry < 10:
519 # notify components that the request is being retried
520 self._notify('requestRetry', self._req, 10 - retry)
522 try:
523 Logger.get('requestHandler').info('\t[pid=%s] from host %s' % (os.getpid(), self.getHostIP()))
524 try:
525 # clear the fossile cache at the start of each request
526 fossilize.clearCache()
528 DBMgr.getInstance().sync()
529 # keep a link to the web session in the access wrapper
530 # this is used for checking access/modification key existence
531 # in the user session
532 self._aw.setIP( self.getHostIP() )
533 self._aw.setSession(self._getSession())
534 #raise(str(dir(self._websession)))
535 self._setSessionUser()
536 self._setLang(params)
537 if self._getAuth():
538 if self._getUser():
539 Logger.get('requestHandler').info('Request %s identified with user %s (%s)' % (id(self._req), self._getUser().getFullName(), self._getUser().getId()))
540 if not self._tohttps and Config.getInstance().getAuthenticatedEnforceSecure():
541 self._tohttps = True
542 if self._checkHttpsRedirect():
543 return
545 #if self._getUser() != None and self._getUser().getId() == "893":
546 # profile = True
547 self._reqParams = copy.copy( params )
548 self._checkParams( self._reqParams )
550 self._checkProtection()
551 security.Sanitization.sanitizationCheck(self._target,
552 self._reqParams,
553 self._aw, self._doNotSanitizeFields)
554 if self._doProcess:
555 if profile:
556 import profile, pstats
557 proffilename = os.path.join(Config.getInstance().getTempDir(), "stone%s.prof" % str(random.random()))
558 result = [None]
559 profile.runctx("result[0] = self._process()", globals(), locals(), proffilename)
560 res = result[0]
561 else:
562 res = self._process()
565 # notify components that the request has finished
566 self._notify('requestFinished', self._req)
567 self._endRequestSpecific2RH( True ) # I.e. implemented by Room Booking request handlers
569 DBMgr.getInstance().endRequest( True )
570 Logger.get('requestHandler').info('Request %s successful' % (id(self._req)))
571 #request succesfull, now, doing tas that must be done only once
572 try:
573 self._sendEmails()
574 self._deleteTempFiles()
575 except:
576 pass
577 break
578 except MaKaCError, e:
579 #DBMgr.getInstance().endRequest(False)
580 res = self._processError(e)
581 except (ConflictError, POSKeyError):
582 import traceback
583 Logger.get('requestHandler').warning('Conflict in Database! (Request %s)\n%s' % (id(self._req), traceback.format_exc()))
584 self._abortSpecific2RH()
585 DBMgr.getInstance().abort()
586 retry -= 1
587 continue
588 except ClientDisconnected:
589 Logger.get('requestHandler').warning('Client Disconnected! (Request %s)' % id(self._req) )
590 self._abortSpecific2RH()
591 DBMgr.getInstance().abort()
592 retry -= 1
593 time.sleep(10-retry)
594 continue
595 except KeyAccessError, e:
596 #Key Access error treatment
597 res = self._processKeyAccessError( e )
598 self._endRequestSpecific2RH( False )
599 DBMgr.getInstance().endRequest(False)
600 except AccessError, e:
601 #Access error treatment
602 res = self._processAccessError( e )
603 self._endRequestSpecific2RH( False )
604 DBMgr.getInstance().endRequest(False)
605 except HostnameResolveError, e:
606 res = self._processHostnameResolveError( e )
607 self._endRequestSpecific2RH( False )
608 DBMgr.getInstance().endRequest(False)
609 except ModificationError, e:
610 #Modification error treatment
611 res = self._processModificationError( e )
612 self._endRequestSpecific2RH( False )
613 DBMgr.getInstance().endRequest(False)
614 except ParentTimingError, e:
615 #Modification error treatment
616 res = self._processParentTimingError( e )
617 self._endRequestSpecific2RH( False )
618 DBMgr.getInstance().endRequest(False)
619 except EntryTimingError, e:
620 #Modification error treatment
621 res = self._processEntryTimingError( e )
622 self._endRequestSpecific2RH( False )
623 DBMgr.getInstance().endRequest(False)
624 except TimingError, e:
625 #Modification error treatment
626 res = self._processTimingError( e )
627 self._endRequestSpecific2RH( False )
628 DBMgr.getInstance().endRequest(False)
629 except FormValuesError, e:
630 #Error filling the values of a form
631 res = self._processFormValuesError( e )
632 self._endRequestSpecific2RH( False )
633 DBMgr.getInstance().endRequest(False)
634 except ConferenceClosedError, e:
635 #Modification error treatment
636 res = self._processConferenceClosedError( e )
637 self._endRequestSpecific2RH( False )
638 DBMgr.getInstance().endRequest(False)
639 except NoReportError, e:
640 #Error without report option
641 res = self._processNoReportError( e )
642 DBMgr.getInstance().endRequest(False)
643 except NotFoundError, e:
644 #File not fond error
645 res = self._processNotFoundError( e )
646 DBMgr.getInstance().endRequest(False)
647 except HtmlScriptError,e:
648 res = self._processHtmlScriptError(e)
649 DBMgr.getInstance().endRequest(False)
650 except HtmlForbiddenTag,e:
651 res = self._processRestrictedHTML(e)
652 DBMgr.getInstance().endRequest(False)
653 except MaKaCError, e:
654 res = self._processGeneralError( e )
655 DBMgr.getInstance().endRequest(False)
656 except ValueError, e:
657 res = self._processGeneralError( e )
658 DBMgr.getInstance().endRequest(False)
659 except Exception, e: #Generic error treatment
660 res = self._processUnexpectedError( e )
661 #DBMgr.getInstance().endRequest(False)
662 #self._endRequestSpecific2RH( False )
664 #cancels any redirection
665 try:
666 del self._req.headers_out["Location"]
667 except AttributeError:
668 pass
669 try:
670 self._req.status=apache.HTTP_INTERNAL_SERVER_ERROR
671 except NameError:
672 pass
674 totalTime = (datetime.now() - self._startTime)
675 textLog.append("%s : Request ended"%totalTime)
677 # log request timing
678 if profile and totalTime > timedelta(0, 1) and os.path.isfile(proffilename):
679 rep = Config.getInstance().getTempDir()
680 stats = pstats.Stats(proffilename)
681 stats.strip_dirs()
682 stats.sort_stats('cumulative', 'time', 'calls')
683 stats.dump_stats(os.path.join(rep, "IndicoRequestProfile.log"))
684 output = StringIO.StringIO()
685 sys.stdout = output
686 stats.print_stats(100)
687 sys.stdout = sys.__stdout__
688 s = output.getvalue()
689 f = file(os.path.join(rep, "IndicoRequest.log"), 'a+')
690 f.write("--------------------------------\n")
691 f.write("URL : " + self._req.construct_url(self._req.unparsed_uri) + "\n")
692 f.write("%s : start request\n"%self._startTime)
693 f.write("params:%s"%params)
694 f.write("\n".join(textLog))
695 f.write("\n")
696 f.write("retried : %d\n"%(10-retry))
697 f.write(s)
698 f.write("--------------------------------\n\n")
699 f.close()
700 if profile and proffilename != "" and os.path.exists(proffilename):
701 os.remove(proffilename)
703 # In case of no process needed, we should return empty string to avoid erroneous ouput
704 # specially with getVars breaking the JS files.
705 if not self._doProcess:
706 return ""
708 if res is None:
709 return ""
711 return res
713 def _sendEmails( self ):
714 if hasattr( self, "_emailsToBeSent" ):
715 for email in self._emailsToBeSent:
716 GenericMailer.send(GenericNotification(email))
718 def _deleteTempFiles( self ):
719 if len(self._tempFilesToDelete) > 0:
720 for file in self._tempFilesToDelete:
721 os.remove(file)
723 def _startRequestSpecific2RH( self ):
725 Works like DBMgr.getInstance().startRequest() but is specific to
726 request handler. It is used to connect to other database only
727 in choosen request handlers.
729 I.e. all Room Booking request handlers override this
730 method to connect to Room Booking backend.
732 pass
734 def _endRequestSpecific2RH( self, commit ):
736 Works like DBMgr.getInstance().endRequest() but is specific to
737 request handler. It is used to disconnect from other database only
738 in choosen request handlers.
740 I.e. all Room Booking request handlers override this
741 method to disconnect from Room Booking backend.
743 pass
745 def _syncSpecific2RH( self ):
747 Works like DBMgr.getInstance().sync() but is specific to
748 request handler. It is used to connect to other database only
749 in choosen request handlers.
751 I.e. all Room Booking request handlers override this
752 method to sync backend.
754 pass
756 def _abortSpecific2RH( self ):
758 Works like DBMgr.getInstance().abort() but is specific to
759 request handler. It is used to abort transactions of other database
760 only in choosen request handlers.
762 I.e. all Room Booking request handlers override this method.
764 pass
766 # Properties =============================================================
768 relativeURL = None
771 from MaKaC.rb_location import CrossLocationDB
772 import MaKaC.common.info as info
774 class RoomBookingDBMixin: # It's _not_ RH
776 Goal:
777 Only _some_ Request Handlers should connect to
778 room booking database.
780 Mix in this class into all Request Handlers,
781 which must use Room Booking backend.
783 Usage:
785 class RHExample( RoomBookingDBMixin, RHProtected ):
786 pass
788 NOTE: it is important to put RoomBookingDBMixin as first
789 base class.
792 def _startRequestSpecific2RH( self ):
793 minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
794 if minfo.getRoomBookingModuleActive():
795 CrossLocationDB.connect()
797 def _endRequestSpecific2RH( self, commit = True ):
798 minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
799 if minfo.getRoomBookingModuleActive():
800 if commit: CrossLocationDB.commit()
801 else: CrossLocationDB.rollback()
802 CrossLocationDB.disconnect()
804 def _syncSpecific2RH( self ):
805 minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
806 if minfo.getRoomBookingModuleActive():
807 CrossLocationDB.sync()
809 def _abortSpecific2RH( self ):
810 minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
811 if minfo.getRoomBookingModuleActive():
812 CrossLocationDB.rollback()
815 class RHProtected( RH ):
817 def _getLoginURL( self ):
818 return urlHandlers.UHSignIn.getURL(self.getRequestURL())
820 def _checkSessionUser( self ):
824 if self._getUser() == None:
825 self._redirect( self._getLoginURL() )
826 self._doProcess = False
828 def _checkProtection( self ):
829 self._checkSessionUser()
832 class RHRoomBookingProtected( RHProtected ):
834 def _checkSessionUser( self ):
835 user = self._getUser()
836 if user == None:
837 self._redirect( self._getLoginURL() )
838 self._doProcess = False
839 else:
840 try:
841 if PluginsHolder().getPluginType("RoomBooking").isActive():
842 if not AdminList.getInstance().isAdmin(user) and PluginsHolder().getPluginType("RoomBooking").getOption("AuthorisedUsersGroups").getValue() != []:
843 authenticatedUser = False
844 for entity in PluginsHolder().getPluginType("RoomBooking").getOption("AuthorisedUsersGroups").getValue():
845 if isinstance(entity, Group) and entity.containsUser(user) or \
846 isinstance(entity, Avatar) and entity == user:
847 authenticatedUser = True
848 break
849 if not authenticatedUser:
850 raise AccessError()
851 except KeyError:
852 pass
854 class RHDisplayBaseProtected( RHProtected ):
856 def _checkProtection( self ):
857 if not self._target.canAccess( self.getAW() ):
858 from MaKaC.conference import Link, LocalFile, Category
859 if isinstance(self._target,Link) or isinstance(self._target,LocalFile):
860 target = self._target.getOwner()
861 else:
862 target = self._target
863 if not isinstance(self._target, Category) and target.isProtected():
864 if target.getAccessKey() != "" or target.getConference() and target.getConference().getAccessKey() != "":
865 raise KeyAccessError()
866 if self._getUser() == None:
867 self._checkSessionUser()
868 else:
869 raise AccessError()
872 class RHModificationBaseProtected( RHProtected ):
874 _allowClosed = False
876 def _checkProtection( self ):
877 if not self._target.canModify( self.getAW() ):
878 if self._target.getModifKey() != "":
879 raise ModificationError()
880 if self._getUser() == None:
881 self._checkSessionUser()
882 else:
883 raise ModificationError()
884 if hasattr(self._target, "getConference") and not self._allowClosed:
885 if self._target.getConference().isClosed():
886 raise ConferenceClosedError(self._target.getConference())