Ejemplo n.º 1
0
    def getVars(self):
        wvars = WTemplated.getVars(self)
        wvars['standalone'] = self._standalone
        room = wvars['room']

        wvars['attrs'] = {attr.attribute.name: attr for attr in room.attributes
                          if not attr.attribute.is_hidden or rb_is_admin(session.user)}

        wvars['owner_name'] = room.owner.full_name

        wvars['bookable_hours'] = room.bookable_hours.all()
        wvars['nonbookable_periods'] = room.nonbookable_periods.all()

        # URLs
        wvars['stats_url'] = UH.UHRoomBookingRoomStats.getURL(room)
        wvars['delete_room_url'] = url_for('rooms_admin.delete_room', room)
        wvars['modify_room_url'] = url_for('rooms_admin.modify_room', room)
        if not self._standalone:
            wvars['conference'] = self._rh._conf

        room_mapper = RoomMapperHolder().match({'placeName': self._rh._location.name}, exact=True)
        if room_mapper:
            wvars['show_on_map'] = room_mapper[0].getMapURL(self._rh._room.name)
        else:
            wvars['show_on_map'] = UH.UHRoomBookingMapOfRooms.getURL(roomID=self._rh._room.id)

        return wvars
Ejemplo n.º 2
0
 def can_be_modified(self, user):
     """
     The following persons are authorized to modify a blocking:
     - owner (the one who created the blocking)
     - admin (of course)
     """
     return user and (user == self.created_by_user or rb_is_admin(user))
Ejemplo n.º 3
0
    def getVars(self):
        wvars = WTemplated.getVars(self)
        wvars["standalone"] = self._standalone
        room = wvars["room"]

        wvars["attrs"] = {
            attr.attribute.name: attr
            for attr in room.attributes
            if not attr.attribute.is_hidden or rb_is_admin(session.user)
        }

        wvars["owner_name"] = room.owner.full_name

        wvars["bookable_hours"] = room.bookable_hours.all()
        wvars["nonbookable_periods"] = room.nonbookable_periods.all()

        # URLs
        wvars["stats_url"] = UH.UHRoomBookingRoomStats.getURL(room)
        wvars["delete_room_url"] = url_for("rooms_admin.delete_room", room)
        wvars["modify_room_url"] = url_for("rooms_admin.modify_room", room)
        if not self._standalone:
            wvars["conference"] = self._rh._conf

        wvars["show_on_map"] = room.map_url if room.map_url else url_for("rooms.roomBooking-mapOfRooms", room)

        return wvars
Ejemplo n.º 4
0
 def _get_permissions(self, blocking):
     methods = ('can_delete', 'can_edit')
     admin_permissions = None
     user_permissions = {x: getattr(blocking, x)(session.user, allow_admin=False) for x in methods}
     if rb_is_admin(session.user):
         admin_permissions = {x: getattr(blocking, x)(session.user) for x in methods}
     return {'user': user_permissions, 'admin': admin_permissions}
Ejemplo n.º 5
0
 def _get_permissions(self, occurrence):
     methods = ('can_cancel', 'can_reject')
     admin_permissions = None
     user_permissions = {x: getattr(occurrence, x)(session.user, allow_admin=False) for x in methods}
     if rb_is_admin(session.user):
         admin_permissions = {x: getattr(occurrence, x)(session.user) for x in methods}
     return {'user': user_permissions, 'admin': admin_permissions}
Ejemplo n.º 6
0
def _sidemenu_items(sender, **kwargs):
    user_is_admin = session.user is not None and rb_is_admin(session.user)
    user_has_rooms = session.user is not None and Room.user_owns_rooms(session.user)
    map_available = Location.default_location is not None and Location.default_location.is_map_available

    yield SideMenuItem('book_room', _('Book a Room'), url_for('rooms.book'), 80, icon='checkmark')
    if map_available:
        yield SideMenuItem('map', _('Map of Rooms'), url_for('rooms.roomBooking-mapOfRooms'), 70, icon='location')
    yield SideMenuItem('calendar', _('Calendar'), url_for('rooms.calendar'), 60, icon='calendar')
    yield SideMenuItem('my_bookings', _('My Bookings'), url_for('rooms.my_bookings'), 50, icon='time')
    yield SideMenuItem('search_bookings', _('Search bookings'), url_for('rooms.roomBooking-search4Bookings'),
                       section='search')
    yield SideMenuItem('search_rooms', _('Search rooms'), url_for('rooms.search_rooms'),
                       section='search')
    if user_has_rooms:
        yield SideMenuItem('bookings_in_my_rooms', _('Bookings in my rooms'), url_for('rooms.bookings_my_rooms'),
                           section='my_rooms')
        yield SideMenuItem('prebookings_in_my_rooms', _('Pre-bookings in my rooms'),
                           url_for('rooms.pending_bookings_my_rooms'),
                           section='my_rooms')
        yield SideMenuItem('room_list', _('Room list'), url_for('rooms.search_my_rooms'),
                           section='my_rooms')
    yield SideMenuItem('my_blockings', _('My Blockings'),
                       url_for('rooms.blocking_list', only_mine=True, timeframe='recent'),
                       section='blocking')
    if user_has_rooms:
        yield SideMenuItem('blockings_my_rooms', _('Blockings for my rooms'), url_for('rooms.blocking_my_rooms'),
                           section='blocking')
    yield SideMenuItem('blocking_create', _('Block rooms'), url_for('rooms.create_blocking'), section='blocking')
    if user_is_admin:
        yield SideMenuItem('admin', _('Administration'), url_for('rooms_admin.roomBooking-admin'), 10,
                           icon='user-chairperson')
