class YourReservations(ResourceBaseForm, YourReservationsData):

    permission = "seantis.reservation.SubmitReservation"

    grok.name('your-reservations')
    grok.require(permission)

    context_buttons = ('finish', )

    grok.context(Interface)

    css_class = 'seantis-reservation-form'

    template = grok.PageTemplateFile('templates/your_reservations.pt')

    @button.buttonAndHandler(_(u'Submit Reservations'), name="finish")
    def finish(self, data):
        def on_success():
            self.request.response.redirect(self.context.absolute_url())
            self.flash(_(u'Reservations Successfully Submitted'))

        utils.handle_action(self.confirm_reservations, success=on_success)

    @button.buttonAndHandler(_(u'Reserve More'), name="proceed")
    def proceed(self, data):
        # Don't do anything, reservations stay in the session.
        self.request.response.redirect(self.context.absolute_url())

    def update(self):
        if 'remove' in self.request and utils.is_uuid(self.request['remove']):
            self.remove_reservation(self.request['remove'])

            self.request.response.redirect(self.context.absolute_url())

        super(YourReservations, self).update()
    def hint(self):
        if not (self.reservation or self.approved_reservations()):
            return _(u'No such reservation')

        return _(
            u'Do you really want to revoke the following reservations?'
        )
    def isValidThreshold(Allocation):

        thresholds = [
            Allocation.available_threshold,
            Allocation.partly_available_threshold,
        ]

        if thresholds[0] == thresholds[1]:
            raise Invalid(
                _(u'Thresholds must have differing values')
            )

        if not (thresholds[0] > thresholds[1]):
            raise Invalid(
                _(
                    u'The available threshold must have a larger value than '
                    u'the partly available threshold'
                )
            )

        for threshold in thresholds:
            if not (0 <= threshold and threshold <= 100):
                raise Invalid(
                    _(
                        u'Thresholds must have values between 0 and 100'
                    )
                )
Beispiel #4
0
    def links(self, frame=None):

        # global links
        if not frame:
            baseurl = self.context.absolute_url()
            return [(_(u'Add timeframe'),
                    baseurl + '/++add++seantis.reservation.timeframe')]

        # frame specific links
        links = []

        action_tool = getToolByName(frame, 'portal_actions')
        actions = action_tool.listFilteredActionsFor(frame)['workflow']

        for action in actions:
            if action['visible'] and action['available']:
                action['title'] = utils.translate_workflow(
                    self.context, self.request, action['title']
                )
                links.append((action['title'], action['url']))

        baseurl = frame.absolute_url()
        links.append((_(u'Edit'), baseurl + '/edit'))
        links.append((_(u'Delete'), baseurl + '/delete_confirmation'))

        return links
class ReservationApprovalForm(ReservationDecisionForm):

    permission = 'seantis.reservation.ApproveReservations'

    grok.name('approve-reservation')
    grok.require(permission)

    context_buttons = ('approve', )

    label = _(u'Approve reservation')

    @property
    def hint(self):
        if not self.pending_reservations():
            return _(u'No such reservation')

        return _(u'Do you really want to approve the following reservations?')

    @button.buttonAndHandler(_(u'Approve'))
    @extract_action_data
    def approve(self, data):

        def approve():
            self.scheduler.approve_reservation(data['reservation'])
            self.flash(_(u'Reservation confirmed'))

        utils.handle_action(action=approve, success=self.redirect_to_context)

    @button.buttonAndHandler(_(u'Cancel'))
    def cancel(self, action):
        self.redirect_to_context()
class ReservationDenialForm(ReservationDecisionForm):

    permission = 'seantis.reservation.ApproveReservations'

    grok.name('deny-reservation')
    grok.require(permission)

    destructive_buttons = ('deny', )

    label = _(u'Deny reservation')

    @property
    def hint(self):
        if not self.pending_reservations():
            return _(u'No such reservation')

        return _(u'Do you really want to deny the following reservations?')

    @button.buttonAndHandler(_(u'Deny'))
    @extract_action_data
    def deny(self, data):

        def deny():
            self.scheduler.deny_reservation(data['reservation'])
            self.flash(_(u'Reservation denied'))

        utils.handle_action(action=deny, success=self.redirect_to_context)

    @button.buttonAndHandler(_(u'Cancel'))
    def cancel(self, action):
        self.redirect_to_context()
