source: indico/indico/MaKaC/webinterface/rh/roomBooking.py @ 4e58aa

hello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.98b2v0.99v1.0v1.1
Last change on this file since 4e58aa was 4e58aa, checked in by Alberto Resco Perez <alberto.resco.perez@…>, 21 months ago

[FIX] Blocking rooms should be allowed by admins

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