Ejemplo n.º 7
0
    def _process_select_room(self):
        # Step 1: Room(s), dates, repetition selection
        form = self._make_select_room_form()
        if form.validate_on_submit():
            flexible_days = form.flexible_dates_range.data
            day_start_dt = datetime.combine(form.start_dt.data.date(), time())
            day_end_dt = datetime.combine(form.end_dt.data.date(), time(23, 59))

            selected_rooms = [r for r in self._rooms if r.id in form.room_ids.data]
            occurrences, candidates = self._get_all_occurrences(form.room_ids.data, form, flexible_days)

            period_form_defaults = FormDefaults(repeat_interval=form.repeat_interval.data,
                                                repeat_frequency=form.repeat_frequency.data)
            period_form = self._make_select_period_form(period_form_defaults)

            # Show step 2 page
            return self._get_view('select_period', rooms=selected_rooms, occurrences=occurrences, candidates=candidates,
                                  start_dt=day_start_dt, end_dt=day_end_dt, period_form=period_form, form=form,
                                  repeat_frequency=form.repeat_frequency.data,
                                  repeat_interval=form.repeat_interval.data, flexible_days=flexible_days).display()

        # GET or form errors => show step 1 page
        return self._get_view('select_room', errors=form.error_list, rooms=self._rooms, form=form,
                              my_rooms=[r.id for r in Room.get_owned_by(session.user)],
                              max_room_capacity=Room.max_capacity, can_override=rb_is_admin(session.user),
                              date_changed=not form.is_submitted() and self.date_changed, ).display()
Ejemplo n.º 8
0
    def _process_select_room(self):
        # Step 1: Room(s), dates, repetition selection
        form = self._make_select_room_form()
        if form.validate_on_submit():
            flexible_days = form.flexible_dates_range.data
            day_start_dt = datetime.combine(form.start_dt.data.date(), time())
            day_end_dt = datetime.combine(form.end_dt.data.date(), time(23, 59))
            selected_rooms = [r for r in self._rooms if r.id in form.room_ids.data]
            selected_period_days = (day_end_dt - day_start_dt).days
            for room in selected_rooms:
                booking_limit_days = room.booking_limit_days or rb_settings.get('booking_limit')
                if selected_period_days > booking_limit_days:
                    flash(_(u'Bookings for the room "{}" may not be longer than {} days')
                          .format(room.name, booking_limit_days), 'error')
                    return redirect(url_for('rooms.book'))
            occurrences, candidates = self._get_all_occurrences(form.room_ids.data, form, flexible_days)
            period_form_defaults = FormDefaults(repeat_interval=form.repeat_interval.data,
                                                repeat_frequency=form.repeat_frequency.data)
            period_form = self._make_select_period_form(period_form_defaults)

            # Show step 2 page
            return self._get_view('select_period', rooms=selected_rooms, occurrences=occurrences, candidates=candidates,
                                  start_dt=day_start_dt, end_dt=day_end_dt, period_form=period_form, form=form,
                                  repeat_frequency=form.repeat_frequency.data,
                                  repeat_interval=form.repeat_interval.data, flexible_days=flexible_days).display()

        # GET or form errors => show step 1 page
        return self._get_view('select_room', errors=form.error_list, rooms=self._rooms, form=form,
                              my_rooms=[r.id for r in Room.get_owned_by(session.user)],
                              max_room_capacity=Room.max_capacity, can_override=rb_is_admin(session.user),
                              date_changed=not form.is_submitted() and self.date_changed, ).display()
Ejemplo n.º 9
0
 def can_be_modified(self, user):
     if user is None:
         return False
     if self.is_rejected or self.is_cancelled:
         return False
     if rb_is_admin(user):
         return True
     return self.created_by_user == user or self.is_booked_for(user) or self.room.is_owned_by(user)
Ejemplo n.º 10
0
 def check_bookable_hours(self, start_time, end_time, user=None, quiet=False):
     if user and (rb_is_admin(user) or self.is_owned_by(user)):
         return True
     bookable_hours = self.bookable_hours.all()
     if not bookable_hours:
         return True
     for bt in bookable_hours:
         if bt.fits_period(start_time, end_time):
             return True
     if quiet:
         return False
     raise NoReportError(u'Room cannot be booked at this time')
Ejemplo n.º 11
0
 def can_cancel(self, user, allow_admin=True):
     if user is None:
         return False
     if not self.is_valid or self.end_dt < datetime.now():
         return False
     booking = self.reservation
     booked_or_owned_by_user = booking.is_owned_by(user) or booking.is_booked_for(user)
     if booking.is_rejected or booking.is_cancelled or booking.is_archived:
         return False
     if booked_or_owned_by_user and self.is_within_cancel_grace_period:
         return True
     return allow_admin and rb_is_admin(user)
Ejemplo n.º 12
0
 def check_advance_days(self, end_date, user=None, quiet=False):
     if not self.max_advance_days:
         return True
     if user and (rb_is_admin(user) or self.is_owned_by(user)):
         return True
     advance_days = (end_date - date.today()).days
     ok = advance_days < self.max_advance_days
     if quiet or ok:
         return ok
     else:
         msg = _(u'You cannot book this room more than {} days in advance')
         raise NoReportError(msg.format(self.max_advance_days))
Ejemplo n.º 13
0
    def _can_be_booked(self, user, prebook=False, ignore_admin=False):
        if not user or not rb_check_user_access(user):
            return False

        if (not ignore_admin and rb_is_admin(user)) or (self.is_owned_by(user) and self.is_active):
            return True

        if self.is_active and self.is_reservable and (prebook or not self.reservations_need_confirmation):
            group_name = self.get_attribute_value('allowed-booking-group')
            if not group_name or user in GroupProxy.get_named_default_group(group_name):
                return True

        return False