def human_date(date):
    # timezones are currently naive and implicity the one used by
    # the users - we don't have international reservations yet
    now = utils.utcnow()

    this_morning = datetime(
        now.year, now.month, now.day
    ).replace(tzinfo=now.tzinfo)

    time = utils.localize_date(date, time_only=True)

    if date >= this_morning:
        return _(u'Today, at ${time}', mapping={'time': time})

    days = (now.date() - date.date()).days

    if days <= 1:
        return _(u'Yesterday, at ${time}', mapping={
            'time': time
        })
    else:
        return _(u'${days} days ago, at ${time}', mapping={
            'days': days,
            'time': time
        })
class ISelectionReservation(Interface):
    """ A reservation of a selected number of allocations. """

    # hidden
    ids = schema.TextLine(
        title=_(u'Ids'),
        required=False
    )

    start_time = schema.Time(
        title=_(u'Start'),
        required=False
    )

    end_time = schema.Time(
        title=_(u'End'),
        required=False
    )

    quota = schema.Int(
        title=_(u'Number of Reservations'),
        required=False,
        default=1
    )

    email = Email(
        title=_(u'Email'),
        required=True
    )
Beispiel #9
0
class IExportSelection(Interface):
    """ Configuration for all exports. """

    export = schema.Choice(
        title=_(u'Export'),
        source=export_choices,
        required=True,
        default=sources[0].id,
    )

    format = schema.Choice(
        title=_(u'Format'),
        source=format_choices,
        required=True,
        default=extensions.keys()[0]
    )

    year = schema.Choice(
        title=_(u"Year"),
        source=year_choices,
        required=True,
        default=u'all'
    )

    month = schema.Choice(
        title=_(u"Month"),
        source=month_choices,
        required=True,
        default=u'all'
    )
    def add_reservation(start, end, reservation):
        day = start.day

        used_days[day] = True

        end += timedelta(microseconds=1)
        start, end = start.strftime("%H:%M"), end.strftime("%H:%M")

        context = resources[utils.string_uuid(reservation.resource)]

        if reservation.status == u"approved":
            rightside_urls = [(_(u"Delete"), reservation_urls.revoke_all_url(reservation.token, context))]
        elif reservation.status == u"pending":
            rightside_urls = [
                (_(u"Approve"), reservation_urls.approve_all_url(reservation.token, context)),
                (_(u"Deny"), reservation_urls.deny_all_url(reservation.token, context)),
            ]
        else:
            raise NotImplementedError

        reservation_lists = report[day][utils.string_uuid(reservation.resource)]
        reservation_lists[reservation.status].append(
            dict(
                start=start,
                end=end,
                email=reservation.email,
                data=reservation.data,
                timespans=json_timespans(start, end),
                rightside_urls=rightside_urls,
                token=reservation.token,
                quota=utils.get_reservation_quota_statement(reservation.quota),
                resource=context,
            )
        )
class ISeantisReservationSettings(Interface):

    throttle_minutes = schema.Int(
        title=_(u"Reservation Throttling"),
        description=_(
            u'The number of minutes a user needs to wait between '
            u'reservations, use 0 if no throttling should occur. '
            u'Users with the \'Unthrottled Reservations\' permission '
            u'are excempt from this rule (Reservation-Managers by default).'))

    send_email_to_managers = schema.Bool(
        title=_(u"Email Notifications for Managers"),
        description=_(u'Send emails about newly made reservations to '
                      u'the first reservation managers found in the path.'))

    send_approval_email_to_managers = schema.Bool(
        title=_(u"Approval Email Notifications for Managers"),
        description=_(u'Send emails about new pending reservations to '
                      u'the first reservation managers found in the path.'))

    send_email_to_reservees = schema.Bool(
        title=_(u"Email Notifications for Reservees"),
        description=_(
            u'Send emails about made, approved and denied reservations '
            u'to the user that made the reservation.'))

    pre_reservation_script = schema.Text(
        title=_(u"Pre-Reservation Script"),
        description=_(
            u'Run custom validation code for reservations. This is for '
            u'advanced users only and may disable the reservations process. '
            u'For documentation study the source code at Github.'),
        required=False,
        constraint=valid_expression)
