source: indico/indico/MaKaC/webinterface/rh/roomBooking.py @ f777b0

hello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.99v1.0v1.1
Last change on this file since f777b0 was f777b0, checked in by Jose Benito <jose.benito.gonzalez@…>, 14 months ago

[FTR] Email notification of assistance with room setup

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