Ejemplo n.º 14
0
    def create_occurrences(self, skip_conflicts, user=None):
        ReservationOccurrence.create_series_for_reservation(self)
        db.session.flush()

        if user is None:
            user = self.created_by_user

        # Check for conflicts with nonbookable periods
        if not rb_is_admin(user) and not self.room.is_owned_by(user):
            nonbookable_periods = self.room.nonbookable_periods.filter(NonBookablePeriod.end_dt > self.start_dt)
            for occurrence in self.occurrences:
                if not occurrence.is_valid:
                    continue
                for nbd in nonbookable_periods:
                    if nbd.overlaps(occurrence.start_dt, occurrence.end_dt):
                        if not skip_conflicts:
                            raise ConflictingOccurrences()
                        occurrence.cancel(user, u'Skipped due to nonbookable date', silent=True, propagate=False)
                        break

        # Check for conflicts with blockings
        blocked_rooms = self.room.get_blocked_rooms(*(occurrence.start_dt for occurrence in self.occurrences))
        for br in blocked_rooms:
            blocking = br.blocking
            if blocking.can_be_overridden(user, self.room):
                continue
            for occurrence in self.occurrences:
                if occurrence.is_valid and blocking.is_active_at(occurrence.start_dt.date()):
                    # Cancel OUR occurrence
                    msg = u'Skipped due to collision with a blocking ({})'
                    occurrence.cancel(user, msg.format(blocking.reason), silent=True, propagate=False)

        # Check for conflicts with other occurrences
        conflicting_occurrences = self.get_conflicting_occurrences()
        for occurrence, conflicts in conflicting_occurrences.iteritems():
            if not occurrence.is_valid:
                continue
            if conflicts['confirmed']:
                if not skip_conflicts:
                    raise ConflictingOccurrences()
                # Cancel OUR occurrence
                msg = u'Skipped due to collision with {} reservation(s)'
                occurrence.cancel(user, msg.format(len(conflicts['confirmed'])), silent=True, propagate=False)
            elif conflicts['pending'] and self.is_accepted:
                # Reject OTHER occurrences
                for conflict in conflicts['pending']:
                    conflict.reject(user, u'Rejected due to collision with a confirmed reservation')
Ejemplo n.º 15
0
def get_rooms_conflicts(rooms, start_dt, end_dt, repeat_frequency, repeat_interval, blocked_rooms,
                        nonbookable_periods, unbookable_hours, skip_conflicts_with=None, allow_admin=False):
    rooms_conflicts = defaultdict(set)
    rooms_pre_conflicts = defaultdict(set)
    rooms_conflicting_candidates = defaultdict(set)
    skip_conflicts_with = skip_conflicts_with or []

    candidates = ReservationOccurrence.create_series(start_dt, end_dt, (repeat_frequency, repeat_interval))
    room_ids = [room.id for room in rooms]
    query = (ReservationOccurrence.query
             .filter(Reservation.room_id.in_(room_ids),
                     ReservationOccurrence.is_valid,
                     ReservationOccurrence.filter_overlap(candidates))
             .join(ReservationOccurrence.reservation)
             .options(ReservationOccurrence.NO_RESERVATION_USER_STRATEGY,
                      contains_eager(ReservationOccurrence.reservation)))

    if skip_conflicts_with:
        query = query.filter(~Reservation.id.in_(skip_conflicts_with))

    overlapping_occurrences = group_list(query, key=lambda obj: obj.reservation.room.id)
    for room_id, occurrences in overlapping_occurrences.iteritems():
        conflicts = get_room_bookings_conflicts(candidates, occurrences, room_id, skip_conflicts_with)
        rooms_conflicts[room_id], rooms_pre_conflicts[room_id], rooms_conflicting_candidates[room_id] = conflicts
    for room_id, occurrences in blocked_rooms.iteritems():
        conflicts, conflicting_candidates = get_room_blockings_conflicts(room_id, candidates, occurrences)
        rooms_conflicts[room_id] |= conflicts
        rooms_conflicting_candidates[room_id] |= conflicting_candidates

    if not (allow_admin and rb_is_admin(session.user)):
        for room_id, occurrences in nonbookable_periods.iteritems():
            room = Room.get_one(room_id)
            if not room.can_override(session.user, allow_admin=allow_admin):
                conflicts, conflicting_candidates = get_room_nonbookable_periods_conflicts(candidates, occurrences)
                rooms_conflicts[room_id] |= conflicts
                rooms_conflicting_candidates[room_id] |= conflicting_candidates

        for room_id, occurrences in unbookable_hours.iteritems():
            room = Room.get_one(room_id)
            if not room.can_override(session.user, allow_admin=allow_admin):
                conflicts, conflicting_candidates = get_room_unbookable_hours_conflicts(candidates, occurrences)
                rooms_conflicts[room_id] |= conflicts
                rooms_conflicting_candidates[room_id] |= conflicting_candidates
    rooms_conflicting_candidates = defaultdict(list, ((k, list(v)) for k, v in rooms_conflicting_candidates.items()))
    return rooms_conflicts, rooms_pre_conflicts, rooms_conflicting_candidates
Ejemplo n.º 16
0
    def can_override(self, user, room=None, explicit_only=False, allow_admin=True):
        """Check if a user can override the blocking

        The following persons are authorized to override a blocking:
        - the creator of the blocking
        - anyone on the blocking's ACL
        - unless explicit_only is set: rb admins and room managers (if a room is given)
        """
        if not user:
            return False
        if self.created_by_user == user:
            return True
        if not explicit_only:
            if allow_admin and rb_is_admin(user):
                return True
            if room and room.can_manage(user):
                return True
        return any(user in principal for principal in iter_acl(self.allowed))
Ejemplo n.º 17
0
    def can_be_overridden(self, user, room=None, explicit_only=False):
        """Determines if a user can override the blocking

        The following persons are authorized to override a blocking:
        - owner (the one who created the blocking)
        - any users on the blocking's ACL
        - unless explicitOnly is set: admins and room owners (if a room is given)
        """
        if not user:
            return False
        if self.created_by_user == user:
            return True
        if not explicit_only:
            if rb_is_admin(user):
                return True
            elif room and room.is_owned_by(user):
                return True
        return any(user in principal for principal in iter_acl(self.allowed))