Beispiel #12
0
class GroupReservationForm(ReservationForm, AllocationGroupView,
                           SessionFormdataMixin, YourReservationsData):
    permission = 'seantis.reservation.SubmitReservation'

    grok.name('reserve-group')
    grok.require(permission)

    context_buttons = ('reserve', )
    standalone_buttons = ('cancel', )

    fields = field.Fields(IGroupReservation)
    label = _(u'Recurrance reservation')

    template = ViewPageTemplateFile('templates/reserve_group.pt')

    ignore_requirements = True

    autoGroups = True
    enable_form_tabbing = True
    default_fieldset_label = _(u'General Information')

    @property
    def hidden_fields(self):
        hidden = ['group']

        try:
            allocation = self.group and self.scheduler.allocations_by_group(
                self.group).first()

            if allocation.quota_limit == 1:
                hidden.append('quota')

        except DirtyReadOnlySession:
            pass

        return hidden

    def defaults(self, **kwargs):
        return self.your_reservation_defaults(dict(group=self.group, quota=1))

    @button.buttonAndHandler(_(u'Reserve'))
    @extract_action_data
    def reserve(self, data):

        approve_manually = self.scheduler.allocations_by_group(data['group']) \
            .first().approve_manually

        def reserve():
            self.run_reserve(data=data,
                             approve_manually=approve_manually,
                             group=data['group'],
                             quota=data['quota'])

        utils.handle_action(action=reserve,
                            success=self.redirect_to_your_reservations)

    @button.buttonAndHandler(_(u'Cancel'))
    def cancel(self, action):
        self.redirect_to_context()
Beispiel #13
0
    def validate(self, data):
        try:
            start, end = utils.get_date_range(data["day"], data["start_time"], data["end_time"])
            if not self.allocation(data["id"]).contains(start, end):
                utils.form_error(_(u"Reservation out of bounds"))

            return start, end
        except (NoResultFound, TypeError):
            utils.form_error(_(u"Invalid reservation request"))
class IGroupReservation(Interface):
    """ A reservation of an allocation group. """

    group = schema.Text(title=_(u'Recurrence'), required=False)

    quota = schema.Int(title=_(u'Reservation Quota'),
                       required=False,
                       default=1)

    email = EmailField(title=_(u'Email'), required=True)
Beispiel #15
0
    def validate(self, data):
        try:
            start, end = utils.get_date_range(data['day'], data['start_time'],
                                              data['end_time'])
            if not self.allocation(data['id']).contains(start, end):
                utils.form_error(_(u'Reservation out of bounds'))

            return start, end
        except (NoResultFound, TypeError):
            utils.form_error(_(u'Invalid reservation request'))
Beispiel #16
0
class ExportSelection(BaseForm, form.ResourceParameterView):
    permission = 'seantis.reservation.ViewReservations'
    grok.require(permission)
    grok.context(Interface)
    grok.name('reservation-exports')

    label = _(u'Reservation Export')

    fields = field.Fields(IExportSelection)
    ignoreContext = True

    enable_unload_protection = False

    @property
    def action(self):
        return u'{base}/reservation-exports?uuid={uuids}'.format(
            base=self.context.absolute_url(),
            uuids='&uuid='.join(self.uuids)
        )

    def build_export_url(self, data):
        if not self.uuids:
            utils.form_error(_(u"Missing 'uuid' parameter"))

        url_template = (
            u'{base}/reservation-export.{format}?source={export}&uuid={uuids}'
            u'&year={year}&month={month}'
        )

        data['base'] = self.context.absolute_url()
        data['uuids'] = '&uuid='.join(self.uuids)

        return url_template.format(**data)

    def updateActions(self):
        super(ExportSelection, self).updateActions()
        self.actions['export'].addClass('allowMultiSubmit')
        self.actions['export'].addClass('context')

    def update(self):
        self.fields['export'].field = copy(self.fields['export'].field)
        self.fields['export'].field.description = get_sources_description(
            self.request
        )
        super(ExportSelection, self).update()

    @button.buttonAndHandler(_(u'Export'))
    @extract_action_data
    def export(self, data):
        self.request.response.redirect(self.build_export_url(data))

    @button.buttonAndHandler(_(u'Cancel'))
    @extract_action_data
    def cancel(self, data):
        self.request.response.redirect(self.context.absolute_url())
class IReservation(Interface):
    """ A reservation of an allocation (may be pending or approved). """

    id = schema.Int(title=_(u'Id'), default=-1, required=False)

    day = schema.Date(title=_(u'Day'), required=False)

    start_time = schema.Time(title=_(u'Start'), required=False)

    end_time = schema.Time(title=_(u'End'), required=False)

    quota = schema.Int(title=_(u'Reservation Quota'),
                       required=False,
                       default=1)

    email = EmailField(title=_(u'Email'), required=True)

    description = schema.TextLine(
        title=_(u'Description'),
        description=_('Visible on the calendar'),
        required=False,
    )

    recurrence = schema.Text(
        title=_(u'Recurrence'),
        required=False,
    )
