source: indico/indico/MaKaC/webinterface/rh/roomBooking.py @ 55c97c

burotelhello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.98b1v0.98b2v0.99v1.0v1.1
Last change on this file since 55c97c was 55c97c, checked in by Jose Benito <jose.benito.gonzalez@…>, 2 years ago

[FIX] Fix editing rooms without uploading an image

  • Property mode set to 100644
File size: 87.3 KB
Line 
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.
11##
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.
16##
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.
20from MaKaC.plugins.base import pluginId
21
22# Most of the following imports are probably not necessary - to clean
23
24import os,time,re
25
26import MaKaC.webinterface.urlHandlers as urlHandlers
27import MaKaC.webinterface.locators as locators
28from MaKaC.common.general import *
29from MaKaC.common.Configuration import Config
30from MaKaC.webinterface.rh.base import RoomBookingDBMixin, RHRoomBookingProtected
31from datetime import datetime, timedelta
32from MaKaC.common.utils import validMail, setValidEmailSeparators
33from MaKaC.common.datetimeParser import parse_date
34
35# The following are room booking related
36
37import MaKaC.webinterface.pages.roomBooking as roomBooking_wp
38import MaKaC.webinterface.pages.admins as admins
39from MaKaC.rb_room import RoomBase
40from MaKaC.rb_reservation import ReservationBase, RepeatabilityEnum
41from MaKaC.rb_factory import Factory
42from MaKaC.rb_location import CrossLocationQueries, RoomGUID, Location
43from MaKaC.rb_tools import intd, FormMode, doesPeriodsOverlap
44from MaKaC.errors import MaKaCError, FormValuesError, NoReportError
45from MaKaC.plugins import PluginLoader
46from MaKaC import plugins
47from MaKaC.plugins.RoomBooking.default.reservation import ResvHistoryEntry
48from MaKaC.search.cache import MapOfRoomsCache
49
50class CandidateDataFrom( object ):
51    DEFAULTS, PARAMS, SESSION = xrange( 3 )
52
53# 0. Base classes
54
55class RoomBookingAvailabilityParamsMixin:
56    def _checkParamsRepeatingPeriod( self, params ):
57        """
58        Extracts startDT, endDT and repeatability
59        from the form, if present.
60
61        Assigns these values to self, or Nones if values
62        are not present.
63        """
64
65        sDay = params.get( "sDay" )
66        eDay = params.get( "eDay" )
67        sMonth = params.get( "sMonth" )
68        eMonth = params.get( "eMonth" )
69        sYear = params.get( "sYear" )
70        eYear = params.get( "eYear" )
71
72        if sDay and len( sDay.strip() ) > 0:
73            sDay = int( sDay.strip() )
74
75        if eDay and len( eDay.strip() ) > 0:
76            eDay = int( eDay.strip() )
77
78        if sMonth and len( sMonth.strip() ) > 0:
79            sMonth = int( sMonth.strip() )
80
81#        if sYear and sMonth and sDay:
82#            # For format checking
83#            try:
84#                time.strptime(sDay.strip() + "/" + sMonth.strip() + "/" + sYear.strip() , "%d/%m/%Y")
85#            except ValueError:
86#                raise NoReportError(_("The Start Date must be of the form DD/MM/YYYY and must be a valid date."))
87
88        if eMonth and len( eMonth.strip() ) > 0:
89            eMonth = int( eMonth.strip() )
90
91        if sYear and len( sYear.strip() ) > 0:
92            sYear = int( sYear.strip() )
93
94        if eYear and len( eYear.strip() ) > 0:
95            eYear = int( eYear.strip() )
96
97#        if eYear and eMonth and eDay:
98#            # For format checking
99#            try:
100#                time.strptime(eDay.strip() + "/" + eMonth.strip() + "/" + eYear.strip() , "%d/%m/%Y")
101#            except ValueError:
102#                raise NoReportError(_("The End Date must be of the form DD/MM/YYYY and must be a valid date."))
103
104
105        sTime = params.get( "sTime" )
106        if sTime and len( sTime.strip() ) > 0:
107            sTime = sTime.strip()
108        eTime = params.get( "eTime" )
109        if eTime and len( eTime.strip() ) > 0:
110            eTime = eTime.strip()
111
112        # process sTime and eTime
113        if sTime and eTime:
114
115            try:
116                time.strptime(sTime, "%H:%M")
117            except ValueError:
118                raise NoReportError(_("The Start Time must be of the form HH:MM and must be a valid time."))
119
120            t = sTime.split( ':' )
121            sHour = int( t[0] )
122            sMinute = int( t[1] )
123
124            try:
125                time.strptime(eTime, "%H:%M")
126            except ValueError:
127                raise NoReportError(_("The End Time must be of the form HH:MM and must be a valid time."))
128
129            t = eTime.split( ':' )
130            eHour = int( t[0] )
131            eMinute = int( t[1] )
132
133        repeatability = params.get( "repeatability" )
134        if repeatability and len( repeatability.strip() ) > 0:
135            if repeatability == "None":
136                repeatability = None
137            else:
138                repeatability = int( repeatability.strip() )
139
140        self._startDT = None
141        self._endDT = None
142        self._repeatability = repeatability
143        if sYear and sMonth and sDay and sTime and eYear and eMonth and eDay and eTime:
144            # Full period specified
145            self._startDT = datetime( sYear, sMonth, sDay, sHour, sMinute )
146            self._endDT = datetime( eYear, eMonth, eDay, eHour, eMinute )
147        elif sYear and sMonth and sDay and eYear and eMonth and eDay:
148            # There are no times
149            self._startDT = datetime( sYear, sMonth, sDay, 0, 0, 0 )
150            self._endDT = datetime( eYear, eMonth, eDay, 23, 59, 59 )
151        elif sTime and eTime:
152            # There are no dates
153            self._startDT = datetime( 1990, 1, 1, sHour, sMinute )
154            self._endDT = datetime( 2030, 12, 31, eHour, eMinute )
155        self._today=False
156        if params.get( "day", "" ) == "today":
157            self._today=True
158            self._startDT = datetime.today().replace(hour=0,minute=0,second=0)
159            self._endDT = self._startDT.replace(hour=23,minute=59,second=59)
160
161class RHRoomBookingBase( RoomBookingAvailabilityParamsMixin, RoomBookingDBMixin, RHRoomBookingProtected ):
162    """
163    All room booking related hanlders are derived from this class.
164    This gives them:
165    - several general use methods
166    - login-protection
167    - auto connecting/disconnecting from room booking db
168    """
169
170    def _checkProtection( self ):
171        RHRoomBookingProtected._checkProtection(self)
172
173    def _clearSessionState( self ):
174        session = self._websession
175
176        session.setVar( "actionSucceeded", None )
177        session.setVar( "deletionFailed", None )
178        session.setVar( "formMode", None )
179
180        session.setVar( "candDataInSession", None )
181        session.setVar( "candDataInParams", None )
182        session.setVar( "afterCalPreview", None )
183
184        session.setVar( "showErrors", False )
185        session.setVar( "errors", None )
186        session.setVar( "thereAreConflicts", None )
187
188        session.setVar( "roomID", None )
189        session.setVar( "roomLocation", None )
190        session.setVar( "resvID", None )
191
192    # Room
193
194    def _saveRoomCandidateToSession( self, c ):
195        # TODO: is this method needed anymore??
196        session = self._websession     # Just an alias
197        if self._formMode == FormMode.MODIF:
198            session.setVar( "roomID", c.id )
199            session.setVar( "roomLocation", c.locationName )
200
201        session.setVar( "name", c.name )
202        session.setVar( "site", c.site )
203        session.setVar( "building", c.building )
204        session.setVar( "floor", c.floor )
205        session.setVar( "roomNr", c.roomNr )
206        session.setVar( "latitude", c.latitude )
207        session.setVar( "longitude", c.longitude )
208
209        session.setVar( "isActive", c.isActive )
210        session.setVar( "isReservable", c.isReservable )
211        session.setVar( "resvsNeedConfirmation", c.resvsNeedConfirmation )
212
213        session.setVar( "responsibleId", c.responsibleId )
214        session.setVar( "whereIsKey", c.whereIsKey )
215        session.setVar( "telephone", c.telephone )
216
217        session.setVar( "capacity", c.capacity )
218        session.setVar( "division", c.division )
219        session.setVar( "surfaceArea", c.surfaceArea )
220        session.setVar( "comments", c.comments )
221
222        session.setVar( "equipment", c.getEquipment() )
223        for name, value in c.customAtts.iteritems():
224            session.setVar( "cattr_" + name, value )
225
226    def _getErrorsOfRoomCandidate( self, c ):
227        errors = []
228        #if not c.site:
229        #    errors.append( "Site can not be blank" )
230        if not c.floor:
231            errors.append( "Floor can not be blank" )
232        if not c.roomNr:
233            errors.append( "Room number can not be blank" )
234        if not c.responsibleId:
235            errors.append( "Room must have a responsible person" )
236        if not c.building or c.building < 1:
237            errors.append( "Building must be a positive integer" )
238        if not c.capacity or c.capacity < 1:
239            errors.append( "Capacity must be a positive integer" )
240
241        try:
242            if c.longitude and float(c.longitude) < 0:
243                errors.append("Longitude must be a positive number")
244        except ValueError:
245            errors.append("Longitude must be a number")
246
247        try:
248            if c.latitude and float(c.latitude) < 0:
249                errors.append("Latitude must be a positive number")
250        except ValueError:
251            errors.append("Latitude must be a number")
252
253        params = self._params
254        if ( params['largePhotoPath'] != '' ) ^ ( params['smallPhotoPath'] != '' ):
255            errors.append( "Either upload both photos or none")
256
257        # Custom attributes
258        manager = CrossLocationQueries.getCustomAttributesManager( c.locationName )
259        for ca in manager.getAttributes( location = c.locationName ):
260            if ca['name'] == 'notification email' :
261                if c.customAtts[ 'notification email' ] and not validMail(c.customAtts['notification email']) :
262                    errors.append( "Invalid format for the notification email" )
263            if ca['required']:
264                if not c.customAtts.has_key( ca['name'] ): # not exists
265                    errors.append( ca['name'] + " can not be blank" )
266                elif not c.customAtts[ ca['name'] ]:       # is empty
267                    errors.append( ca['name'] + " can not be blank" )
268
269        return errors
270
271    def _loadRoomCandidateFromDefaults( self, candRoom ):
272        candRoom.isActive = True
273
274        candRoom.building = None
275        candRoom.floor = ''
276        candRoom.roomNr = ''
277        candRoom.longitude = ''
278        candRoom.latitude = ''
279
280        candRoom.capacity = 20
281        candRoom.site = ''
282        candRoom.division = None
283        candRoom.isReservable = True
284        candRoom.resvsNeedConfirmation = False
285        candRoom.photoId = None
286        candRoom.externalId = None
287
288        candRoom.telephone = ''      # str
289        candRoom.surfaceArea = None
290        candRoom.whereIsKey = ''
291        candRoom.comments = ''
292        candRoom.responsibleId = None
293
294    def _loadRoomCandidateFromSession( self, candRoom ):
295        session = self._websession # Just an alias
296
297        candRoom.name = session.getVar( "name" )
298        candRoom.site = session.getVar( "site" )
299        candRoom.building = intd( session.getVar( "building" ) )
300        candRoom.floor = session.getVar( "floor" )
301        candRoom.roomNr = session.getVar( "roomNr" )
302        candRoom.latitude = session.getVar( "latitude" )
303        candRoom.longitude = session.getVar( "longitude" )
304
305        candRoom.isActive = bool( session.getVar( "isActive" ) )
306        candRoom.isReservable = bool( session.getVar( "isReservable" ) )
307        candRoom.resvsNeedConfirmation = bool( session.getVar( "resvsNeedConfirmation" ) )
308
309        candRoom.responsibleId = session.getVar( "responsibleId" )
310        candRoom.whereIsKey = session.getVar( "whereIsKey" )
311        candRoom.telephone = session.getVar( "telephone" )
312
313        candRoom.capacity = intd( session.getVar( "capacity" ) )
314        candRoom.division = session.getVar( "division" )
315        candRoom.surfaceArea = intd( session.getVar( "surfaceArea" ) )
316        candRoom.comments = session.getVar( "comments" )
317
318        candRoom.setEquipment( session.getVar( "equipment" ) )
319
320        manager = CrossLocationQueries.getCustomAttributesManager( candRoom.locationName )
321        for ca in manager.getAttributes( location = candRoom.locationName ):
322            value = session.getVar( "cattr_" + ca['name'] )
323            if value != None:
324                if ca['name'] == 'notification email' :
325                    candRoom.customAtts[ 'notification email' ] = setValidEmailSeparators(value)
326                else :
327                    candRoom.customAtts[ ca['name'] ] = value
328
329
330    def _loadRoomCandidateFromParams( self, candRoom, params ):
331        candRoom.name = params.get( "name" )
332        candRoom.site = params.get( "site" )
333        candRoom.building = intd( params.get( "building" ) )
334        candRoom.floor = params.get( "floor" )
335        candRoom.roomNr = params.get( "roomNr" )
336        candRoom.latitude = params.get( "latitude" )
337        candRoom.longitude = params.get( "longitude" )
338
339        candRoom.isActive = bool( params.get( "isActive" ) ) # Safe
340        candRoom.isReservable = bool( params.get( "isReservable" ) ) # Safe
341        candRoom.resvsNeedConfirmation = bool( params.get( "resvsNeedConfirmation" ) ) # Safe
342
343        candRoom.responsibleId = params.get( "responsibleId" )
344        if candRoom.responsibleId == "None":
345            candRoom.responsibleId = None
346        candRoom.whereIsKey = params.get( "whereIsKey" )
347        candRoom.telephone = params.get( "telephone" )
348
349        candRoom.capacity = intd( params.get( "capacity" ) )
350        candRoom.division = params.get( "division" )
351        candRoom.surfaceArea = intd( params.get( "surfaceArea" ) )
352        candRoom.comments = params.get( "comments" )
353        #TODO: change this in order to support many periods
354        candRoom.clearNonBookableDates()
355        if params.get("startDateNonBookablePeriod0", "") and params.get("endDateNonBookablePeriod0",""):
356            candRoom.addNonBookableDateFromParams({"startDate": datetime(*(time.strptime(params.get("startDateNonBookablePeriod0"), '%d/%m/%Y')[0:6])),
357                                                   "endDate": datetime(*(time.strptime(params.get("endDateNonBookablePeriod0"), '%d/%m/%Y')[0:6]))})
358
359        eqList = []
360        vcList = []
361        for k, v in params.iteritems():
362            if k.startswith( "equ_" ) and v:
363                eqList.append(k[4:len(k)])
364            if k.startswith( "vc_" ) and v:
365                vcList.append(k[3:])
366        candRoom.setEquipment( eqList )
367        candRoom.setAvailableVC(vcList)
368
369        for k, v in params.iteritems():
370            if k.startswith( "cattr_" ):
371                attrName = k[6:len(k)]
372                if attrName == 'notification email' :
373                    candRoom.customAtts['notification email'] = setValidEmailSeparators(v)
374                else :
375                    candRoom.customAtts[attrName] = v
376
377    # Resv
378
379    def _saveResvCandidateToSession( self, c ):
380        session = self._websession
381        if self._formMode == FormMode.MODIF:
382            session.setVar( "resvID", c.id )
383            session.setVar( "roomLocation", c.locationName )
384        session.setVar( "roomID", c.room.id )
385        session.setVar( "startDT", c.startDT )
386        session.setVar( "endDT", c.endDT )
387        session.setVar( "repeatability", c.repeatability )
388        session.setVar( "bookedForName", c.bookedForName )
389        session.setVar( "contactPhone", c.contactPhone )
390        session.setVar( "contactEmail", c.contactEmail )
391        session.setVar( "reason", c.reason )
392        session.setVar( "usesAVC", c.usesAVC )
393        session.setVar( "needsAVCSupport", c.needsAVCSupport )
394
395        if hasattr(self, '_skipConflicting'):
396            if self._skipConflicting:
397                skip = 'on'
398            else:
399                skip = 'off'
400            session.setVar( "skipConflicting", skip )
401
402        if hasattr(c, "useVC"):
403            session.setVar( "useVC",  c.useVC)
404
405
406    def _getErrorsOfResvCandidate( self, c ):
407        errors = []
408        self._thereAreConflicts = False
409        if not c.bookedForName:
410            errors.append( "Booked for can not be blank" )
411        if not c.reason:
412            errors.append( "Purpose can not be blank" )
413        if not c.isRejected and not c.isCancelled:
414            collisions = c.getCollisions( sansID = self._candResv.id )
415            if len( collisions ) > 0:
416                if self._skipConflicting and c.startDT.date() != c.endDT.date():
417                    for collision in collisions:
418                        c.excludeDay( collision.startDT.date() )
419                else:
420                    self._thereAreConflicts = True
421                    errors.append( "There are conflicts with other bookings" )
422
423        return errors
424
425    def _loadResvCandidateFromSession( self, candResv, params ):
426        # After successful searching or failed save
427        session = self._websession
428
429        roomID = params['roomID']
430        if isinstance( roomID, list ):
431            roomID = int( roomID[0] )
432        else:
433            roomID = int( roomID )
434        roomLocation = params.get( "roomLocation" )
435        if isinstance( roomLocation, list ):
436            roomLocation = roomLocation[0]
437        if not roomLocation:
438            roomLocation = session.getVar( "roomLocation" )
439
440        if not candResv:
441            candResv = Location.parse( roomLocation ).factory.newReservation() # The same location as for room
442
443        if not candResv.room:
444            candResv.room = CrossLocationQueries.getRooms( roomID = roomID, location = roomLocation )
445        candResv.startDT = session.getVar( "startDT" )
446        candResv.endDT = session.getVar( "endDT" )
447        candResv.repeatability = session.getVar( "repeatability" )
448        candResv.bookedForName = session.getVar( "bookedForName" )
449        candResv.contactPhone = session.getVar( "contactPhone" )
450        candResv.contactEmail = setValidEmailSeparators(session.getVar( "contactEmail" ))
451        candResv.reason = session.getVar( "reason" )
452        candResv.usesAVC = session.getVar( "usesAVC" )
453        candResv.needsAVCSupport = session.getVar( "needsAVCSupport" )
454        self._skipConflicting = session.getVar( "skipConflicting" ) == "on"
455
456        useVC = session.getVar('useVC')
457        if useVC is not None:
458            candResv.useVC = useVC
459
460        return candResv
461
462    def _loadResvCandidateFromParams( self, candResv, params ):
463        # After calendar preview
464        roomID = params['roomID']
465        if isinstance( roomID, list ):
466            roomID = int( roomID[0] )
467        else:
468            roomID = int( roomID )
469        roomLocation = params.get( "roomLocation" )
470        if isinstance( roomLocation, list ):
471            roomLocation = roomLocation[0]
472        if not candResv:
473            candResv = Location.parse( roomLocation ).factory.newReservation() # The same location as room
474        if not candResv.room:
475            candResv.room = CrossLocationQueries.getRooms( roomID = roomID, location = roomLocation )
476        self._checkParamsRepeatingPeriod( params )
477        candResv.startDT = self._startDT
478        candResv.endDT = self._endDT
479        candResv.repeatability = self._repeatability
480        candResv.bookedForName = params["bookedForName"]
481        candResv.contactEmail = setValidEmailSeparators(params["contactEmail"])
482        candResv.contactPhone = params["contactPhone"]
483        candResv.reason = params["reason"]
484        candResv.usesAVC = params.get( "usesAVC" ) == "on"
485        candResv.needsAVCSupport = params.get( "needsAVCSupport" ) == "on"
486        self._skipConflicting = params.get( "skipConflicting" ) == "on"
487        d = {}
488        for vc in candResv.room.getAvailableVC():
489            d[vc[:3]] = vc
490        candResv.useVC = []
491        for param in params:
492            if len(param) > 3 and param[:3] == "vc_":
493                vc = d.get(param[3:], None)
494                if vc:
495                    candResv.useVC.append(vc)
496        return candResv
497
498    def _loadResvCandidateFromDefaults( self, params ):
499        ws = self._websession
500        # After room details
501        roomID = int( params['roomID'] )
502        roomLocation = params['roomLocation']
503        candResv = Location.parse( roomLocation ).factory.newReservation() # Create in the same location as room
504        if not candResv.room:
505            candResv.room = CrossLocationQueries.getRooms( roomID = roomID, location = roomLocation )
506        # Generic defaults
507        now = datetime.now()
508        if now.weekday() in [4,5]:
509            now = now + timedelta( 7 - now.weekday() )
510        else:
511            now = now + timedelta( 1 )
512
513        # Sets the dates if needed
514        dayD = params.get("day")
515        monthM = params.get("month")
516        yearY = params.get("year")
517
518        if dayD != None and dayD.isdigit() and \
519           monthM != None and monthM.isdigit() and \
520           yearY != None and yearY.isdigit():
521            candResv.startDT = datetime(int(yearY), int(monthM), int(dayD), 8, 30)
522            candResv.endDT = datetime(int(yearY), int(monthM), int(dayD), 17, 30)
523        else:
524            if candResv.startDT == None:
525                candResv.startDT = datetime( now.year, now.month, now.day, 8, 30 )
526            if candResv.endDT == None:
527                candResv.endDT = datetime( now.year, now.month, now.day, 17, 30 )
528        if self._getUser():
529            if candResv.bookedForName == None:
530                candResv.bookedForName = self._getUser().getFullName()
531            if candResv.contactEmail == None:
532                candResv.contactEmail = self._getUser().getEmail()
533            if candResv.contactPhone == None:
534                candResv.contactPhone = self._getUser().getTelephone()
535        else:
536            candResv.bookedForName = candResv.contactEmail = candResv.contactPhone = ""
537        if candResv.reason == None:
538            candResv.reason = ""
539        if candResv.usesAVC == None:
540            candResv.usesAVC = False
541        if candResv.needsAVCSupport == None:
542            candResv.needsAVCSupport = False
543
544        if not ws.getVar( "dontAssign" ):
545            if ws.getVar( "defaultStartDT" ):
546                candResv.startDT = ws.getVar( "defaultStartDT" )
547            if ws.getVar( "defaultEndDT" ):
548                candResv.endDT = ws.getVar( "defaultEndDT" )
549            if ws.getVar( "defaultRepeatability" ) != None:
550                candResv.repeatability = ws.getVar( "defaultRepeatability" )
551            if ws.getVar( "defaultBookedForName" ):
552                candResv.bookedForName = ws.getVar( "defaultBookedForName" )
553            if ws.getVar( "defaultReason" ):
554                candResv.reason = ws.getVar( "defaultReason" )
555
556            if ws.getVar( "assign2Session" ):
557                self._assign2Session = ws.getVar( "assign2Session" )
558            if ws.getVar( "assign2Contribution" ):
559                self._assign2Contributioon = ws.getVar( "assign2Contribution" )
560
561        return candResv
562
563
564class RHRoomBookingAdminBase( RHRoomBookingBase ):
565    """
566    Adds admin authorization. All classes that implement admin
567    tasks should be derived from this class.
568    """
569    def _checkProtection( self ):
570        if self._getUser() == None:
571            self._checkSessionUser()
572        elif not self._getUser().isAdmin():
573            raise MaKaCError( "You are not authorized to take this action." )
574
575class RHRoomBookingWelcome( RHRoomBookingBase ):
576    _uh = urlHandlers.UHRoomBookingWelcome
577
578    def _process( self ):
579        #if self._getUser().isResponsibleForRooms():
580        #    self._redirect( urlHandlers.UHRoomBookingBookingList.getURL( ofMyRooms = True, autoCriteria = True ) )
581        #    return
582        #self._redirect( urlHandlers.UHRoomBookingBookingList.getURL( onlyMy = True, autoCriteria = True ) )
583        self._redirect( urlHandlers.UHRoomBookingSearch4Rooms.getURL( forNewBooking = True ))
584
585# 1. Searching
586
587class RHRoomBookingSearch4Rooms( RHRoomBookingBase ):
588
589    def _cleanDefaultsFromSession( self ):
590        websession = self._websession
591        websession.setVar( "defaultStartDT", None )
592        websession.setVar( "defaultEndDT", None )
593        websession.setVar( "defaultRepeatability", None )
594        websession.setVar( "defaultBookedForName", None )
595        websession.setVar( "defaultReason", None )
596        websession.setVar( "assign2Session", None )
597        websession.setVar( "assign2Contribution", None )
598
599    def _setGeneralDefaultsInSession( self ):
600        now = datetime.now()
601
602        # if it's saturday or sunday, postpone for monday as a default
603        if now.weekday() in [5,6]:
604            now = now + timedelta( 7 - now.weekday() )
605
606        websession = self._websession
607        websession.setVar( "defaultStartDT", datetime( now.year, now.month, now.day, 8, 30 ) )
608        websession.setVar( "defaultEndDT", datetime( now.year, now.month, now.day, 17, 30 ) )
609
610    def _checkParams( self, params ):
611        self._cleanDefaultsFromSession()
612        self._setGeneralDefaultsInSession()
613        self._forNewBooking = False
614        self._eventRoomName = None
615        if params.get( 'forNewBooking' ):
616            self._forNewBooking = params.get( 'forNewBooking' ) == 'True'
617
618    def _businessLogic( self ):
619        self._rooms = CrossLocationQueries.getRooms( allFast = True )
620        self._rooms.sort()
621        self._equipment = CrossLocationQueries.getPossibleEquipment()
622
623    def _process( self ):
624        self._businessLogic()
625        p = roomBooking_wp.WPRoomBookingSearch4Rooms( self, self._forNewBooking )
626        return p.display()
627
628class RHRoomBookingSearch4Bookings( RHRoomBookingBase ):
629
630    def _businessLogic( self ):
631        self._rooms = CrossLocationQueries.getRooms( allFast = True )
632        self._rooms.sort()
633
634    def _process( self ):
635        self._businessLogic()
636        p = roomBooking_wp.WPRoomBookingSearch4Bookings( self )
637        return p.display()
638
639class RHRoomBookingSearch4Users( RHRoomBookingBase ):
640
641    def _checkParams( self, params ):
642
643        roomID = params.get( "roomID" )
644        roomLocation = params.get( "roomLocation" )
645        candRoom = None
646        if roomID:
647            self._formMode = FormMode.MODIF
648            roomID = int( roomID )
649            candRoom = CrossLocationQueries.getRooms( roomID = roomID, location = roomLocation )
650        else:
651            self._formMode = FormMode.NEW
652            candRoom = Factory.newRoom()  # Is it OK? Potential bug.
653
654        self._loadRoomCandidateFromParams( candRoom, params )
655        self._saveRoomCandidateToSession( candRoom )
656
657        # Set session
658        self._websession.setVar( "showErrors", False )
659        self._websession.setVar( "candDataInSession", True )
660
661        if params.has_key( 'largePhotoPath' ): del params['largePhotoPath']
662        if params.has_key( 'smallPhotoPath' ): del params['smallPhotoPath']
663
664        self._forceWithoutExtAuth = True
665        if params.has_key( 'searchExt' )  and  params['searchExt'] == 'Nice':
666            self._forceWithoutExtAuth = False
667
668    def _process( self ):
669        p = roomBooking_wp.WPRoomBookingSearch4Users( self )
670        return p.display( **self._getRequestParams() )
671
672class RHRoomBookingMapOfRooms(RHRoomBookingBase):
673
674    def _checkParams(self, params):
675        RHRoomBookingBase._checkParams(self, params)
676        self._roomID = params.get('roomID')
677
678    def _process(self):
679        params = {}
680        if self._roomID:
681            params['roomID'] = self._roomID
682        page = roomBooking_wp.WPRoomBookingMapOfRooms(self, **params)
683        return page.display()
684
685class RHRoomBookingMapOfRoomsWidget(RHRoomBookingBase):
686
687    def __init__(self, *args, **kwargs):
688        RHRoomBookingBase.__init__(self, *args, **kwargs)
689        self._cache = MapOfRoomsCache()
690
691    def _setGeneralDefaultsInSession( self ):
692        now = datetime.now()
693
694        # if it's saturday or sunday, postpone for monday as a default
695        if now.weekday() in [5,6]:
696            now = now + timedelta( 7 - now.weekday() )
697
698        websession = self._websession
699        websession.setVar( "defaultStartDT", datetime( now.year, now.month, now.day, 8, 30 ) )
700        websession.setVar( "defaultEndDT", datetime( now.year, now.month, now.day, 17, 30 ) )
701
702    def _checkParams(self, params):
703        self._setGeneralDefaultsInSession()
704        RHRoomBookingBase._checkParams(self, params)
705        self._roomID = params.get('roomID')
706
707    def _businessLogic(self):
708        # get all rooms
709        defaultLocation = Location.getDefaultLocation()
710        rooms = RoomBase.getRooms(location=defaultLocation.friendlyName)
711        aspects = [aspect.toDictionary() for aspect in defaultLocation.getAspects()]
712
713        # specialization for a video conference, CERN-specific
714        possibleEquipment = defaultLocation.factory.getEquipmentManager().getPossibleEquipment()
715        possibleVideoConference = 'Video conference' in possibleEquipment
716        self._forVideoConference = possibleVideoConference and self._getRequestParams().get("avc") == 'y'
717
718        # break-down the rooms by buildings
719        buildings = {}
720        for room in rooms:
721            if room.building:
722
723                # FIXME: a hack to check if the room's comments are OK for serialization
724                try:
725                    unicode(room.comments)
726                except:
727                    room.comments = ''
728
729                # if it's the first room in that building, initialize the building
730                building = buildings.get(room.building, None)
731                if building is None:
732                    title = _("Building") + " %s" % room.building
733                    building = {'has_coordinates':False, 'number':room.building, 'title':title, 'rooms':[]}
734                    buildings[room.building] = building
735
736                # if the room has coordinates, set the building coordinates
737                if room.latitude and room.longitude:
738                    building['has_coordinates'] = True
739                    building['latitude'] = room.latitude
740                    building['longitude'] = room.longitude
741
742                # add the room to its building
743                if not self._forVideoConference or room.needsAVCSetup:
744                    building['rooms'].append(room.fossilize())
745
746        # filter the buildings with rooms and coordinates and return them
747        buildings_with_coords = [b for b in buildings.values() if b['rooms'] and b['has_coordinates']]
748        self._defaultLocation = defaultLocation.friendlyName
749        self._aspects = aspects
750        self._buildings = buildings_with_coords
751
752    def _process(self):
753        self._businessLogic()
754        page = roomBooking_wp.WPRoomBookingMapOfRoomsWidget(self, self._aspects, self._buildings, self._defaultLocation, self._forVideoConference, self._roomID)
755
756        params = self._getRequestParams()
757        entry = self._cache.loadObject('', params)
758        if entry:
759            html = entry.getContent()
760        else:
761            html = page.display()
762            self._cache.cacheObject('', html, params)
763        return html
764
765# 2. List of ...
766
767class RHRoomBookingRoomList( RHRoomBookingBase ):
768
769    def _checkParams( self, params ):
770
771        self._roomLocation = None
772        if params.get("roomLocation") and len( params["roomLocation"].strip() ) > 0:
773            self._roomLocation = params["roomLocation"].strip()
774
775        self._freeSearch = None
776        if params.get("freeSearch") and len( params["freeSearch"].strip() ) > 0:
777            s = params["freeSearch"].strip()
778            # Remove commas
779            self._freeSearch = ""
780            for c in s:
781                if c != ',': self._freeSearch += c
782
783        self._capacity = None
784        if params.get("capacity") and len( params["capacity"].strip() ) > 0:
785            self._capacity = int( params["capacity"].strip() )
786
787        self._availability = "Don't care"
788        if params.get("availability") and len( params["availability"].strip() ) > 0:
789            self._availability = params["availability"].strip()
790
791        if self._availability != "Don't care":
792            self._checkParamsRepeatingPeriod( params )
793
794        self._includePrebookings = False
795        if params.get( 'includePrebookings' ) == "on": self._includePrebookings = True
796
797        # The end of "avail/don't care"
798
799        # Equipment
800        self._equipment = []
801        for k, v in params.iteritems():
802            if k[0:4] == "equ_" and v == "on":
803                self._equipment.append( k[4:100] )
804
805        # Special
806        self._isReservable = self._ownedBy = self._isAutoConfirmed = None
807        self._isActive = True
808
809        if params.get( 'isReservable' ) == "on": self._isReservable = True
810        if params.get( 'isAutoConfirmed' ) == "on": self._isAutoConfirmed = True
811
812        # only admins can choose to consult non-active rooms
813        if self._getUser() and self._getUser().isAdmin() and params.get( 'isActive', None ) != "on":
814            self._isActive = None
815
816        self._onlyMy = params.get( 'onlyMy' ) == "on"
817
818    def _businessLogic( self ):
819        if self._onlyMy: # Can't be done in checkParams since it must be after checkProtection
820            self._title = "My rooms"
821            self._ownedBy = self._getUser()
822
823        r = RoomBase()
824        r.capacity = self._capacity
825        r.isActive = self._isActive
826        #r.responsibleId = self._responsibleId
827        r.isReservable = self._isReservable
828        if self._isAutoConfirmed:
829            r.resvsNeedConfirmation = False
830        for eq in self._equipment:
831            r.insertEquipment( eq )
832
833        if self._availability == "Don't care":
834            rooms = CrossLocationQueries.getRooms( location = self._roomLocation, freeText = self._freeSearch, ownedBy = self._ownedBy, roomExample = r )
835            # Special care for capacity (20% => greater than)
836            if len ( rooms ) == 0:
837                rooms = CrossLocationQueries.getRooms( location = self._roomLocation, freeText = self._freeSearch, ownedBy = self._ownedBy, roomExample = r, minCapacity = True )
838        else:
839            # Period specification
840            p = ReservationBase()
841            p.startDT = self._startDT
842            p.endDT = self._endDT
843            p.repeatability = self._repeatability
844            if self._includePrebookings:
845                p.isConfirmed = None   # because it defaults to True
846
847            # Set default values for later booking form
848            self._websession.setVar( "defaultStartDT", p.startDT )
849            self._websession.setVar( "defaultEndDT", p.endDT )
850            self._websession.setVar( "defaultRepeatability", p.repeatability )
851
852            available = ( self._availability == "Available" )
853
854            rooms = CrossLocationQueries.getRooms( \
855                location = self._roomLocation,
856                freeText = self._freeSearch,
857                ownedBy = self._ownedBy,
858                roomExample = r,
859                resvExample = p,
860                available = available )
861            # Special care for capacity (20% => greater than)
862            if len ( rooms ) == 0:
863                rooms = CrossLocationQueries.getRooms( \
864                    location = self._roomLocation,
865                    freeText = self._freeSearch,
866                    ownedBy = self._ownedBy,
867                    roomExample = r,
868                    resvExample = p,
869                    available = available,
870                    minCapacity = True )
871
872        rooms.sort()
873
874        self._rooms = rooms
875
876        self._mapAvailable = Location.getDefaultLocation().isMapAvailable()
877
878    def _process( self ):
879        self._businessLogic()
880        p = roomBooking_wp.WPRoomBookingRoomList( self, self._onlyMy )
881        return p.display()
882
883class RHRoomBookingBookingList( RHRoomBookingBase ):
884
885    def _checkParams( self, params ):
886        self._roomGUIDs = []
887        self._allRooms = False
888        roomGUIDs = params.get( "roomGUID" )
889        if isinstance( roomGUIDs, str ):
890            if roomGUIDs == "allRooms":
891                self._allRooms = True
892                roomGUIDs = [ str(room.guid) for room in CrossLocationQueries.getRooms( allFast = True )]
893            else:
894                roomGUIDs = [roomGUIDs.strip()]
895        if isinstance( roomGUIDs, list )  and  roomGUIDs != ['']:
896            self._roomGUIDs = roomGUIDs
897
898        resvEx = ReservationBase()
899        self._checkParamsRepeatingPeriod( params )
900        resvEx.startDT = self._startDT
901        resvEx.endDT = self._endDT
902        bookedForName = params.get( "bookedForName" )
903        if bookedForName and len( bookedForName.strip() ) > 0:
904            resvEx.bookedForName = bookedForName.strip()
905        reason = params.get( "reason" )
906        if reason and len( reason.strip() ) > 0:
907            resvEx.reason = reason.strip()
908        self._title = "Bookings"
909
910        onlyPrebookings = params.get( "onlyPrebookings" )
911        self._onlyPrebookings = False
912
913        onlyBookings = params.get( "onlyBookings" )
914        self._onlyBookings = False
915
916        if onlyPrebookings and len( onlyPrebookings.strip() ) > 0:
917            if onlyPrebookings == 'on':
918                resvEx.isConfirmed = False
919                self._title = "PRE-" + self._title
920                self._onlyPrebookings = True
921        elif onlyBookings and len( onlyBookings.strip() ) > 0:
922            if onlyBookings == 'on':
923                resvEx.isConfirmed = True
924                self._onlyBookings = True
925        else:
926            # find pre-bookings as well
927            resvEx.isConfirmed = None
928
929        self._onlyMy = False
930        onlyMy = params.get( "onlyMy" )
931        if onlyMy and len( onlyMy.strip() ) > 0:
932            if onlyMy == 'on':
933                self._onlyMy = True
934                self._title = "My " + self._title
935        else:
936            resvEx.createdBy = None
937        self._ofMyRooms = False
938        ofMyRooms = params.get( "ofMyRooms" )
939        if ofMyRooms and len( ofMyRooms.strip() ) > 0:
940            if ofMyRooms == 'on':
941                self._ofMyRooms = True
942                self._title = self._title + " for your rooms"
943        else:
944            self._rooms = None
945
946        self._search = False
947        search = params.get( "search" )
948        if search and len( search.strip() ) > 0:
949            if search == 'on':
950                self._search = True
951                self._title = "Search " + self._title
952
953        self._order = params.get( "order", "" )
954
955        isArchival = params.get( "isArchival" )
956        if isArchival and len( isArchival.strip() ) > 0:
957            self._isArchival = True
958        else:
959            self._isArchival = None
960
961        self._autoCriteria = False
962        if params.get( "autoCriteria" ) == "True" or not resvEx.startDT:
963            now = datetime.now()
964            after = now + timedelta( 7 ) # 1 week later
965
966            resvEx.startDT = datetime( now.year, now.month, now.day, 0, 0, 0 )
967            resvEx.endDT = datetime( after.year, after.month, after.day, 23, 59, 00 )
968            self._autoCriteria = True
969            self._isArchival = None
970
971        isRejected = params.get( "isRejected" )
972        if isRejected and len( isRejected.strip() ) > 0:
973            resvEx.isRejected = isRejected == 'on'
974        else:
975            resvEx.isRejected = False
976        isCancelled = params.get( "isCancelled" )
977        if isCancelled and len( isCancelled.strip() ) > 0:
978            resvEx.isCancelled = isCancelled == 'on'
979        else:
980            resvEx.isCancelled = False
981
982
983        needsAVCSupport = params.get( "needsAVCSupport" )
984        if needsAVCSupport and len( needsAVCSupport.strip() ) > 0:
985            resvEx.needsAVCSupport = needsAVCSupport == 'on'
986        else:
987            resvEx.needsAVCSupport = None
988
989        usesAVC = params.get( "usesAVC" )
990        if usesAVC and len( usesAVC.strip() ) > 0:
991            resvEx.usesAVC = usesAVC == 'on'
992        else:
993            resvEx.usesAVC = None
994
995        isHeavy = params.get( "isHeavy" )
996        if isHeavy and len( isHeavy.strip() ) > 0:
997            self._isHeavy = True
998        else:
999            self._isHeavy = None
1000
1001        self._resvEx = resvEx
1002
1003        session = self._websession
1004        self._prebookingsRejected = session.getVar( 'prebookingsRejected' )
1005        self._subtitle = session.getVar( 'title' )
1006        self._description = session.getVar( 'description' )
1007        session.setVar( 'title', None )
1008        session.setVar( 'description', None )
1009        session.setVar( 'prebookingsRejected', None )
1010
1011
1012    def _process( self ):
1013        # The following can't be done in checkParams since it must be after checkProtection
1014        if self._onlyMy:
1015            self._resvEx.createdBy = str( self._getUser().id )
1016        if self._ofMyRooms:
1017            self._rooms = self._getUser().getRooms()
1018            self._roomGUIDs = None
1019
1020        # Whether to show [Reject ALL conflicting PRE-bookings] button
1021        self._showRejectAllButton = False
1022        if self._rooms and not self._resvEx.isConfirmed:
1023            self._showRejectAllButton = True
1024
1025        if self._roomGUIDs:
1026            rooms = [ RoomGUID.parse( rg ).getRoom() for rg in self._roomGUIDs ]
1027            if self._rooms is list:
1028                self._rooms.extend( rooms )
1029            else:
1030                self._rooms = rooms
1031
1032        # Init
1033        resvEx = self._resvEx
1034
1035        days = None
1036        self._overload = False
1037        if resvEx.startDT and resvEx.endDT:
1038            if ( resvEx.endDT - resvEx.startDT ).days > 400:
1039                self._overload = True
1040                self._resvs = []
1041            else:
1042                # Prepare 'days' so .getReservations will use days index
1043                if resvEx.repeatability == None:
1044                    resvEx.repeatability = RepeatabilityEnum.daily
1045                periods = resvEx.splitToPeriods(endDT = resvEx.endDT)
1046                days = [ period.startDT.date() for period in periods ]
1047                if len( days ) > 32:
1048                    days = None # Using day index won't help
1049
1050        if not self._overload:
1051            self._resvs = CrossLocationQueries.getReservations( resvExample = resvEx, rooms = self._rooms, archival = self._isArchival, heavy = self._isHeavy, days = days )
1052
1053
1054        p = roomBooking_wp.WPRoomBookingBookingList( self )
1055        return p.display()
1056
1057
1058# 3. Details of ...
1059
1060class RHRoomBookingRoomDetails( RHRoomBookingBase ):
1061
1062    def _checkParams( self, params ):
1063        locator = locators.WebLocator()
1064        locator.setRoom( params )
1065        self._room = self._target = locator.getObject()
1066
1067        session = self._websession
1068        self._afterActionSucceeded = session.getVar( "actionSucceeded" )
1069        self._afterDeletionFailed = session.getVar( "deletionFailed" )
1070        self._formMode = session.getVar( "formMode" )
1071
1072        self._searchingStartDT = self._searchingEndDT = None
1073        if not params.get( 'calendarMonths' ):
1074            self._searchingStartDT = session.getVar( "defaultStartDT" )
1075            self._searchingEndDT = session.getVar( "defaultEndDT" )
1076
1077        self._clearSessionState()
1078
1079    def _businessLogic( self ):
1080        pass
1081
1082    def _process( self ):
1083        self._businessLogic()
1084        p = roomBooking_wp.WPRoomBookingRoomDetails( self )
1085        return p.display()
1086
1087class RHRoomBookingRoomStats( RHRoomBookingBase ):
1088
1089    def _checkParams( self, params ):
1090        locator = locators.WebLocator()
1091        locator.setRoom( params )
1092        self._period = params.get("period","pastmonth")
1093        self._room = self._target = locator.getObject()
1094
1095    def _businessLogic( self ):
1096        self._kpiAverageOccupation = self._room.getMyAverageOccupation(self._period)
1097        self._kpiActiveRooms = RoomBase.getNumberOfActiveRooms()
1098        self._kpiReservableRooms = RoomBase.getNumberOfReservableRooms()
1099        self._kpiReservableCapacity, self._kpiReservableSurface = RoomBase.getTotalSurfaceAndCapacity()
1100        # Bookings
1101        st = ReservationBase.getRoomReservationStats(self._room)
1102        self._booking_stats = st
1103        self._totalBookings = st['liveValid'] + st['liveCancelled'] + st['liveRejected'] + st['archivalValid'] + st['archivalCancelled'] + st['archivalRejected']
1104
1105    def _process( self ):
1106        self._businessLogic()
1107        p = roomBooking_wp.WPRoomBookingRoomStats( self )
1108        return p.display()
1109
1110class RHRoomBookingBookingDetails( RHRoomBookingBase ):
1111
1112    def _checkParams( self, params ):
1113
1114        locator = locators.WebLocator()
1115        locator.setRoomBooking( params )
1116        self._resv = self._target = locator.getObject()
1117
1118        self._afterActionSucceeded = self._websession.getVar( "actionSucceeded" )
1119        self._title = self._websession.getVar( "title" )
1120        self._description = self._websession.getVar( "description" )
1121        self._afterDeletionFailed = self._websession.getVar( "deletionFailed" )
1122
1123        self._clearSessionState()
1124
1125    def _businessLogic( self ):
1126        pass
1127
1128    def _process( self ):
1129        self._businessLogic()
1130        p = roomBooking_wp.WPRoomBookingBookingDetails( self )
1131        return p.display()
1132
1133# 4. New
1134
1135class RHRoomBookingBookingForm( RHRoomBookingBase ):
1136
1137    def _checkParams( self, params ):
1138        session = self._websession  # Just an alias
1139        self._thereAreConflicts = session.getVar( 'thereAreConflicts' )
1140        self._skipConflicting = False
1141
1142        # DATA FROM?
1143        self._dataFrom = CandidateDataFrom.DEFAULTS
1144        if params.get( "candDataInParams" ) or params.get( "afterCalPreview" ):
1145            self._dataFrom = CandidateDataFrom.PARAMS
1146        if session.getVar( "candDataInSession" ):
1147            self._dataFrom = CandidateDataFrom.SESSION
1148
1149        # Reservation ID?
1150        resvID = None
1151        if self._dataFrom == CandidateDataFrom.SESSION:
1152            resvID = session.getVar( "resvID" )
1153            roomLocation = session.getVar( "roomLocation" )
1154        else:
1155            resvID = params.get( "resvID" )
1156            if isinstance( resvID, list ): resvID = resvID[0]
1157            roomLocation = params.get( "roomLocation" )
1158            if isinstance( roomLocation, list ): roomLocation = roomLocation[0]
1159        if resvID == "None": resvID = None
1160        if resvID: resvID = int( resvID )
1161
1162        # FORM MODE?
1163        if resvID:
1164            self._formMode = FormMode.MODIF
1165        else:
1166            self._formMode = FormMode.NEW
1167
1168        # SHOW ERRORS?
1169        self._showErrors = self._websession.getVar( "showErrors" )
1170        if self._showErrors:
1171            self._errors = self._websession.getVar( "errors" )
1172
1173        # CREATE CANDIDATE OBJECT
1174        candResv = None
1175
1176        if self._formMode == FormMode.NEW:
1177            if self._dataFrom == CandidateDataFrom.SESSION:
1178                candResv = self._loadResvCandidateFromSession( None, params )
1179            elif self._dataFrom == CandidateDataFrom.PARAMS:
1180                candResv = self._loadResvCandidateFromParams( None, params )
1181            else:
1182                candResv = self._loadResvCandidateFromDefaults( params )
1183
1184        if self._formMode == FormMode.MODIF:
1185            import copy
1186            candResv = copy.copy(CrossLocationQueries.getReservations( resvID = resvID, location = roomLocation ))
1187            if self._dataFrom == CandidateDataFrom.PARAMS:
1188                self._loadResvCandidateFromParams( candResv, params )
1189            if self._dataFrom == CandidateDataFrom.SESSION:
1190                self._loadResvCandidateFromSession( candResv, params )
1191
1192        self._errors = session.getVar( "errors" )
1193
1194        self._candResv = candResv
1195
1196        self._clearSessionState()
1197
1198
1199    def _checkProtection( self ):
1200
1201        RHRoomBookingBase._checkProtection(self)
1202
1203        # only do the remaining checks the rest if the basic ones were successful
1204        # (i.e. user is logged in)
1205        if self._doProcess:
1206            if not self._candResv.room.isActive and not self._getUser().isAdmin():
1207                raise MaKaCError( "You are not authorized to book this room." )
1208
1209            if not self._candResv.room.canBook( self._getUser() ) and not self._candResv.room.canPrebook( self._getUser() ):
1210                raise MaKaCError( "You are not authorized to book this room." )
1211
1212            if self._formMode == FormMode.MODIF:
1213                if not self._candResv.canModify( self.getAW() ):
1214                    raise MaKaCError( "You are not authorized to take this action." )
1215
1216    def _businessLogic( self ):
1217        pass
1218
1219    def _process( self ):
1220        self._businessLogic()
1221        p = roomBooking_wp.WPRoomBookingBookingForm( self )
1222        return p.display()
1223
1224class RHRoomBookingCloneBooking( RHRoomBookingBase ):
1225    """
1226    Performs open a new booking form with the data of an already existing booking.
1227    """
1228
1229    def _checkParams( self, params ):
1230        session = self._websession  # Just an alias
1231
1232        # DATA FROM
1233        session.setVar( "candDataInSession", True )
1234
1235        self._formMode = FormMode.NEW
1236
1237        # Reservation ID
1238        resvID = int(params.get( "resvID" ))
1239
1240        # CREATE CANDIDATE OBJECT
1241        candResv = CrossLocationQueries.getReservations( resvID = resvID)
1242        if type(candResv) == list:
1243            candResv=candResv[0]
1244        self._saveResvCandidateToSession(candResv)
1245        self._room = candResv.room
1246
1247
1248    def _process( self ):
1249        self._redirect(urlHandlers.UHRoomBookingBookingForm.getURL(self._room))
1250
1251
1252class RHRoomBookingSaveBooking( RHRoomBookingBase ):
1253    """
1254    Performs physical INSERT or UPDATE.
1255    When succeeded redirects to booking details, otherwise returns to booking
1256    form.
1257    """
1258
1259    def _checkParams( self, params ):
1260
1261        self._roomLocation = params.get("roomLocation")
1262        self._roomID       = params.get("roomID")
1263        self._resvID             = params.get( "resvID" )
1264        if self._resvID == 'None':
1265            self._resvID = None
1266
1267        # if the user is not logged in it will be redirected
1268        # to the login page by the _checkProtection, so we don't
1269        # need to store info in the session or process the other params
1270        if not self._getUser():
1271            return
1272
1273        self._answer = params.get( "answer", None )
1274
1275        self._skipConflicting = False
1276
1277        # forceAddition is set by the confirmation dialog, so that
1278        # prebookings that conflict with other prebookings are
1279        # silently added
1280
1281        self._forceAddition = params.get("forceAddition","False")
1282        if self._forceAddition == 'True':
1283            self._forceAddition = True
1284        else:
1285            self._forceAddition = False
1286
1287        candResv = None
1288        if self._resvID:
1289            self._formMode = FormMode.MODIF
1290            self._resvID = int( self._resvID )
1291            _candResv = CrossLocationQueries.getReservations( resvID = self._resvID, location = self._roomLocation )
1292            self._orig_candResv = _candResv
1293
1294            # Creates a "snapshot" of the reservation's attributes before modification
1295            self._resvAttrsBefore = self._orig_candResv.createSnapshot()
1296
1297            import copy
1298            candResv = copy.copy(_candResv)
1299
1300            if self._forceAddition:
1301                # booking data comes from session if confirmation was required
1302                self._loadResvCandidateFromSession( candResv, params )
1303            else:
1304                self._loadResvCandidateFromParams( candResv, params )
1305
1306            # Creates a "snapshot" of the reservation's attributes after modification
1307            self._resvAttrsAfter = candResv.createSnapshot()
1308
1309        else:
1310            self._formMode = FormMode.NEW
1311            candResv = Location.parse( self._roomLocation ).factory.newReservation()
1312            candResv.createdDT = datetime.now()
1313            candResv.createdBy = str( self._getUser().id )
1314            candResv.isRejected = False
1315            candResv.isCancelled = False
1316
1317            if self._forceAddition:
1318                # booking data comes from session if confirmation was required
1319                self._loadResvCandidateFromSession( candResv, params )
1320            else:
1321                self._loadResvCandidateFromParams( candResv, params )
1322
1323            self._resvID = None
1324
1325
1326        self._candResv = candResv
1327
1328        for nbd in self._candResv.room.getNonBookableDates():
1329            if (doesPeriodsOverlap(nbd.getStartDate(),nbd.getEndDate(),self._candResv.startDT,self._candResv.endDT)):
1330                raise FormValuesError("You cannot book this room during the following periods due to maintenance reasons: %s"%("; ".join(map(lambda x: "from %s to %s"%(x.getStartDate().strftime("%d/%m/%Y"),x.getEndDate().strftime("%d/%m/%Y")), self._candResv.room.getNonBookableDates()))))
1331
1332        self._params = params
1333        self._clearSessionState()
1334
1335
1336    def _checkProtection( self ):
1337        # If the user is not logged in, we redirect the same reservation page
1338        if not self._getUser():
1339            self._redirect(urlHandlers.UHSignIn.getURL(
1340                                    returnURL = urlHandlers.UHRoomBookingBookingForm.getURL(
1341                                            roomID = self._roomID,
1342                                            resvID = self._resvID,
1343                                            roomLocation = self._roomLocation )))
1344            self._doProcess = False
1345        else:
1346            RHRoomBookingBase._checkProtection(self)
1347            if not self._candResv.room.isActive and not self._getUser().isAdmin():
1348                raise MaKaCError( "You are not authorized to book this room." )
1349
1350            if self._formMode == FormMode.MODIF:
1351                if not self._candResv.canModify( self.getAW() ):
1352                    raise MaKaCError( "You are not authorized to take this action." )
1353
1354    def _businessLogic( self ):
1355
1356        candResv = self._candResv
1357        self._emailsToBeSent = []
1358        self._confirmAdditionFirst = False;
1359
1360        # Set confirmation status
1361        candResv.isConfirmed = True
1362        user = self._getUser()
1363        if not candResv.room.canBook( user ):
1364            candResv.isConfirmed = False
1365
1366
1367        errors = self._getErrorsOfResvCandidate( candResv )
1368        session = self._websession
1369
1370        if not errors and self._answer != 'No':
1371
1372            isConfirmed = candResv.isConfirmed
1373            candResv.isConfirmed = None
1374            # find pre-booking collisions
1375            self._collisions = candResv.getCollisions(sansID=candResv.id)
1376            candResv.isConfirmed = isConfirmed
1377
1378            if not self._forceAddition and self._collisions:
1379                # save the reservation to the session
1380                self._saveResvCandidateToSession( candResv )
1381                # ask for confirmation about the pre-booking
1382                self._confirmAdditionFirst = True
1383
1384
1385            # approved pre-booking or booking
1386            if not self._confirmAdditionFirst:
1387
1388                # Form is OK and (no conflicts or skip conflicts)
1389                if self._formMode == FormMode.NEW:
1390                    candResv.insert()
1391                    self._emailsToBeSent += candResv.notifyAboutNewReservation()
1392                    if candResv.isConfirmed:
1393                        session.setVar( "title", 'You have successfully made a booking.' )
1394                        session.setVar( "description", 'NOTE: Your booking is complete. However, be <b>aware</b> that in special cases the person responsible for a room may reject your booking. In that case you would be instantly notified by e-mail.' )
1395                    else:
1396                        session.setVar( "title", 'You have successfully made a <span style="color: Red;">PRE</span>-booking.' )
1397                        session.setVar( "description", 'NOTE: PRE-bookings are subject to acceptance or rejection. Expect an e-mail with acceptance/rejection information.' )
1398                elif self._formMode == FormMode.MODIF:
1399                    self._orig_candResv.unindexDayReservations()
1400                    if self._forceAddition:
1401                        self._loadResvCandidateFromSession( self._orig_candResv, self._params )
1402                    else:
1403                        self._loadResvCandidateFromParams( self._orig_candResv, self._params )
1404                    self._orig_candResv.update()
1405                    self._orig_candResv.indexDayReservations()
1406                    self._emailsToBeSent += self._orig_candResv.notifyAboutUpdate()
1407
1408                    # Add entry to the log
1409                    info = []
1410                    self._orig_candResv.getResvHistory().getResvModifInfo(info, self._resvAttrsBefore , self._resvAttrsAfter)
1411
1412                    # If no modification was observed ("Save" was pressed but no field
1413                    # was changed) no entry is added to the log
1414                    if len(info) > 1 :
1415                        histEntry = ResvHistoryEntry(self._getUser(), info, self._emailsToBeSent)
1416                        self._orig_candResv.getResvHistory().addHistoryEntry(histEntry)
1417
1418                    session.setVar( "title", 'Booking updated.' )
1419                    session.setVar( "description", 'Please review details below.' )
1420
1421                session.setVar( "actionSucceeded", True )
1422
1423                # Booking - reject all colliding PRE-Bookings
1424                if candResv.isConfirmed and self._collisions:
1425                    rejectionReason = "Conflict with booking: %s" % urlHandlers.UHRoomBookingBookingDetails.getURL(candResv)
1426                    for coll in self._collisions:
1427                        collResv = coll.withReservation
1428                        if collResv.repeatability is None: # not repeatable -> reject whole booking. easy :)
1429                            collResv.rejectionReason = rejectionReason
1430                            collResv.reject()    # Just sets isRejected = True
1431                            collResv.update()
1432                            emails = collResv.notifyAboutRejection()
1433                            self._emailsToBeSent += emails
1434
1435                            # Add entry to the booking history
1436                            info = []
1437                            info.append("Booking rejected")
1438                            info.append("Reason: '%s'" % collResv.rejectionReason)
1439                            histEntry = ResvHistoryEntry(self._getUser(), info, emails)
1440                            collResv.getResvHistory().addHistoryEntry(histEntry)
1441                        else: # repeatable -> only reject the specific days
1442                            rejectDate = coll.startDT.date()
1443                            collResv.excludeDay(rejectDate, unindex=True)
1444                            collResv.update()
1445                            emails = collResv.notifyAboutRejection(date=rejectDate, reason=rejectionReason)
1446                            self._emailsToBeSent += emails
1447
1448                            # Add entry to the booking history
1449                            info = []
1450                            info.append("Booking occurence of the %s rejected" % rejectDate.strftime("%d %b %Y"))
1451                            info.append("Reason: '%s'" % rejectionReason)
1452                            histEntry = ResvHistoryEntry(self._getUser(), info, emails)
1453                            collResv.getResvHistory().addHistoryEntry(histEntry)
1454
1455        else:
1456            session.setVar( "candDataInSession", True )
1457            session.setVar( "errors", errors )
1458
1459            if self._answer == 'No':
1460                session.setVar( "actionSucceeded", True )
1461            else:
1462                session.setVar( "actionSucceeded", False )
1463                session.setVar( "showErrors", True )
1464                session.setVar( "thereAreConflicts", self._thereAreConflicts )
1465
1466            self._saveResvCandidateToSession( candResv )
1467
1468        # Form is not properly filled OR there are conflicts
1469        self._errors = errors
1470
1471    def _process( self ):
1472
1473        self._businessLogic()
1474
1475        if self._errors or self._answer == 'No':
1476            url = urlHandlers.UHRoomBookingBookingForm.getURL( self._candResv.room, resvID=self._resvID )
1477        elif self._confirmAdditionFirst:
1478            p = roomBooking_wp.WPRoomBookingConfirmBooking( self )
1479            return p.display()
1480        else:
1481            url = urlHandlers.UHRoomBookingBookingDetails.getURL( self._candResv )
1482
1483        self._redirect( url )
1484
1485
1486class RHRoomBookingRoomForm( RHRoomBookingAdminBase ):
1487    """
1488    Form for creating NEW and MODIFICATION of an existing room.
1489    """
1490
1491    def _checkParams( self, params ):
1492        session = self._websession  # Just an alias
1493
1494        # DATA FROM?
1495        self._dataFrom = CandidateDataFrom.DEFAULTS
1496        if params.get( "candDataInParams" ):
1497            self._dataFrom = CandidateDataFrom.PARAMS
1498        if session.getVar( "candDataInSession" ):
1499            self._dataFrom = CandidateDataFrom.SESSION
1500
1501        # Room ID?
1502        roomID = None
1503        roomLocation = None
1504        if self._dataFrom == CandidateDataFrom.SESSION:
1505            roomID = session.getVar( "roomID" )
1506            roomLocation = session.getVar( "roomLocation" )
1507        else:
1508            roomID = params.get( "roomID" )
1509            roomLocation = params.get( "roomLocation" )
1510        if roomID: roomID = int( roomID )
1511
1512        # FORM MODE?
1513        if roomID:
1514            self._formMode = FormMode.MODIF
1515        else:
1516            self._formMode = FormMode.NEW
1517
1518        # SHOW ERRORS?
1519        self._showErrors = self._websession.getVar( "showErrors" )
1520        if self._showErrors:
1521            self._errors = self._websession.getVar( "errors" )
1522
1523        # CREATE CANDIDATE OBJECT
1524        candRoom = None
1525
1526        if self._formMode == FormMode.NEW:
1527            locationName = params.get("roomLocation", "")
1528            location = Location.parse(locationName)
1529            if str(location) == "None":
1530                # Room should be inserted into default backend => using Factory
1531                candRoom = Factory.newRoom()
1532            else:
1533                candRoom = location.newRoom()
1534            if self._dataFrom == CandidateDataFrom.SESSION:
1535                self._loadRoomCandidateFromSession( candRoom )
1536            else:
1537                self._loadRoomCandidateFromDefaults( candRoom )
1538
1539        if self._formMode == FormMode.MODIF:
1540            candRoom = CrossLocationQueries.getRooms( roomID = roomID, location = roomLocation )
1541
1542            if self._dataFrom == CandidateDataFrom.PARAMS:
1543                self._loadRoomCandidateFromParams( candRoom, params )
1544            if self._dataFrom == CandidateDataFrom.SESSION:
1545                self._loadRoomCandidateFromSession( candRoom )
1546
1547        self._errors = session.getVar( "errors" )
1548
1549        # After searching for responsible
1550        if params.get( 'selectedPrincipals' ):
1551            candRoom.responsibleId = params['selectedPrincipals']
1552
1553        self._candRoom = self._target = candRoom
1554        self._clearSessionState()
1555
1556    def _process( self ):
1557        p = admins.WPRoomBookingRoomForm( self )
1558        return p.display()
1559
1560class RHRoomBookingSaveRoom( RHRoomBookingAdminBase ):
1561    """
1562    Performs physical INSERT or UPDATE.
1563    When succeeded redirects to room details, otherwise returns to room form.
1564    """
1565
1566    def _uploadPhotos( self, candRoom, params ):
1567        if (params.get( "largePhotoPath" ) and params.get( "smallPhotoPath" )
1568            and params["largePhotoPath"].filename and params["smallPhotoPath"].filename):
1569            candRoom.savePhoto( params["largePhotoPath"] )
1570            candRoom.saveSmallPhoto( params["smallPhotoPath"] )
1571
1572    def _checkParams( self, params ):
1573        roomID = params.get( "roomID" )
1574        roomLocation = params.get( "roomLocation" )
1575
1576        candRoom = None
1577        if roomID:
1578            self._formMode = FormMode.MODIF
1579            if roomID: roomID = int( roomID )
1580            candRoom = CrossLocationQueries.getRooms( roomID = roomID, location = roomLocation )
1581        else:
1582            self._formMode = FormMode.NEW
1583            if roomLocation:
1584                name = Location.parse(roomLocation).friendlyName
1585            else:
1586                name = Location.getDefaultLocation().friendlyName
1587            location = Location.parse(name)
1588            candRoom = location.newRoom()
1589            candRoom.locationName = name
1590
1591        self._loadRoomCandidateFromParams( candRoom, params )
1592        self._candRoom = self._target = candRoom
1593        self._params = params
1594
1595    def _process( self ):
1596        candRoom = self._candRoom
1597        params = self._params
1598
1599        errors = self._getErrorsOfRoomCandidate( candRoom )
1600        if not errors:
1601            # Succeeded
1602            if self._formMode == FormMode.MODIF:
1603                candRoom.update()
1604                # if responsibleId changed
1605                candRoom.notifyAboutResponsibility()
1606                url = urlHandlers.UHRoomBookingRoomDetails.getURL(candRoom)
1607
1608            elif self._formMode == FormMode.NEW:
1609                candRoom.insert()
1610                candRoom.notifyAboutResponsibility()
1611                url = urlHandlers.UHRoomBookingAdminLocation.getURL(Location.parse(candRoom.locationName), actionSucceeded = True)
1612
1613            self._uploadPhotos( candRoom, params )
1614            self._websession.setVar( "actionSucceeded", True )
1615            self._websession.setVar( "formMode", self._formMode )
1616            self._redirect( url ) # Redirect to room DETAILS
1617        else:
1618            # Failed
1619            self._websession.setVar( "actionSucceeded", False )
1620            self._websession.setVar( "candDataInSession", True )
1621            self._websession.setVar( "errors", errors )
1622            self._websession.setVar( "showErrors", True )
1623
1624            self._saveRoomCandidateToSession( candRoom )
1625            url = urlHandlers.UHRoomBookingRoomForm.getURL( None )
1626            self._redirect( url ) # Redirect again to FORM
1627
1628
1629class RHRoomBookingDeleteRoom( RHRoomBookingAdminBase ):
1630
1631    def _checkParams( self , params ):
1632        roomID = params.get( "roomID" )
1633        roomID = int( roomID )
1634        roomLocation = params.get( "roomLocation" )
1635        self._room = CrossLocationQueries.getRooms( roomID = roomID, location = roomLocation )
1636        self._target = self._room
1637
1638    def _process( self ):
1639        room = self._room
1640        # Check whether deletion is possible
1641        liveResv = room.getLiveReservations()
1642        if len( liveResv ) == 0:
1643            # Possible - delete
1644            for resv in room.getReservations():
1645                resv.remove()
1646            room.remove()
1647            self._websession.setVar( 'title', "Room has been deleted." )
1648            self._websession.setVar( 'description', "You have successfully deleted the room. All its archival, cancelled and rejected bookings have also been deleted." )
1649            url = urlHandlers.UHRoomBookingStatement.getURL()
1650            self._redirect( url ) # Redirect to deletion confirmation
1651        else:
1652            # Impossible
1653            self._websession.setVar( 'deletionFailed', True )
1654            url = urlHandlers.UHRoomBookingRoomDetails.getURL( room )
1655            self._redirect( url ) # Redirect to room DETAILS
1656
1657class RHRoomBookingDeleteBooking( RHRoomBookingAdminBase ):
1658
1659    def _checkParams( self , params ):
1660        resvID = int( params.get( "resvID" ) )
1661        roomLocation = params.get( "roomLocation" )
1662        self._resv = CrossLocationQueries.getReservations( resvID = resvID, location = roomLocation )
1663        self._target = self._resv
1664
1665    def _process( self ):
1666        # Booking deletion is always possible - just delete
1667        self._resv.remove()
1668        self._websession.setVar( 'title', "Booking has been deleted." )
1669        self._websession.setVar( 'description', "You have successfully deleted the booking." )
1670        url = urlHandlers.UHRoomBookingStatement.getURL()
1671        self._redirect( url ) # Redirect to deletion confirmation
1672
1673class RHRoomBookingCancelBooking( RHRoomBookingBase ):
1674
1675    def _checkParams( self , params ):
1676        resvID = int( params.get( "resvID" ) )
1677        roomLocation = params.get( "roomLocation" )
1678        self._resv = CrossLocationQueries.getReservations( resvID = resvID, location = roomLocation )
1679        self._target = self._resv
1680
1681    def _checkProtection( self ):
1682        RHRoomBookingBase._checkProtection(self)
1683        user = self._getUser()
1684        # Only owner (the one who created) and admin can CANCEL
1685        # (Responsible can not cancel a booking!)
1686        if ( not self._resv.isOwnedBy( user ) ) and \
1687            ( not self._getUser().isAdmin() ):
1688                raise MaKaCError( "You are not authorized to take this action." )
1689
1690    def _process( self ):
1691        # Booking deletion is always possible - just delete
1692        self._emailsToBeSent = []
1693        self._resv.cancel()    # Just sets isCancel = True
1694        self._resv.update()
1695        self._emailsToBeSent += self._resv.notifyAboutCancellation()
1696
1697        # Add entry to the booking history
1698        info = []
1699        info.append("Booking cancelled")
1700        histEntry = ResvHistoryEntry(self._getUser(), info, self._emailsToBeSent)
1701        self._resv.getResvHistory().addHistoryEntry(histEntry)
1702
1703        self._websession.setVar( 'actionSucceeded', True )
1704        self._websession.setVar( 'title', "Booking has been cancelled." )
1705        self._websession.setVar( 'description', "You have successfully cancelled the booking." )
1706        url = urlHandlers.UHRoomBookingBookingDetails.getURL( self._resv )
1707        self._redirect( url ) # Redirect to booking details
1708
1709
1710class RHRoomBookingCancelBookingOccurrence( RHRoomBookingBase ):
1711
1712    def _checkParams( self , params ):
1713        resvID = int( params.get( "resvID" ) )
1714        roomLocation = params.get( "roomLocation" )
1715        date = params.get( "date" )
1716
1717        self._resv = CrossLocationQueries.getReservations( resvID = resvID, location = roomLocation )
1718        self._date = parse_date( date )
1719        self._target = self._resv
1720
1721    def _checkProtection( self ):
1722        RHRoomBookingBase._checkProtection(self)
1723        user = self._getUser()
1724        # Only user/admin can cancell a booking occurrence
1725        # (Owner can not reject his own booking, he should cancel instead)
1726        if self._resv.createdBy != user.getId() and (not user.isAdmin()):
1727                raise MaKaCError( "You are not authorized to take this action." )
1728
1729    def _process( self ):
1730        self._emailsToBeSent = []
1731        self._resv.excludeDay( self._date, unindex=True )
1732        self._resv.update()
1733        self._emailsToBeSent += self._resv.notifyAboutCancellation( date = self._date )
1734
1735        # Add entry to the booking history
1736        info = []
1737        info.append("Booking occurence of the %s cancelled" %self._date.strftime("%d %b %Y"))
1738        histEntry = ResvHistoryEntry(self._getUser(), info, self._emailsToBeSent)
1739        self._resv.getResvHistory().addHistoryEntry(histEntry)
1740
1741        self._websession.setVar( 'actionSucceeded', True )
1742        self._websession.setVar( 'title', "Selected occurrence has been cancelled." )
1743        self._websession.setVar( 'description', "You have successfully cancelled an occurrence of this booking." )
1744        url = urlHandlers.UHRoomBookingBookingDetails.getURL( self._resv )
1745        self._redirect( url ) # Redirect to booking details
1746
1747
1748class RHRoomBookingRejectBooking( RHRoomBookingBase ):
1749
1750    def _checkParams( self , params ):
1751        resvID = int( params.get( "resvID" ) )
1752        roomLocation = params.get( "roomLocation" )
1753        reason = params.get( "reason" )
1754
1755        self._resv = CrossLocationQueries.getReservations( resvID = resvID, location = roomLocation )
1756        self._resv.rejectionReason = reason
1757        self._target = self._resv
1758
1759    def _checkProtection( self ):
1760        RHRoomBookingBase._checkProtection(self)
1761        user = self._getUser()
1762        # Only responsible and admin can REJECT
1763        # (Owner can not reject his own booking, he should cancel instead)
1764        if ( not self._resv.room.isOwnedBy( user ) ) and \
1765            ( not self._getUser().isAdmin() ):
1766                raise MaKaCError( "You are not authorized to take this action." )
1767
1768    def _process( self ):
1769        self._emailsToBeSent = []
1770        self._resv.reject()    # Just sets isRejected = True
1771        self._resv.update()
1772        self._emailsToBeSent += self._resv.notifyAboutRejection()
1773
1774        # Add entry to the booking history
1775        info = []
1776        info.append("Booking rejected")
1777        info.append("Reason : '%s'" %self._resv.rejectionReason)
1778        histEntry = ResvHistoryEntry(self._getUser(), info, self._emailsToBeSent)
1779        self._resv.getResvHistory().addHistoryEntry(histEntry)
1780
1781        self._websession.setVar( 'actionSucceeded', True )
1782        self._websession.setVar( 'title', "Booking has been rejected." )
1783        self._websession.setVar( 'description', "NOTE: rejection e-mail has been sent to the user. However, it's advisable to <strong>inform</strong> the user directly. Note that users often don't read e-mails." )
1784        url = urlHandlers.UHRoomBookingBookingDetails.getURL( self._resv )
1785        self._redirect( url ) # Redirect to booking details
1786
1787
1788class RHRoomBookingRejectALlConflicting( RHRoomBookingBase ):
1789
1790#    def _checkParams( self , params ):
1791#        pass
1792
1793    def _checkProtection( self ):
1794        RHRoomBookingBase._checkProtection(self)
1795        user = self._getUser()
1796        # Only responsible and admin can REJECT
1797        # (Owner can not reject his own booking, he should cancel instead)
1798        if ( not user.getRooms() ) and \
1799            ( not self._getUser().isAdmin() ):
1800                raise MaKaCError( "You are not authorized to take this action." )
1801
1802    def _process( self ):
1803        userRooms = self._getUser().getRooms()
1804        self._emailsToBeSent = []
1805
1806        resvEx = ReservationBase()
1807        resvEx.isConfirmed = False
1808        resvEx.isRejected = False
1809        resvEx.isCancelled = False
1810
1811        resvs = CrossLocationQueries.getReservations( resvExample = resvEx, rooms = userRooms )
1812
1813        counter = 0
1814        for resv in resvs:
1815            if resv.getCollisions( sansID = resv.id, boolResult = True ):
1816                resv.rejectionReason = "Your PRE-booking conflicted with exiting booking. (Please note it IS possible even if you were the first one to PRE-book the room)."
1817                resv.reject()    # Just sets isRejected = True
1818                resv.update()
1819                self._emailsToBeSent += resv.notifyAboutRejection()
1820                counter += 1
1821                # Add entry to the history of the rejected reservation
1822                info = []
1823                info.append("Booking rejected due to conflict with existing booking")
1824                histEntry = ResvHistoryEntry(self._getUser(), info, self._emailsToBeSent)
1825                resv.getResvHistory().addHistoryEntry(histEntry)
1826        self._websession.setVar( 'prebookingsRejected', True )
1827        if counter > 0:
1828            self._websession.setVar( 'title', str( counter ) + " conflicting PRE-bookings have been rejected." )
1829            self._websession.setVar( 'description', "Rejection e-mails have been sent to the users, with explanation that their PRE-bookings conflicted with the present confirmed bookings." )
1830        else:
1831            self._websession.setVar( 'title', "There are no conflicting PRE-bookings for your rooms." )
1832            self._websession.setVar( 'description', "" )
1833        url = urlHandlers.UHRoomBookingBookingList.getURL( ofMyRooms = True, onlyPrebookings = True, autoCriteria = True )
1834        self._redirect( url ) # Redirect to booking details
1835
1836class RHRoomBookingAcceptBooking( RHRoomBookingBase ):
1837
1838    def _checkParams( self , params ):
1839        resvID = int( params.get( "resvID" ) )
1840        roomLocation = params.get( "roomLocation" )
1841        self._resv = CrossLocationQueries.getReservations( resvID = resvID, location = roomLocation )
1842        self._target = self._resv
1843
1844    def _checkProtection( self ):
1845        RHRoomBookingBase._checkProtection(self)
1846
1847        # only do the remaining checks the rest if the basic ones were successful
1848        # (i.e. user is logged in)
1849        if self._doProcess:
1850            user = self._getUser()
1851            # Only responsible and admin can ACCEPT
1852            if ( not self._resv.room.isOwnedBy( user ) ) and \
1853                ( not self._getUser().isAdmin() ):
1854                raise MaKaCError( "You are not authorized to take this action." )
1855
1856    def _process( self ):
1857        self._emailsToBeSent = []
1858        session = self._websession
1859        if len( self._resv.getCollisions( sansID = self._resv.id ) ) == 0:
1860            # No conflicts
1861            self._resv.isConfirmed = True
1862            self._resv.update()
1863            self._emailsToBeSent += self._resv.notifyAboutConfirmation()
1864
1865            # Add entry to the booking history
1866            info = []
1867            info.append("Booking accepted")
1868            histEntry = ResvHistoryEntry(self._getUser(), info, self._emailsToBeSent)
1869            self._resv.getResvHistory().addHistoryEntry(histEntry)
1870
1871            session.setVar( 'actionSucceeded', True )
1872            session.setVar( 'title', "Booking has been accepted." )
1873            session.setVar( 'description', "NOTE: confirmation e-mail has been sent to the user." )
1874            url = urlHandlers.UHRoomBookingBookingDetails.getURL( self._resv )
1875            self._redirect( url ) # Redirect to booking details
1876        else:
1877            errors = ["PRE-Booking conflicts with other (confirmed) bookings."]
1878            session.setVar( "actionSucceeded", False )
1879            session.setVar( "candDataInSession", True )
1880            session.setVar( "errors", errors )
1881            session.setVar( "showErrors", True )
1882            session.setVar( "thereAreConflicts", True )
1883
1884            session.setVar( 'title', "PRE-Booking conflicts with other (confirmed) bookings." )
1885            session.setVar( 'description', "" )
1886
1887            self._formMode = FormMode.MODIF
1888            self._saveResvCandidateToSession( self._resv )
1889            url = urlHandlers.UHRoomBookingBookingForm.getURL( self._resv.room )
1890            self._redirect( url ) # Redirect to booking details
1891
1892
1893class RHRoomBookingStatement( RHRoomBookingBase ):
1894
1895    def _checkParams( self , params ):
1896        session = self._websession
1897        self._title = session.getVar( 'title' )
1898        self._description = session.getVar( 'description' )
1899        session.setVar( 'title', None )
1900        session.setVar( 'description', None )
1901
1902    def _process( self ):
1903        return roomBooking_wp.WPRoomBookingStatement( self ).display()
1904
1905class RHRoomBookingAdmin( RHRoomBookingAdminBase ):
1906
1907    def _process( self ):
1908        return admins.WPRoomBookingAdmin( self ).display()
1909
1910class RHRoomBookingAdminLocation( RHRoomBookingAdminBase ):
1911
1912    def _checkParams( self, params ):
1913        self._withKPI = False
1914        if params.get( 'withKPI' ) == 'True':
1915            self._withKPI = True
1916        name = params.get("locationId","")
1917        self._location = Location.parse(name)
1918        if str(self._location) == "None":
1919            raise MaKaCError( "%s: Unknown Location" % name )
1920
1921        if params.get("actionSucceeded", None):
1922            self._actionSucceeded = True
1923        else:
1924            self._actionSucceeded = False
1925
1926    def _process( self ):
1927
1928        if self._withKPI:
1929            self._kpiAverageOccupation = RoomBase.getAverageOccupation(location=self._location.friendlyName)
1930            self._kpiTotalRooms = RoomBase.getNumberOfRooms(location=self._location.friendlyName)
1931            self._kpiActiveRooms = RoomBase.getNumberOfActiveRooms(location=self._location.friendlyName)
1932            self._kpiReservableRooms = RoomBase.getNumberOfReservableRooms(location=self._location.friendlyName)
1933            self._kpiReservableCapacity, self._kpiReservableSurface = RoomBase.getTotalSurfaceAndCapacity(location=self._location.friendlyName)
1934
1935            # Bookings
1936
1937            st = ReservationBase.getReservationStats(location=self._location.friendlyName)
1938            self._booking_stats = st
1939            self._totalBookings = st['liveValid'] + st['liveCancelled'] + st['liveRejected'] + st['archivalValid'] + st['archivalCancelled'] + st['archivalRejected']
1940
1941        return admins.WPRoomBookingAdminLocation( self, self._location, actionSucceeded = self._actionSucceeded ).display()
1942
1943
1944class RHRoomBookingSetDefaultLocation( RHRoomBookingAdminBase ):
1945
1946    def _checkParams( self , params ):
1947        self._defaultLocation = params["defaultLocation"]
1948
1949    def _process( self ):
1950        Location.setDefaultLocation( self._defaultLocation )
1951        url = urlHandlers.UHRoomBookingAdmin.getURL()
1952        self._redirect( url )
1953
1954class RHRoomBookingSaveLocation( RHRoomBookingAdminBase ):
1955
1956    def _checkParams( self , params ):
1957        self._locationName = params["newLocationName"].strip()
1958        self._pluginClass = None
1959        name = params.get("pluginName","default")
1960        plugs = PluginLoader.getPluginsByType("RoomBooking")
1961        for plug in plugs:
1962            if pluginId(plug) == name:
1963                self._pluginClass = plug.roombooking.getRBClass()
1964        if self._pluginClass == None:
1965            raise MaKaCError( "%s: Cannot find requested plugin" % name )
1966
1967    def _process( self ):
1968        if self._locationName:
1969            location = Location( self._locationName, self._pluginClass )
1970            Location.insertLocation( location )
1971
1972        url = urlHandlers.UHRoomBookingAdmin.getURL()
1973        self._redirect( url )
1974
1975class RHRoomBookingDeleteLocation( RHRoomBookingAdminBase ):
1976
1977    def _checkParams( self , params ):
1978        self._locationName = params["removeLocationName"]
1979
1980    def _process( self ):
1981
1982        if self._locationName:
1983            Location.removeLocation( self._locationName )
1984        url = urlHandlers.UHRoomBookingAdmin.getURL()
1985        self._redirect( url )
1986
1987class RHRoomBookingSaveEquipment( RHRoomBookingAdminBase ):
1988
1989    def _checkParams( self , params ):
1990        self._eq = params["newEquipmentName"].strip()
1991        name = params.get("locationId","")
1992        self._location = Location.parse(name)
1993        if str(self._location) == "None":
1994            raise MaKaCError( "%s: Unknown Location" % name )
1995
1996    def _process( self ):
1997        if self._eq:
1998            self._location.factory.getEquipmentManager().insertEquipment( self._eq, location=self._location.friendlyName )
1999        url = urlHandlers.UHRoomBookingAdminLocation.getURL(self._location)
2000        self._redirect( url )
2001
2002class RHRoomBookingDeleteEquipment( RHRoomBookingAdminBase ):
2003
2004    def _checkParams( self , params ):
2005        self._eq = params["removeEquipmentName"]
2006        name = params.get("locationId","")
2007        self._location = Location.parse(name)
2008        if str(self._location) == "None":
2009            raise MaKaCError( "%s: Unknown Location" % name )
2010
2011    def _process( self ):
2012        self._location.factory.getEquipmentManager().removeEquipment( self._eq, location=self._location.friendlyName )
2013        url = urlHandlers.UHRoomBookingAdminLocation.getURL(self._location)
2014        self._redirect( url )
2015
2016class RHRoomBookingSaveCustomAttribute( RHRoomBookingAdminBase ): # + additional
2017
2018    def _checkParams( self , params ):
2019        name = params.get("locationId","")
2020        self._location = Location.parse(name)
2021        if str(self._location) == "None":
2022            raise MaKaCError( "%s: Unknown Location" % name )
2023
2024        self._newAttr = None
2025        if params.get( "newCustomAttributeName" ):
2026            attrName = params["newCustomAttributeName"].strip()
2027            if attrName:
2028                attrIsReq = False
2029                if params.get( "newCustomAttributeIsRequired" ) == "on":
2030                    attrIsReq = True
2031                attrIsHidden = False
2032                if params.get( "newCustomAttributeIsHidden" ) == "on":
2033                    attrIsHidden = True
2034                self._newAttr = { \
2035                    'name': attrName,
2036                    'type': 'str',
2037                    'required': attrIsReq,
2038                    'hidden': attrIsHidden }
2039
2040        # Set "required" for _all_ custom attributes
2041        manager = self._location.factory.getCustomAttributesManager()
2042        for ca in manager.getAttributes(location=self._location.friendlyName):
2043            required = hidden = False
2044            # Try to find in params (found => required == True)
2045            for k in params.iterkeys():
2046                if k[0:10] == "cattr_req_":
2047                    attrName = k[10:100].strip()
2048                    if attrName == ca['name']:
2049                        required = True
2050                if k[0:10] == "cattr_hid_":
2051                    attrName = k[10:100].strip()
2052                    if attrName == ca['name']:
2053                        hidden = True
2054            manager.setRequired( ca['name'], required, location=self._location.friendlyName )
2055            manager.setHidden( ca['name'], hidden, location=self._location.friendlyName )
2056
2057    def _process( self ):
2058        if self._newAttr:
2059            self._location.factory.getCustomAttributesManager().insertAttribute( self._newAttr, location=self._location.friendlyName )
2060        url = urlHandlers.UHRoomBookingAdminLocation.getURL(self._location)
2061        self._redirect( url )
2062
2063class RHRoomBookingDeleteCustomAttribute( RHRoomBookingAdminBase ):  # + additional
2064
2065    def _checkParams( self , params ):
2066        self._attr = params["removeCustomAttributeName"]
2067        name = params.get("locationId","")
2068        self._location = Location.parse(name)
2069        if str(self._location) == "None":
2070            raise MaKaCError( "%s: Unknown Location" % name )
2071
2072    def _process( self ):
2073        self._location.factory.getCustomAttributesManager().removeAttribute( self._attr, location=self._location.friendlyName )
2074        url = urlHandlers.UHRoomBookingAdminLocation.getURL(self._location)
2075        self._redirect( url )
2076
2077class RHRoomBookingSendRoomPhoto( RHRoomBookingBase ):
2078
2079    def _checkParams( self, params ):
2080        self.fileName = params['photoId'] + ".jpg"
2081        self.small = params.get( 'small' ) == 'True' # Large by default
2082
2083    def _process( self ):
2084        cfg = Config.getInstance()
2085
2086        filePath = cfg.getRoomPhotosDir()
2087        if self.small:
2088            filePath = cfg.getRoomSmallPhotosDir()
2089        fullPath = os.path.join( filePath, self.fileName )
2090
2091        self._req.content_type = "image/jpeg"
2092        #self._req.headers_out["Content-Disposition"] = "inline; filename=\"%s\"" % self.fileName
2093        self._req.sendfile( fullPath )
2094
2095
2096class RHRoomBookingGetRoomSelectList( RHRoomBookingBase ):
2097
2098    def _checkParams( self, params ):
2099        self.location = params.get( 'locationName' )
2100        self.forSubEvents = params.get( 'forSubEvents' ) == 'True'
2101
2102    def _process( self ):
2103
2104        self._roomList = []
2105        if self.location:
2106            self._roomList = CrossLocationQueries.getRooms( location = self.location )
2107        self._locationRoom = ""
2108
2109        from MaKaC.webinterface import wcomponents
2110        if self.forSubEvents:
2111            p = wcomponents.WRoomBookingRoomSelectList4SubEvents( self )
2112        else:
2113            p = wcomponents.WRoomBookingRoomSelectList( self )
2114
2115        return p.getHTML( self.getRequestParams() )
2116
2117        #return "<div style='background-color: red;'>&nbsp;&nbsp;&nbsp;&nbsp;</div>"
2118
2119class RHRoomBookingRejectBookingOccurrence( RHRoomBookingBase ):
2120
2121    def _checkParams( self , params ):
2122        resvID = int( params.get( "resvID" ) )
2123        roomLocation = params.get( "roomLocation" )
2124        reason = params.get( "reason" )
2125        date = params.get( "date" )
2126
2127        self._resv = CrossLocationQueries.getReservations( resvID = resvID, location = roomLocation )
2128        self._rejectionReason = reason
2129        self._date = parse_date( date )
2130        self._target = self._resv
2131
2132    def _checkProtection( self ):
2133        RHRoomBookingBase._checkProtection(self)
2134        user = self._getUser()
2135        # Only responsible and admin can REJECT
2136        # (Owner can not reject his own booking, he should cancel instead)
2137        if ( not self._resv.room.isOwnedBy( user ) ) and \
2138            ( not self._getUser().isAdmin() ):
2139                raise MaKaCError( "You are not authorized to take this action." )
2140
2141    def _process( self ):
2142        self._emailsToBeSent = []
2143        self._resv.excludeDay( self._date, unindex=True )
2144        self._resv.update()
2145        self._emailsToBeSent += self._resv.notifyAboutRejection( date = self._date, reason = self._rejectionReason )
2146
2147        # Add entry to the booking history
2148        info = []
2149        info.append("Booking occurence of the %s rejected" %self._date.strftime("%d %b %Y"))
2150        info.append("Reason : '%s'" %self._rejectionReason)
2151        histEntry = ResvHistoryEntry(self._getUser(), info, self._emailsToBeSent)
2152        self._resv.getResvHistory().addHistoryEntry(histEntry)
2153
2154        self._websession.setVar( 'actionSucceeded', True )
2155        self._websession.setVar( 'title', "Selected occurrence of this booking has been rejected." )
2156        self._websession.setVar( 'description', "NOTE: rejection e-mail has been sent to the user. " )
2157        url = urlHandlers.UHRoomBookingBookingDetails.getURL( self._resv )
2158        self._redirect( url ) # Redirect to booking details
Note: See TracBrowser for help on using the repository browser.