Ejemplo n.º 18
0
    def getVars(self):
        wvars = WTemplated.getVars(self)
        wvars['standalone'] = self._standalone
        room = wvars['room']

        wvars['attrs'] = {attr.attribute.name: attr for attr in room.attributes
                          if not attr.attribute.is_hidden or rb_is_admin(session.user)}

        wvars['owner_name'] = room.owner.full_name

        wvars['bookable_hours'] = room.bookable_hours.all()
        wvars['nonbookable_periods'] = room.nonbookable_periods.all()

        # URLs
        wvars['stats_url'] = url_for('rooms.roomBooking-roomStats', room)
        wvars['delete_room_url'] = url_for('rooms_admin.delete_room', room)
        wvars['modify_room_url'] = url_for('rooms_admin.modify_room', room)

        wvars['show_on_map'] = room.map_url if room.map_url else url_for('rooms.roomBooking-mapOfRooms', room)
        return wvars
Ejemplo n.º 19
0
def _sidemenu_items(sender, **kwargs):
    user_is_admin = session.user is not None and rb_is_admin(session.user)
    user_has_rooms = session.user is not None and Room.user_owns_rooms(session.user)
    map_available = Location.default_location is not None and Location.default_location.is_map_available

    yield SideMenuItem("book_room", _("Book a Room"), url_for("rooms.book"), 80, icon="checkmark")
    if map_available:
        yield SideMenuItem("map", _("Map of Rooms"), url_for("rooms.roomBooking-mapOfRooms"), 70, icon="location")
    yield SideMenuItem("calendar", _("Calendar"), url_for("rooms.calendar"), 60, icon="calendar")
    yield SideMenuItem("my_bookings", _("My Bookings"), url_for("rooms.my_bookings"), 50, icon="time")
    yield SideMenuItem(
        "search_bookings", _("Search bookings"), url_for("rooms.roomBooking-search4Bookings"), section="search"
    )
    yield SideMenuItem("search_rooms", _("Search rooms"), url_for("rooms.search_rooms"), section="search")
    if user_has_rooms:
        yield SideMenuItem(
            "bookings_in_my_rooms", _("Bookings in my rooms"), url_for("rooms.bookings_my_rooms"), section="my_rooms"
        )
        yield SideMenuItem(
            "prebookings_in_my_rooms",
            _("Pre-bookings in my rooms"),
            url_for("rooms.pending_bookings_my_rooms"),
            section="my_rooms",
        )
        yield SideMenuItem("room_list", _("Room list"), url_for("rooms.search_my_rooms"), section="my_rooms")
    yield SideMenuItem(
        "my_blockings",
        _("My Blockings"),
        url_for("rooms.blocking_list", only_mine=True, timeframe="recent"),
        section="blocking",
    )
    if user_has_rooms:
        yield SideMenuItem(
            "blockings_my_rooms", _("Blockings for my rooms"), url_for("rooms.blocking_my_rooms"), section="blocking"
        )
    yield SideMenuItem("blocking_create", _("Block rooms"), url_for("rooms.create_blocking"), section="blocking")
    if user_is_admin:
        yield SideMenuItem(
            "admin", _("Administration"), url_for("rooms_admin.roomBooking-admin"), 10, icon="user-chairperson"
        )
Ejemplo n.º 20
0
    def create_occurrences(self, skip_conflicts, user=None):
        ReservationOccurrence.create_series_for_reservation(self)
        db.session.flush()

        if user is None:
            user = self.created_by_user

        # Check for conflicts with nonbookable periods
        if not rb_is_admin(user) and not self.room.is_owned_by(user):
            nonbookable_periods = self.room.nonbookable_periods.filter(
                NonBookablePeriod.end_dt > self.start_dt)
            for occurrence in self.occurrences:
                if not occurrence.is_valid:
                    continue
                for nbd in nonbookable_periods:
                    if nbd.overlaps(occurrence.start_dt, occurrence.end_dt):
                        if not skip_conflicts:
                            raise ConflictingOccurrences()
                        occurrence.cancel(user,
                                          u'Skipped due to nonbookable date',
                                          silent=True,
                                          propagate=False)
                        break

        # Check for conflicts with blockings
        blocked_rooms = self.room.get_blocked_rooms(
            *(occurrence.start_dt for occurrence in self.occurrences))
        for br in blocked_rooms:
            blocking = br.blocking
            if blocking.can_be_overridden(user, self.room):
                continue
            for occurrence in self.occurrences:
                if occurrence.is_valid and blocking.is_active_at(
                        occurrence.start_dt.date()):
                    # Cancel OUR occurrence
                    msg = u'Skipped due to collision with a blocking ({})'
                    occurrence.cancel(user,
                                      msg.format(blocking.reason),
                                      silent=True,
                                      propagate=False)

        # Check for conflicts with other occurrences
        conflicting_occurrences = self.get_conflicting_occurrences()
        for occurrence, conflicts in conflicting_occurrences.iteritems():
            if not occurrence.is_valid:
                continue
            if conflicts['confirmed']:
                if not skip_conflicts:
                    raise ConflictingOccurrences()
                # Cancel OUR occurrence
                msg = u'Skipped due to collision with {} reservation(s)'
                occurrence.cancel(user,
                                  msg.format(len(conflicts['confirmed'])),
                                  silent=True,
                                  propagate=False)
            elif conflicts['pending'] and self.is_accepted:
                # Reject OTHER occurrences
                for conflict in conflicts['pending']:
                    conflict.reject(
                        user,
                        u'Rejected due to collision with a confirmed reservation'
                    )

        # Mark occurrences created within the notification window as notified
        for occurrence in self.occurrences:
            if occurrence.is_valid and occurrence.is_in_notification_window():
                occurrence.notification_sent = True

        # Mark occurrences created within the digest window as notified
        if self.repeat_frequency == RepeatFrequency.WEEK:
            if self.room.is_in_digest_window():
                digest_start = round_up_month(date.today())
            else:
                digest_start = date.today()
            digest_end = get_month_end(digest_start)
            self.occurrences.filter(
                ReservationOccurrence.start_dt <= digest_end).update(
                    {'notification_sent': True}, synchronize_session='fetch')