Beispiel #18
0
class ISearchAndReserveForm(model.Schema):
    """ Search form for search & reserve view. """

    form.mode(timeframes='hidden')
    timeframes = schema.Text(title=_(u'Timeframes'),
                             default=u'',
                             required=False)

    recurrence_start = schema.Date(title=_(u"Start date"), required=True)

    recurrence_end = schema.Date(title=_(u"End date"), required=True)

    whole_day = schema.Bool(title=_(u"Whole day"),
                            required=False,
                            default=False)

    start_time = schema.Time(title=_(u"Start time"), required=False)

    end_time = schema.Time(title=_(u"End time"), required=False)

    form.widget(days=CheckBoxFieldWidget)
    days = schema.List(title=_(u"Days"),
                       value_type=schema.Choice(vocabulary=weekdays),
                       required=False)

    minspots = schema.Int(title=_(u"Spots"), required=False)

    available_only = schema.Bool(title=_(u"Available only"),
                                 required=False,
                                 default=True)
Beispiel #19
0
class AllocationRemoveForm(AllocationForm, AllocationGroupView):
    permission = 'cmf.ModifyPortalContent'

    grok.name('remove-allocation')
    grok.require(permission)

    destructive_buttons = ('delete', )

    fields = field.Fields(IAllocation).select('id', 'group') + \
                            field.Fields(schema.Int(__name__='recurrence_id',
                                                    required=False))
    template = ViewPageTemplateFile('templates/remove_allocation.pt')

    label = _(u'Remove allocations')

    hidden_fields = ['id', 'group', 'recurrence_id']
    ignore_requirements = True

    @button.buttonAndHandler(_(u'Delete'))
    @extract_action_data
    def delete(self, data):
        nof_params = len(
            list(
                itertools.ifilter(None, (
                    data['id'],
                    data['group'],
                    data['recurrence_id'],
                ))))
        assert nof_params == 1, "Exactly one of id, group or recurrence_id"

        scheduler = self.scheduler

        def delete():
            scheduler.remove_allocation(id=data['id'],
                                        group=data['group'],
                                        recurrence_id=data['recurrence_id'])
            self.flash(_(u'Allocation removed'))

        utils.handle_action(action=delete, success=self.redirect_to_context)

    @button.buttonAndHandler(_(u'Cancel'))
    def cancel(self, action):
        self.redirect_to_context()

    def defaults(self):
        id, group, recurrence_id = self.id, self.group, self.recurrence_id
        result = dict(id=None, recurrence_id=None, group=None)
        if recurrence_id:
            result['recurrence_id'] = recurrence_id
        elif group:
            result['group'] = group
        elif id:
            result['id'] = id
        return result
    def validate(self, data):
        try:
            start, end = utils.get_date_range(
                data['day'], data['start_time'], data['end_time']
            )
            if not self.allocation(data['id']).contains(start, end):
                utils.form_error(_(u'Reservation out of bounds'))

            return start, end
        except (NoResultFound, TypeError):
            utils.form_error(_(u'Invalid reservation request'))
Beispiel #21
0
class AllocationRemoveForm(AllocationForm, AllocationGroupView):
    permission = 'cmf.ModifyPortalContent'

    grok.name('remove-allocation')
    grok.require(permission)

    destructive_buttons = ('delete', )
    standalone_buttons = ('cancel', )

    fields = field.Fields(IAllocation).select('id', 'group')
    template = ViewPageTemplateFile('templates/remove_allocation.pt')

    label = _(u'Delete allocations')

    hidden_fields = ['id', 'group']
    ignore_requirements = True

    @property
    def allocation_groups(self):

        if not self.group:
            return []

        return self.group.split(',')

    @button.buttonAndHandler(_(u'Delete'))
    @extract_action_data
    def remove(self, data):

        assert bool(data['id']) != bool(data['group']), \
            "Either id or group, not both"

        scheduler = self.scheduler
        groups = self.allocation_groups

        def remove():
            scheduler.remove_allocation(id=data['id'], groups=groups)
            if data['id']:
                self.flash(_(u'Allocation deleted'))
            else:
                self.flash(_(u'Allocations deleted'))

        utils.handle_action(action=remove, success=self.redirect_to_context)

    @button.buttonAndHandler(_(u'Cancel'))
    def cancel(self, action):
        self.redirect_to_context()

    def defaults(self):

        if self.group:
            return dict(group=self.group, id=None)
        else:
            return dict(id=self.id, group=None)
    def isValidFirstLastHour(Resource):
        in_valid_range = lambda h: 0 <= h and h <= 24
        first_hour, last_hour = Resource.first_hour, Resource.last_hour

        if not in_valid_range(first_hour):
            raise Invalid(_(u'Invalid first hour'))

        if not in_valid_range(last_hour):
            raise Invalid(_(u'Invalid last hour'))

        if last_hour <= first_hour:
            raise Invalid(_(u'First hour must be smaller than last hour'))