Ejemplo n.º 21
0
 def can_delete(self, user):
     if not user:
         return False
     return rb_is_admin(user)
Ejemplo n.º 22
0
 def can_be_modified(self, user):
     """Only admin can modify rooms."""
     if not user:
         return False
     return rb_is_admin(user)
Ejemplo n.º 23
0
 def _checkProtection(self):
     user = session.user
     if not user or (not rb_is_admin(user)
                     and not self.blocked_room.room.is_owned_by(user)):
         raise ServiceError(
             _('You are not permitted to modify this blocking'))
Ejemplo n.º 24
0
 def _checkProtection(self):
     if session.user is None:
         self._checkSessionUser()
     elif not rb_is_admin(session.user):
         raise AccessError("You are not authorized to take this action.")
Ejemplo n.º 25
0
 def _check_access(self):
     user = session.user
     if not user or (not rb_is_admin(user) and not self.blocked_room.room.is_owned_by(user)):
         raise Forbidden(_('You are not permitted to modify this blocking'))
Ejemplo n.º 26
0
 def _check_access(self):
     RHRoomBookingBase._check_access(self)
     if not rb_is_admin(session.user):
         raise Forbidden
Ejemplo n.º 27
0
def _sidemenu_items(sender, **kwargs):
    user_is_admin = session.user is not None and rb_is_admin(session.user)
    user_has_rooms = session.user is not None and Room.user_owns_rooms(
        session.user)
    map_available = Location.default_location is not None and Location.default_location.is_map_available

    yield SideMenuItem('book_room',
                       _('Book a Room'),
                       url_for('rooms.book'),
                       80,
                       icon='checkmark')
    if map_available:
        yield SideMenuItem('map',
                           _('Map of Rooms'),
                           url_for('rooms.roomBooking-mapOfRooms'),
                           70,
                           icon='location')
    yield SideMenuItem('calendar',
                       _('Calendar'),
                       url_for('rooms.calendar'),
                       60,
                       icon='calendar')
    yield SideMenuItem('my_bookings',
                       _('My Bookings'),
                       url_for('rooms.my_bookings'),
                       50,
                       icon='time')
    yield SideMenuItem('search_bookings',
                       _('Search bookings'),
                       url_for('rooms.roomBooking-search4Bookings'),
                       section='search')
    yield SideMenuItem('search_rooms',
                       _('Search rooms'),
                       url_for('rooms.search_rooms'),
                       section='search')
    if user_has_rooms:
        yield SideMenuItem('bookings_in_my_rooms',
                           _('Bookings in my rooms'),
                           url_for('rooms.bookings_my_rooms'),
                           section='my_rooms')
        yield SideMenuItem('prebookings_in_my_rooms',
                           _('Pre-bookings in my rooms'),
                           url_for('rooms.pending_bookings_my_rooms'),
                           section='my_rooms')
        yield SideMenuItem('room_list',
                           _('Room list'),
                           url_for('rooms.search_my_rooms'),
                           section='my_rooms')
    yield SideMenuItem('my_blockings',
                       _('My Blockings'),
                       url_for('rooms.blocking_list',
                               only_mine=True,
                               timeframe='recent'),
                       section='blocking')
    if user_has_rooms:
        yield SideMenuItem('blockings_my_rooms',
                           _('Blockings for my rooms'),
                           url_for('rooms.blocking_my_rooms'),
                           section='blocking')
    yield SideMenuItem('blocking_create',
                       _('Block rooms'),
                       url_for('rooms.create_blocking'),
                       section='blocking')
    if user_is_admin:
        yield SideMenuItem('admin',
                           _('Administration'),
                           url_for('rooms_admin.roomBooking-admin'),
                           10,
                           icon='user-chairperson')
Ejemplo n.º 28
0
 def can_be_cancelled(self, user):
     if user is None:
         return False
     return self.is_owned_by(user) or rb_is_admin(user) or self.is_booked_for(user)
Ejemplo n.º 29
0
 def _process_DELETE(self):
     if not rb_is_admin(session.user):
         raise Forbidden
     logger.info('Room %r deleted by %r', self.room, session.user)
     self.room.is_deleted = True
     return '', 204
Ejemplo n.º 30
0
 def can_be_overridden(self, user):
     if not user:
         return False
     return rb_is_admin(user) or self.is_owned_by(user)
Ejemplo n.º 31
0
 def _check_access(self):
     RHRoomBookingBase._check_access(self)
     if not rb_is_admin(session.user) and not self._skip_admin_check():
         raise Forbidden
Ejemplo n.º 32
0
 def is_user_admin(user):
     return rb_is_admin(user)
Ejemplo n.º 33
0
 def _check_access(self):
     RHRoomBookingBase._check_access(self)
     if not rb_is_admin(session.user):
         raise Forbidden
Ejemplo n.º 34
0
 def can_be_rejected(self, user):
     if user is None:
         return False
     return rb_is_admin(user) or self.room.is_owned_by(user)