class ITimeframe(form.Schema):
    """ A timespan which is either visible or hidden. """

    title = schema.TextLine(title=_(u'Name'))

    start = schema.Date(title=_(u'Start'))

    end = schema.Date(title=_(u'End'))

    @invariant
    def isValidDateRange(Timeframe):
        if Timeframe.start > Timeframe.end:
            raise Invalid(_(u'End date before start date'))
Beispiel #24
0
    def uncommitted_reservations(self):
        count = self.uncommitted_reservations_count

        if not count:
            return u''

        if count == 1:
            return _(
                u'There is one reservation being entered for this resource.')

        return _(
            u'There are ${nb} reservations being entered for this resource.',
            mapping={'nb': self.uncommitted_reservations_count})
Beispiel #25
0
    def uncommitted_reservations(self):
        count = self.uncommitted_reservations_count

        if not count:
            return u""

        if count == 1:
            return _(u"There is one reservation being entered for this resource.")

        return _(
            u"There are ${nb} reservations being entered for this resource.",
            mapping={"nb": self.uncommitted_reservations_count},
        )
    def isValidFirstLastHour(Resource):
        in_valid_range = lambda h: 0 <= h and h <= 24
        first_hour, last_hour = Resource.first_hour, Resource.last_hour

        if not in_valid_range(first_hour):
            raise Invalid(_(u'Invalid first hour'))

        if not in_valid_range(last_hour):
            raise Invalid(_(u'Invalid last hour'))

        if last_hour <= first_hour:
            raise Invalid(
                _(u'First hour must be smaller than last hour')
            )
Beispiel #27
0
class ReservationRevocationForm(ReservationTargetForm, ReservationListView,
                                ReservationUrls):

    permission = 'seantis.reservation.ApproveReservations'

    grok.name('revoke-reservation')
    grok.require(permission)
    grok.layer(ISeantisReservationSpecific)

    destructive_buttons = ('revoke', )
    standalone_buttons = ('cancel', )

    fields = field.Fields(IRevokeReservation)
    template = ViewPageTemplateFile('templates/revoke_reservation.pt')

    label = _(u'Revoke reservation')

    show_links = False

    @property
    def hint(self):
        if not self.has_reservations:
            return _(u'No such reservation')

        return _(u'Do you really want to revoke the following reservations?')

    @property
    def has_reservations(self):
        return self.approved_reservations() and True or False

    @button.buttonAndHandler(_(u'Revoke'))
    @extract_action_data
    def revoke(self, data):

        # might happen if the user submits twice
        if not self.has_reservations:
            return

        def revoke():
            self.scheduler.revoke_reservation(token=data['token'],
                                              reason=data['reason'],
                                              id=data.get('id'),
                                              send_email=data['send_email'])
            self.flash(_(u'Reservation revoked'))

        utils.handle_action(action=revoke, success=self.redirect_to_context)

    @button.buttonAndHandler(_(u'Cancel'))
    def cancel(self, action):
        self.redirect_to_context()
    def run_reserve(
            self, data, approve_manually, dates=None, group=None, quota=1,
            rrule=None, description=None
    ):

        assert dates or group
        assert not (dates and group)

        email = self.email(data)
        session_id = self.session_id()

        additional_data = self.additional_data(data, add_manager_defaults=True)

        # only store forms defined in the formsets list
        additional_data = dict(
            (
                form, additional_data[form]
            ) for form in self.context.formsets if form in additional_data
        )

        if dates:
            for start, end in utils.pairs(dates):
                run_pre_reserve_script(
                    self.context, start, end, additional_data
                )
        else:
            run_pre_reserve_script(self.context, None, None, additional_data)

        def run():
            if dates:
                return self.scheduler.reserve(
                    email, dates, data=additional_data,
                    session_id=session_id, quota=quota, rrule=rrule,
                    description=description
                )
            else:
                return self.scheduler.reserve(
                    email, group=group,
                    data=additional_data, session_id=session_id, quota=quota,
                    description=description
                )

        token = throttled(run, 'reserve')()

        if approve_manually:
            self.flash(_(u'Added to waitinglist'))
        else:
            self.scheduler.approve_reservation(token)
            self.flash(_(u'Reservation successful'))