Ejemplo n.º 35
0
    def create_occurrences(self, skip_conflicts, user=None):
        ReservationOccurrence.create_series_for_reservation(self)
        db.session.flush()

        if user is None:
            user = self.created_by_user

        # Check for conflicts with nonbookable periods
        if not rb_is_admin(user) and not self.room.is_owned_by(user):
            nonbookable_periods = self.room.nonbookable_periods.filter(NonBookablePeriod.end_dt > self.start_dt)
            for occurrence in self.occurrences:
                if not occurrence.is_valid:
                    continue
                for nbd in nonbookable_periods:
                    if nbd.overlaps(occurrence.start_dt, occurrence.end_dt):
                        if not skip_conflicts:
                            raise ConflictingOccurrences()
                        occurrence.cancel(user, u"Skipped due to nonbookable date", silent=True, propagate=False)
                        break

        # Check for conflicts with blockings
        blocked_rooms = self.room.get_blocked_rooms(*(occurrence.start_dt for occurrence in self.occurrences))
        for br in blocked_rooms:
            blocking = br.blocking
            if blocking.can_be_overridden(user, self.room):
                continue
            for occurrence in self.occurrences:
                if occurrence.is_valid and blocking.is_active_at(occurrence.start_dt.date()):
                    # Cancel OUR occurrence
                    msg = u"Skipped due to collision with a blocking ({})"
                    occurrence.cancel(user, msg.format(blocking.reason), silent=True, propagate=False)

        # Check for conflicts with other occurrences
        conflicting_occurrences = self.get_conflicting_occurrences()
        for occurrence, conflicts in conflicting_occurrences.iteritems():
            if not occurrence.is_valid:
                continue
            if conflicts["confirmed"]:
                if not skip_conflicts:
                    raise ConflictingOccurrences()
                # Cancel OUR occurrence
                msg = u"Skipped due to collision with {} reservation(s)"
                occurrence.cancel(user, msg.format(len(conflicts["confirmed"])), silent=True, propagate=False)
            elif conflicts["pending"] and self.is_accepted:
                # Reject OTHER occurrences
                for conflict in conflicts["pending"]:
                    conflict.reject(user, u"Rejected due to collision with a confirmed reservation")

        # Mark occurrences created within the notification window as notified
        for occurrence in self.occurrences:
            if occurrence.is_valid and occurrence.is_in_notification_window():
                occurrence.notification_sent = True

        # Mark occurrences created within the digest window as notified
        if self.repeat_frequency == RepeatFrequency.WEEK:
            if self.room.is_in_digest_window():
                digest_start = round_up_month(date.today())
            else:
                digest_start = date.today()
            digest_end = get_month_end(digest_start)
            self.occurrences.filter(ReservationOccurrence.start_dt <= digest_end).update({"notification_sent": True})
Ejemplo n.º 36
0
def search_for_rooms(filters, allow_admin=False, availability=None):
    """Search for a room, using the provided filters.

    :param filters: The filters, provided as a dictionary
    :param allow_admin: A boolean specifying whether admins have override privileges
    :param availability: A boolean specifying whether (un)available rooms should be provided,
                         or `None` in case all rooms should be returned.
    """
    query = (Room.query
             .outerjoin(favorite_room_table, db.and_(favorite_room_table.c.user_id == session.user.id,
                                                     favorite_room_table.c.room_id == Room.id))
             .reset_joinpoint()  # otherwise filter_by() would apply to the favorite table
             .options(joinedload('owner').load_only('id'))
             .filter(~Room.is_deleted)
             .order_by(favorite_room_table.c.user_id.is_(None), db.func.indico.natsort(Room.full_name)))

    criteria = {}
    if 'capacity' in filters:
        query = query.filter(Room.capacity >= filters['capacity'])
    if 'building' in filters:
        criteria['building'] = filters['building']
    if 'division' in filters:
        criteria['division'] = filters['division']
    query = query.filter_by(**criteria)
    if 'text' in filters:
        text = ' '.join(filters['text'].strip().split())
        query = query.filter(_make_room_text_filter(text))
    if filters.get('equipment'):
        subquery = (db.session.query(RoomEquipmentAssociation)
                    .with_entities(db.func.count(RoomEquipmentAssociation.c.room_id))
                    .filter(
                        RoomEquipmentAssociation.c.room_id == Room.id,
                        EquipmentType.name.in_(filters['equipment'])
                    )
                    .join(EquipmentType, RoomEquipmentAssociation.c.equipment_id == EquipmentType.id)
                    .correlate(Room)
                    .as_scalar())
        query = query.filter(subquery == len(filters['equipment']))
    if filters.get('features'):
        for feature in filters['features']:
            query = query.filter(Room.available_equipment.any(EquipmentType.features.any(RoomFeature.name == feature)))
    if filters.get('favorite'):
        query = query.filter(favorite_room_table.c.user_id.isnot(None))
    if filters.get('mine'):
        ids = get_managed_room_ids(session.user)
        query = query.filter(Room.id.in_(ids))
    query = _filter_coordinates(query, filters)

    if availability is None:
        return query

    start_dt, end_dt = filters['start_dt'], filters['end_dt']
    repeatability = (filters['repeat_frequency'], filters['repeat_interval'])
    availability_filters = [Room.filter_available(start_dt, end_dt, repeatability, include_blockings=False,
                                                  include_pre_bookings=True)]
    if not (allow_admin and rb_is_admin(session.user)):
        selected_period_days = (filters['end_dt'] - filters['start_dt']).days
        booking_limit_days = db.func.coalesce(Room.booking_limit_days, rb_settings.get('booking_limit'))

        criterion = db.and_(Room.filter_bookable_hours(start_dt.time(), end_dt.time()),
                            Room.filter_nonbookable_periods(start_dt, end_dt),
                            db.or_(booking_limit_days.is_(None),
                            selected_period_days <= booking_limit_days))
        unbookable_ids = [room.id
                          for room in query.filter(db.and_(*availability_filters), ~criterion)
                          if not room.can_override(session.user, allow_admin=False)]
        availability_filters.append(~Room.id.in_(unbookable_ids))
    availability_criterion = db.and_(*availability_filters)
    if availability is False:
        availability_criterion = ~availability_criterion
    return query.filter(availability_criterion)
Ejemplo n.º 37
0
def test_rb_is_admin(create_user, is_admin, is_rb_admin, expected):
    user = create_user(1, admin=is_admin, rb_admin=is_rb_admin)
    assert rb_is_admin(user) == expected
Ejemplo n.º 38
0
 def can_be_deleted(self, user):
     if user is None:
         return False
     return rb_is_admin(user)
Ejemplo n.º 39
0
    def create_occurrences(self, skip_conflicts, user=None, allow_admin=True):
        ReservationOccurrence.create_series_for_reservation(self)
        db.session.flush()

        if user is None:
            user = self.created_by_user

        # Check for conflicts with nonbookable periods
        admin = allow_admin and rb_is_admin(user)
        if not admin and not self.room.can_manage(user, permission='override'):
            nonbookable_periods = self.room.nonbookable_periods.filter(
                NonBookablePeriod.end_dt > self.start_dt)
            for occurrence in self.occurrences:
                if not occurrence.is_valid:
                    continue
                for nbd in nonbookable_periods:
                    if nbd.overlaps(occurrence.start_dt, occurrence.end_dt):
                        if not skip_conflicts:
                            raise ConflictingOccurrences()
                        occurrence.cancel(user,
                                          'Skipped due to nonbookable date',
                                          silent=True,
                                          propagate=False)
                        break

        # Check for conflicts with blockings
        blocked_rooms = self.room.get_blocked_rooms(
            *(occurrence.start_dt for occurrence in self.occurrences))
        for br in blocked_rooms:
            blocking = br.blocking
            if blocking.can_override(user,
                                     room=self.room,
                                     allow_admin=allow_admin):
                continue
            for occurrence in self.occurrences:
                if occurrence.is_valid and blocking.is_active_at(
                        occurrence.start_dt.date()):
                    # Cancel OUR occurrence
                    msg = 'Skipped due to collision with a blocking ({})'
                    occurrence.cancel(user,
                                      msg.format(blocking.reason),
                                      silent=True,
                                      propagate=False)

        # Check for conflicts with other occurrences
        conflicting_occurrences = self.get_conflicting_occurrences()
        for occurrence, conflicts in conflicting_occurrences.items():
            if not occurrence.is_valid:
                continue
            if conflicts['confirmed']:
                if not skip_conflicts:
                    raise ConflictingOccurrences()
                # Cancel OUR occurrence
                msg = 'Skipped due to collision with {} reservation(s)'
                occurrence.cancel(user,
                                  msg.format(len(conflicts['confirmed'])),
                                  silent=True,
                                  propagate=False)
            elif conflicts['pending'] and self.is_accepted:
                # Reject OTHER occurrences
                for conflict in conflicts['pending']:
                    conflict.reject(
                        user,
                        'Rejected due to collision with a confirmed reservation'
                    )
Ejemplo n.º 40
0
 def can_be_cancelled(self, user):
     if user is None:
         return False
     return self.is_owned_by(user) or rb_is_admin(
         user) or self.is_booked_for(user)
Ejemplo n.º 41
0
 def can_delete(self, user, allow_admin=True):
     if user is None:
         return False
     return allow_admin and rb_is_admin(user) and (self.is_cancelled
                                                   or self.is_rejected)
Ejemplo n.º 42
0
 def can_be_overridden(self, user):
     if not user:
         return False
     return rb_is_admin(user) or self.is_owned_by(user)
Ejemplo n.º 43
0
 def can_delete(self, user, allow_admin=True):
     if not user:
         return False
     return user == self.created_by_user or (allow_admin
                                             and rb_is_admin(user))
Ejemplo n.º 44
0
 def can_be_modified(self, user):
     """Only admin can modify rooms."""
     if not user:
         return False
     return rb_is_admin(user)
Ejemplo n.º 45
0
 def can_edit(self, user):
     if not user:
         return False
     return rb_is_admin(user)
Ejemplo n.º 46
0
def test_rb_is_admin(create_user, is_admin, is_rb_admin, expected):
    user = create_user(1, admin=is_admin, rb_admin=is_rb_admin)
    assert rb_is_admin(user) == expected
Ejemplo n.º 47
0
def get_rooms_conflicts(rooms,
                        start_dt,
                        end_dt,
                        repeat_frequency,
                        repeat_interval,
                        blocked_rooms,
                        nonbookable_periods,
                        unbookable_hours,
                        skip_conflicts_with=None,
                        allow_admin=False,
                        skip_past_conflicts=False):
    rooms_conflicts = defaultdict(set)
    rooms_pre_conflicts = defaultdict(set)
    rooms_conflicting_candidates = defaultdict(set)
    skip_conflicts_with = skip_conflicts_with or []

    candidates = ReservationOccurrence.create_series(
        start_dt, end_dt, (repeat_frequency, repeat_interval))
    room_ids = [room.id for room in rooms]
    query = (ReservationOccurrence.query.filter(
        Reservation.room_id.in_(room_ids), ReservationOccurrence.is_valid,
        ReservationOccurrence.filter_overlap(candidates)).join(
            ReservationOccurrence.reservation).options(
                ReservationOccurrence.NO_RESERVATION_USER_STRATEGY,
                contains_eager(ReservationOccurrence.reservation)))

    if skip_conflicts_with:
        query = query.filter(~Reservation.id.in_(skip_conflicts_with))
    if skip_past_conflicts:
        query = query.filter(ReservationOccurrence.start_dt > datetime.now())

    overlapping_occurrences = group_list(
        query,
        key=lambda obj: obj.reservation.room.id,
        sort_by=lambda obj: obj.reservation.room.id)
    for room_id, occurrences in overlapping_occurrences.items():
        conflicts = get_room_bookings_conflicts(candidates, occurrences,
                                                skip_conflicts_with)
        rooms_conflicts[room_id], rooms_pre_conflicts[
            room_id], rooms_conflicting_candidates[room_id] = conflicts
    for room_id, occurrences in blocked_rooms.items():
        conflicts, conflicting_candidates = get_room_blockings_conflicts(
            room_id, candidates, occurrences, allow_admin=allow_admin)
        rooms_conflicts[room_id] |= conflicts
        rooms_conflicting_candidates[room_id] |= conflicting_candidates

    if not (allow_admin and rb_is_admin(session.user)):
        for room_id, occurrences in nonbookable_periods.items():
            room = Room.get_or_404(room_id)
            if not room.can_override(session.user, allow_admin=allow_admin):
                conflicts, conflicting_candidates = get_room_nonbookable_periods_conflicts(
                    candidates, occurrences)
                rooms_conflicts[room_id] |= conflicts
                rooms_conflicting_candidates[room_id] |= conflicting_candidates

        for room_id, occurrences in unbookable_hours.items():
            room = Room.get_or_404(room_id)
            if not room.can_override(session.user, allow_admin=allow_admin):
                conflicts, conflicting_candidates = get_room_unbookable_hours_conflicts(
                    candidates, occurrences)
                rooms_conflicts[room_id] |= conflicts
                rooms_conflicting_candidates[room_id] |= conflicting_candidates
    rooms_conflicting_candidates = defaultdict(
        list, ((k, list(v)) for k, v in rooms_conflicting_candidates.items()))
    return rooms_conflicts, rooms_pre_conflicts, rooms_conflicting_candidates