class IReservationTargetForm(Interface):
    """ Describes a form with a hidden reservation-token and an optional
    reservation-id field.
    """

    token = schema.Text(
        title=_(u'Token'),
        required=False
    )

    id = schema.Text(
        title=_(u'Id'),
        required=False,
        default=None
    )
Beispiel #30
0
    def uncommitted_reservations(self):
        count = self.uncommitted_reservations_count

        if not count:
            return u''

        if count == 1:
            return _(
                u'There is one reservation being entered for this resource.'
            )

        return _(
            u'There are ${nb} reservations being entered for this resource.',
            mapping={'nb': self.uncommitted_reservations_count}
        )
    def validate_templates(email):
        # go through the *_content attributes
        content_attributes = (
            (k, v) for k, v in IEmailTemplate._v_attrs.items()
            if k.endswith('_content')
        )

        # gather all the %(xxx)s tags
        form_tags = re.compile(r'%\(([a-z_]+)\)')

        # try to fill those with dummy variables
        for key, attr in content_attributes:
            tags = form_tags.findall(attr.getDoc())
            test_data = zip(tags, (str(i) for i in range(0, len(tags))))

            try:
                getattr(email, key) % dict(test_data)

            # if anything fails, it would also in the email sending process
            # which is the last thing we want to fail
            except KeyError as e:
                raise Invalid(
                    _(
                        u"Invalid template variable '${name}' in '${field}'",
                        mapping={
                            'name': e.message,
                            'field': attr.title
                        }
                    )
                )
def validate_email(value):
    try:
        if value:
            checkEmailAddress(value)
    except EmailAddressInvalid:
        raise Invalid(_(u'Invalid email address'))
    return True
Beispiel #33
0
    def links(self, template=None):

        # global links
        if not template:
            baseurl = self.context.absolute_url()
            return [(_(u'Add email template'),
                     baseurl + '/++add++seantis.reservation.emailtemplate')]

        # template specific links
        links = []

        baseurl = template.absolute_url()
        links.append((_(u'Edit'), baseurl + '/edit'))
        links.append((_(u'Delete'), baseurl + '/delete_confirmation'))

        return links
def validate_email(value):
    try:
        if value:
            checkEmailAddress(value)
    except EmailAddressInvalid:
        raise Invalid(_(u'Invalid email address'))
    return True
 def isValidOption(Allocation):
     if Allocation.recurrence:
         if Allocation.partly_available and not Allocation.separately:
             raise Invalid(_(
                 u'Partly available allocations can only be reserved '
                 u'separately'
             ))
Beispiel #36
0
        def save():
            if self.reservation.email != data['email']:
                self.scheduler.change_email(self.token, data['email'])

            self.scheduler.change_reservation_data(self.token,
                                                   self.additional_data)
            self.flash(_(u'Formdata updated'))
Beispiel #37
0
def validate_template(context, request, data):
    if context.portal_type == 'seantis.reservation.emailtemplate':
        folder = context.aq_inner.aq_parent
    else:
        folder = context

    templates = utils.portal_type_in_context(
        folder, portal_type='seantis.reservation.emailtemplate')

    duplicate = False

    for template in templates:
        if template.id == context.id:
            continue

        if template.getObject().title == context.title:
            duplicate = True
            break

    if duplicate:
        msg = utils.translate(
            context, request,
            _(u"There's already an Email template in the same folder for the "
              u"same language"))
        utils.form_error(msg)
    def isValidRange(Allocation):
        start, end = utils.get_date_range(Allocation.day,
                                          Allocation.start_time,
                                          Allocation.end_time)

        if not Allocation.whole_day and abs((end - start).seconds // 60) < 5:
            raise Invalid(_(u'The allocation must be at least 5 minutes long'))
class IRevokeReservation(IReservationIdForm):
    """ For the reservation revocation form. """

    send_email = schema.Bool(
        title=_(u"Send Email"),
        description=_(
            u"Send an email to the reservee informing him of the revocation"),
        default=True)

    reason = schema.Text(
        title=_(u'Reason'),
        description=_(
            u"Optional reason for the revocation. Sent to the reservee. "
            u"e.g. 'Your reservation has to be cancelled because the lecturer "
            u"is ill'."),
        required=False)
 def isValidOption(Allocation):
     if Allocation.recurring:
         if Allocation.partly_available and not Allocation.separately:
             raise Invalid(_(
                 u'Partly available allocations can only be reserved '
                 u'separately'
             ))
Beispiel #41
0
def validate_template(context, request, data):
    if context.portal_type == 'seantis.reservation.emailtemplate':
        folder = context.aq_inner.aq_parent
    else:
        folder = context

    templates = utils.portal_type_in_context(
        folder, portal_type='seantis.reservation.emailtemplate'
    )

    duplicate = False

    for template in templates:
        if template.id == context.id:
            continue

        if template.getObject().title == context.title:
            duplicate = True
            break

    if duplicate:
        msg = utils.translate(
            context, request,
            _(u"There's already an Email template in the same folder for the "
              u"same language")
        )
        utils.form_error(msg)
Beispiel #42
0
        def change():
            start = datetime.combine(self.reservation.start.date(), data["start_time"])
            end = datetime.combine(self.reservation.end.date(), data["end_time"])

            changed = self.scheduler.change_reservation_time(
                token=data["token"],
                id=data["id"],
                new_start=start,
                new_end=end,
                send_email=data["send_email"],
                reason=data["reason"],
            )
            if changed:
                self.flash(_(u"Reservation changed."))
            else:
                self.flash(_(u"There was nothing to change."))
Beispiel #43
0
def as_human_readable_string(value):
    if isinstance(value, six.string_types):
        return value

    if isinstance(value, datetime):
        # don't use strftime here because users may end up entering year '3'
        # strftime does not suppport years before 1900, which is just lame
        # in this case we just use the German locale for now..
        if value.year < 1900:
            return '%02d.%02d.%04d %02d:%02d' % (
                value.day,
                value.month,
                value.year,
                value.hour,
                value.minute
            )
        else:
            return localize_date(value, long_format=True)

    if isinstance(value, date):
        if value.year < 1900:
            return '%02d.%02d.%04d' % (
                value.day,
                value.month,
                value.year
            )
        else:
            dt = datetime(value.year, value.month, value.day)
            return localize_date(dt, long_format=False)

    if isinstance(value, RichTextValue):
        return value.output

    if value is True:
        return _(u'Yes')

    if value is False:
        return _(u'No')

    if value is None:
        return u''

    if isinstance(value, (list, tuple)):
        return ', '.join(as_human_readable_string(v) for v in value)

    return value
    def isValidRange(Allocation):
        start, end = utils.get_date_range(
            Allocation.day,
            Allocation.start_time, Allocation.end_time
        )

        if not Allocation.whole_day and abs((end - start).seconds // 60) < 5:
            raise Invalid(_(u'The allocation must be at least 5 minutes long'))
Beispiel #45
0
        def edit():
            scheduler.move_allocation(*args)

            # ensure that the allocation is not accessed again by the defaults,
            # this prevents a DirtyReadOnlySession error
            self.allocation_stale = True

            self.flash(_(u'Allocation saved'))
Beispiel #46
0
    def links(self, template=None):

        # global links
        if not template:
            baseurl = self.context.absolute_url()
            return [(
                _(u'Add email template'),
                baseurl + '/++add++seantis.reservation.emailtemplate'
            )]

        # template specific links
        links = []

        baseurl = template.absolute_url()
        links.append((_(u'Edit'), baseurl + '/edit'))
        links.append((_(u'Delete'), baseurl + '/delete_confirmation'))

        return links
 def update(self, *args, **kwargs):
     super(SeantisReservationSettingsPanelForm, self).update(
         *args, **kwargs
     )
     if self.request.get('form.actions.remove_orphans'):
         count = self.remove_orphan_records()
         utils.flash(
             self.context,
             _(u'${count} Orphan Records Removed', mapping={'count': count})
         )
 def save():
     self.scheduler.update_reservation(
         self.reservation,
         start,
         end,
         data.get('email'),
         data.get('description'),
         self.additional_data,
     )
     self.flash(_(u'Formdata updated'))
def validate_timeframe(context, request, data):
    overlap = overlapping_timeframe(context, data['start'], data['end'])
    if overlap:
        msg = utils.translate(
            context, request,
            _(
                u"Timeframe overlaps with '${overlap}' in the current folder",
                mapping={'overlap': overlap.title}
            ))
        utils.form_error(msg)
 def title(self):
     return _(
         u'Monthly Report for ${month} ${year}',
         mapping={
             'month': utils.month_name(
                 self.context, self.request, self.month
             ),
             'year': self.year
         }
     )
Beispiel #51
0
 def additionalSchemata(self):
     return [
         (
             'default', _(u'Settings'),
             field.Fields(IResourceAllocationDefaults).select(
                 'quota',
                 'reservation_quota_limit',
                 'approve_manually'
             )
         )
     ]
    def daterange_label(self):
        since, until = self.daterange

        if until.date() == utils.utcnow().date():
            return ' - '.join((
                utils.localize_date(since, long_format=False),
                self.translate(_(u'Today'))
            ))
        return ' - '.join((
            utils.localize_date(since, long_format=False),
            utils.localize_date(until, long_format=False)
        ))
Beispiel #53
0
 def allocate():
     self.scheduler.allocate(
         dates,
         raster=data['raster'],
         quota=data['quota'],
         partly_available=data['partly_available'],
         grouped=data['recurring'] and not data['separately'],
         approve_manually=data['approve_manually'],
         quota_limit=data['reservation_quota_limit'],
         whole_day=data['whole_day']
     )
     self.flash(_(u'Allocation added'))
Beispiel #54
0
    def events(self):
        resource = self.context
        scheduler = resource.scheduler()
        translate = utils.translator(self.context, self.request)

        is_exposed = exposure.for_allocations([resource])

        # get an event for each exposed allocation
        events = []
        for alloc in scheduler.allocations_in_range(*self.range):

            if not is_exposed(alloc):
                continue

            start = alloc.display_start(settings.timezone())
            end = alloc.display_end(settings.timezone())

            # get the urls
            urls = self.urls(alloc)

            # calculate the availability for title and class
            availability, title, klass = utils.event_availability(
                resource, self.request, scheduler, alloc
            )

            if alloc.partly_available:
                partitions = alloc.availability_partitions()
            else:
                # if the allocation is not partly available there can only
                # be one partition meant to be shown as empty unless the
                # availability is zero
                partitions = [(100, availability == 0.0)]

            event_header = alloc.whole_day and translate(_(u'Whole Day'))

            events.append(dict(
                title=title,
                start=start.isoformat(),
                end=end.isoformat(),
                className=klass,
                url=urls.default,
                menu=urls.menu,
                menuorder=urls.order,
                allocation=alloc.id,
                partitions=partitions,
                group=alloc.group,
                allDay=False,
                moveurl=urls.move,
                header=event_header
            ))

        return events
Beispiel #55
0
    def run_reserve(
        self, data, approve_manually, start=None, end=None, group=None, quota=1
    ):

        assert (start and end) or group
        assert not (start and end and group)

        email = self.email(data)
        additional_data = self.additional_data(data, add_manager_defaults=True)
        session_id = self.session_id()

        # only store forms defined in the formsets list
        additional_data = dict(
            (
                form, additional_data[form]
            ) for form in self.context.formsets if form in additional_data
        )

        run_pre_reserve_script(self.context, start, end, additional_data)

        def run():
            if start and end:
                return self.scheduler.reserve(
                    email, (start, end),
                    data=additional_data, session_id=session_id, quota=quota
                )
            else:
                return self.scheduler.reserve(
                    email, group=group,
                    data=additional_data, session_id=session_id, quota=quota
                )

        token = throttled(run, 'reserve')()

        if approve_manually:
            self.flash(_(u'Added to waitinglist'))
        else:
            self.scheduler.approve_reservation(token)
            self.flash(_(u'Reservation successful'))
Beispiel #56
0
    def build_export_url(self, data):
        if not self.uuids:
            utils.form_error(_(u"Missing 'uuid' parameter"))

        url_template = (
            u'{base}/reservation-export.{format}?source={export}&uuid={uuids}'
            u'&year={year}&month={month}'
        )

        data['base'] = self.context.absolute_url()
        data['uuids'] = '&uuid='.join(self.uuids)

        return url_template.format(**data)