Ejemplo n.º 48
0
 def can_be_deleted(self, user):
     if user is None:
         return False
     return rb_is_admin(user)
Ejemplo n.º 49
0
 def _process(self):
     return jsonify(
         user=self._get_permissions(allow_admin=False),
         admin=(self._get_permissions(
             allow_admin=True) if rb_is_admin(session.user) else None))
Ejemplo n.º 50
0
 def can_be_rejected(self, user):
     if user is None:
         return False
     return rb_is_admin(user) or self.room.is_owned_by(user)
Ejemplo n.º 51
0
 def _check_access(self):
     if session.user is None:
         self._require_user()
     elif not rb_is_admin(session.user):
         raise Forbidden(_('You are not authorized to take this action.'))
Ejemplo n.º 52
0
 def _jsonify_user_permissions(user):
     permissions = Room.get_permissions_for_user(user, allow_admin=False)
     return jsonify(user=permissions,
                    admin=(Room.get_permissions_for_user(user)
                           if rb_is_admin(user) else None))
Ejemplo n.º 53
0
 def _check_access(self):
     user = session.user
     if not user or (not rb_is_admin(user) and not self.blocked_room.room.is_owned_by(user)):
         raise Forbidden(_('You are not permitted to modify this blocking'))
Ejemplo n.º 54
0
def search_for_rooms(filters, allow_admin=False, availability=None):
    """Search for a room, using the provided filters.

    :param filters: The filters, provided as a dictionary
    :param allow_admin: A boolean specifying whether admins have override privileges
    :param availability: A boolean specifying whether (un)available rooms should be provided,
                         or `None` in case all rooms should be returned.
    """
    query = (
        Room.query.outerjoin(
            favorite_room_table,
            db.and_(
                favorite_room_table.c.user_id == session.user.id,
                favorite_room_table.c.room_id == Room.id)).reset_joinpoint(
                )  # otherwise filter_by() would apply to the favorite table
        .options(raiseload('owner')).filter(Room.is_active).order_by(
            favorite_room_table.c.user_id.is_(None),
            db.func.indico.natsort(Room.full_name)))

    criteria = {}
    if 'capacity' in filters:
        query = query.filter(Room.capacity >= filters['capacity'])
    if 'building' in filters:
        criteria['building'] = filters['building']
    if 'division' in filters:
        criteria['division'] = filters['division']
    query = query.filter_by(**criteria)
    if 'text' in filters:
        query = query.filter(_make_room_text_filter(filters['text']))
    if filters.get('equipment'):
        subquery = (db.session.query(RoomEquipmentAssociation).with_entities(
            db.func.count(RoomEquipmentAssociation.c.room_id)).filter(
                RoomEquipmentAssociation.c.room_id == Room.id,
                EquipmentType.name.in_(filters['equipment'])).join(
                    EquipmentType, RoomEquipmentAssociation.c.equipment_id ==
                    EquipmentType.id).correlate(Room).as_scalar())
        query = query.filter(subquery == len(filters['equipment']))
    if filters.get('features'):
        for feature in filters['features']:
            query = query.filter(
                Room.available_equipment.any(
                    EquipmentType.features.any(RoomFeature.name == feature)))
    if filters.get('favorite'):
        query = query.filter(favorite_room_table.c.user_id.isnot(None))
    if filters.get('mine'):
        ids = get_managed_room_ids(session.user)
        query = query.filter(Room.id.in_(ids))
    query = _filter_coordinates(query, filters)

    if availability is None:
        return query

    start_dt, end_dt = filters['start_dt'], filters['end_dt']
    repeatability = (filters['repeat_frequency'], filters['repeat_interval'])
    include_blockings = not rb_is_admin(session.user)
    availability_filters = [
        Room.filter_available(start_dt,
                              end_dt,
                              repeatability,
                              include_blockings=include_blockings,
                              include_pre_bookings=True)
    ]
    if not (allow_admin and rb_is_admin(session.user)):
        selected_period_days = (filters['end_dt'] - filters['start_dt']).days
        booking_limit_days = db.func.coalesce(Room.booking_limit_days,
                                              rb_settings.get('booking_limit'))

        criterion = db.and_(
            Room.filter_bookable_hours(start_dt.time(), end_dt.time()),
            Room.filter_nonbookable_periods(start_dt, end_dt),
            db.or_(booking_limit_days.is_(None),
                   selected_period_days <= booking_limit_days))
        unbookable_ids = [
            room.id for room in query.filter(db.and_(
                *availability_filters), ~criterion)
            if not room.can_override(session.user, allow_admin=False)
        ]
        availability_filters.append(~Room.id.in_(unbookable_ids))
    availability_criterion = db.and_(*availability_filters)
    if availability is False:
        availability_criterion = ~availability_criterion
    return query.filter(availability_criterion)
Ejemplo n.º 55
0
 def _checkProtection(self):
     if not session.user:
         self._checkSessionUser()
     elif not rb_is_admin(session.user):
         raise Forbidden(_('You are not authorized to take this action.'))