def build_form_renderer():
    """
    Builds the form handler by creating and adding the forms.
    """

    # build forms
    membership_listing_date_pdf_form = deform.Form(
        MembershipListingDate(),
        buttons=[deform.Button('submit', _(u'Generate PDF'))],
        formid='membership_listing_date_pdf')

    membership_listing_year_end_pdf_form = deform.Form(
        MembershipListingYearEnd(),
        buttons=[deform.Button('submit', _(u'Generate PDF'))],
        formid='membership_listing_year_end_pdf')

    # create form handler
    form_renderer = MultipleFormRenderer()

    # add forms
    form_renderer.add_form(membership_listing_date_pdf_form,
                           membership_listing_date_pdf_callback)
    form_renderer.add_form(membership_listing_year_end_pdf_form,
                           membership_listing_date_pdf_callback)
    return form_renderer
Exemple #2
0
    class Fees(colander.Schema):
        member_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Please tell us wether you\'re an individual, '
                    u'freelancer or company or want to support us '
                    u'generously as a sustaining member'),
            widget=deform.widget.RadioChoiceWidget(
                values=[(member_type, t_description) for fee, member_type,
                        t_description in customization.membership_fees]),
            oid='member_type')

        # not validating here: depends on ^
        # http://deformdemo.repoze.org/require_one_or_another/
        member_custom_fee = colander.SchemaNode(
            colander.Decimal('1.00'),
            title=_(u'custom membership fee'),
            widget=deform.widget.MoneyInputWidget(
                symbol=customization.currency,
                showSymbol=True,
                defaultZero=True),
            description=_(
                u'Sustaining members: You can set your fees (minimum 100 €)'),
            oid='membership_custom_fee',
            default=customization.membership_fee_custom_min,
            validator=Range(
                min=customization.membership_fee_custom_min,
                max=None,
                min_err=
                _(u'please enter at least the minimum fee for sustaining members'
                  )))
Exemple #3
0
def build_form_renderer():
    """
    Builds the form handler by creating and adding the forms.
    """

    # build forms
    membership_listing_date_pdf_form = deform.Form(
        MembershipListingDate(),
        buttons=[deform.Button('submit', _(u'Generate PDF'))],
        formid='membership_listing_date_pdf')

    membership_listing_year_end_pdf_form = deform.Form(
        MembershipListingYearEnd(),
        buttons=[deform.Button('submit', _(u'Generate PDF'))],
        formid='membership_listing_year_end_pdf'
    )

    # create form handler
    form_renderer = MultipleFormRenderer()

    # add forms
    form_renderer.add_form(
        membership_listing_date_pdf_form,
        membership_listing_date_pdf_callback)
    form_renderer.add_form(
        membership_listing_year_end_pdf_form,
        membership_listing_date_pdf_callback)
    return form_renderer
Exemple #4
0
def show_success_pdf(request):
    """
    Given there is valid information in the session
    this view sends an encrypted mail to C3S staff with the users data set
    and returns a PDF for the user.

    It is called after visiting the verification URL/page/view in
    def success_verify_email below.
    """
    # check if user has used form or 'guessed' this URL
    if 'appstruct' in request.session:
        # we do have valid info from the form in the session
        # print("-- valid session with data found")
        # send mail to accountants // prepare a mailer
        mailer = get_mailer(request)
        # prepare mail
        appstruct = request.session['appstruct']
        message_recipient = request.registry.settings['c3smembership.mailaddr']
        appstruct['message_recipient'] = message_recipient
        the_mail = accountant_mail(appstruct)
        if 'true' in request.registry.settings['testing.mail_to_console']:
            print(the_mail.body)
        else:
            try:
                mailer.send(the_mail)
            except:
                # if mailout fails for some reason (gnupg, whatever), we need
                # 1) a mail to staff, so we take notice and fix it
                # 2) a message to $user, so she does not worry
                staff_mail = Message(subject=_("[yes][ALERT] check the logs!"),
                                     sender="*****@*****.**",
                                     recipients=['*****@*****.**'],
                                     body="""
problems! problems!

this is {}

a user could not download her pdf, because....
maybe gnupg failed? something else?

go fix it!
                    """.format(request.registry.settings['c3smembership.url']))
                mailer.send(staff_mail)

                request.session.flash(
                    _(u"Oops. we hit a bug. Staff will be "
                      u"informed. We will come back to you "
                      u"once we fixed it!",
                      'message_to_user')  # msg queue f. user
                )
                return HTTPFound(request.route_url('error_page'))

        return generate_pdf(request.session['appstruct'])
    # 'else': send user to the form
    # print("-- no valid session with data found")
    return HTTPFound(location=request.route_url('join'))
    class MembershipInfo(colander.Schema):

        yes_no = ((u'yes', _(u'Yes')),
                  (u'no', _(u'No')),
                  (u'dontknow', _(u'Unknown')),)

        entity_type = colander.SchemaNode(
            colander.String(),
            title=(u'Person oder Körperschaft?'),
            description=u'Bitte die Kategorie des Mitglied auswählen.',
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'person',
                     (u'Person')),
                    (u'legalentity',
                     u'Körperschaft'),
                ),
            ),
            missing=unicode(''),
            oid='entity_type',
        )
        membership_type = colander.SchemaNode(
            colander.String(),
            title=(u'Art der Mitgliedschaft (lt. Satzung, §4)'),
            description=u'Bitte die Art der Mitgliedschaft auswählen.',
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'normal',
                     (u'Normales Mitglied')),
                    (u'investing',
                     u'Investierendes Mitglied'),
                    (u'unknown',
                     u'Unbekannt.'),
                ),
            ),
            missing=unicode(''),
            oid='membership_type',
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title='Mitglied einer Verwertungsgesellschaft?',
            validator=colander.OneOf([x[0] for x in yes_no]),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            missing=unicode(''),
            oid="other_colsoc",
            # validator=colsoc_validator
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=(u'Falls ja, welche? (Kommasepariert)'),
            missing=unicode(''),
            oid="colsoc_name",
            # validator=colander.All(
            #    colsoc_validator,
            # )
        )
 class SharesSchema(colander.Schema):
     """
     Defines the colander schema for shares.
     """
     number = colander.SchemaNode(
         colander.Integer(),
         title=_('Number of Shares'),
     )
     date_of_acquisition = colander.SchemaNode(
         colander.Date(), title=_('Date of Acquisition'))
class MembershipListingDate(colander.Schema):
    """
    Provides a colander schema for entering a date.
    """

    date = colander.SchemaNode(colander.Date(),
                               title=_('Date'),
                               validator=colander.Range(
                                   max=datetime.date.today(),
                                   max_err=_('${val} is later than today')),
                               default=datetime.date.today())
Exemple #8
0
    class TermsInfo(colander.Schema):
        """
        some legal requirements
        """
        def statute_validator(node, value):
            """
            Validator for statute confirmation.
            """
            if not value:
                # raise without additional error message as the description
                # already explains the necessity of the checkbox
                raise Invalid(node, u'')

        got_statute = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            #title=(u''),
            title=_(u'I acknowledge that the statutes and membership dues '
                    u'regulations determine periodic contributions '
                    u'for full members.'),
            label=_(
                u'An electronic copy of the statute of the '
                u'C3S SCE has been made available to me (see link below).'),
            description=_(u'You must confirm to have access to the statute.'),
            widget=deform.widget.CheckboxWidget(),
            validator=statute_validator,
            required=True,
            oid='got_statute',
            #label=_('Yes, really'),
        )

        def dues_regulations_validator(node, value):
            """
            Validator for dues regulations confirmation.
            """
            if not value:
                # raise without additional error message as the description
                # already explains the necessity of the checkbox
                raise Invalid(node, u'')

        got_dues_regulations = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            title=(u''),
            label=_(
                u'An electronic copy of the temporary membership dues '
                u'regulations of the C3S SCE has been made available to me '
                u'(see link below).'),
            description=_(u'You must confirm to have access to the temporary '
                          u'membership dues regulations.'),
            widget=deform.widget.CheckboxWidget(),
            validator=dues_regulations_validator,
            required=True,
            oid='got_dues_regulations',
            #label=_('Yes'),
        )
def create_payment_filter_form():
    filter_schema = PaymentListFilterSchema()
    filter_form = deform.Form(
        filter_schema,
        buttons=[
            deform.Button('submit', _(u'Apply')),
            deform.Button('reset', _(u'Reset')),
        ],
        use_ajax=True,
        renderer=ZPT_RENDERER
    )
    return filter_form
Exemple #10
0
def login(request):
    """
    This view lets accountants log in (using a login form).

    If a person is already logged in, she is forwarded to the dashboard.
    """
    logged_in = authenticated_userid(request)

    LOG.info("login by %s", logged_in)

    if logged_in is not None:
        return get_dashboard_redirect(request)

    form = deform.Form(
        AccountantLogin(),
        buttons=[
            deform.Button('submit', _(u'Submit')),
            deform.Button('reset', _(u'Reset'))
        ],
    )

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)
        except ValidationFailure, e_validation_failure:
            request.session.flash(
                _(u"Please note: There were errors, "
                  "please check the form below."),
                'message_above_form',
                allow_duplicate=False)
            return{'form': e_validation_failure.render()}

        # get user and check pw...
        username = appstruct['login']
        password = appstruct['password']

        try:
            checked = Staff.check_password(username, password)
        except AttributeError:  # pragma: no cover
            checked = False
        if checked:
            LOG.info("password check for %s: good!", username)
            headers = remember(request, username)
            LOG.info("logging in %s", username)
            return HTTPFound(
                request.route_url(
                    'dashboard'),
                headers=headers)
        else:
            LOG.info("password check: failed for %s.", username)
Exemple #11
0
class AccountantLogin(colander.MappingSchema):
    """
    colander schema for login form
    """
    login = colander.SchemaNode(colander.String(),
                                title=_(u"login"),
                                oid="login")
    password = colander.SchemaNode(
        colander.String(),
        validator=colander.Length(min=5, max=100),
        widget=deform.widget.PasswordWidget(size=20),
        title=_(u"password"),
        oid="password")
Exemple #12
0
 class MembershipForm(colander.Schema):
     """
     The form for editing membership information combining all forms for
     the subject areas.
     """
     person = PersonalData(title=_(u'Personal Data'), )
     membership_meta = MembershipMeta(
         title=_(u'Membership Bureaucracy'),
         validator=colander.All(
             loss_type_and_date_set_validator,
             loss_date_larger_acceptance_validator,
             loss_date_resignation_validator)).bind(
                 membership_accepted=member.membership_accepted, )
     membership_info = MembershipInfo(title=_(u'Membership Requirements'))
Exemple #13
0
def accountants_login(request):
    """
    This view lets accountants log in (using a login form).

    If a person is already logged in, she is forwarded to the dashboard.
    """
    logged_in = authenticated_userid(request)

    LOG.info("login by %s", logged_in)

    if logged_in is not None:
        return get_dashboard_redirect(request)

    form = deform.Form(
        AccountantLogin(),
        buttons=[
            deform.Button('submit', _(u'Submit')),
            deform.Button('reset', _(u'Reset'))
        ],
    )

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)
        except ValidationFailure, e_validation_failure:
            request.session.flash(_(u"Please note: There were errors, "
                                    "please check the form below."),
                                  'message_above_form',
                                  allow_duplicate=False)
            return {'form': e_validation_failure.render()}

        # get user and check pw...
        login = appstruct['login']
        password = appstruct['password']

        try:
            checked = C3sStaff.check_password(login, password)
        except AttributeError:  # pragma: no cover
            checked = False
        if checked:
            LOG.info("password check for %s: good!", login)
            headers = remember(request, login)
            LOG.info("logging in %s", login)
            return HTTPFound(request.route_url('dashboard'), headers=headers)
        else:
            LOG.info("password check: failed for %s.", login)
Exemple #14
0
def send_accountant_mail(request, member):
    """
    Send an GPG encrypted email to the accountant informing about the new
    membership application.
    """
    mailer = request.registry.get_mailer(request)
    try:
        the_mail = create_accountant_mail(
            member,
            request.registry.settings['c3smembership.notification_sender'],
            [request.registry.settings['c3smembership.status_receiver']])
        if 'true' in request.registry.settings['testing.mail_to_console']:
            # pylint: disable=superfluous-parens
            print(the_mail.body)
        else:
            mailer.send(the_mail)
    except:
        mail = Message(
            subject=_("[yes][ALERT] check the logs!"),
            sender=request.registry.settings[
                'c3smembership.notification_sender'],
            recipients=[request.registry.settings[
                'c3smembership.status_receiver']],
            body="""
A failure occurred at {}. A notification email could not be sent.
Maybe gnupg failed and a key might be expired.
            """.format(request.registry.settings['c3smembership.url']))
        mailer.send(mail)
Exemple #15
0
    def loss_type_and_date_set_validator(form, value):
        """
        Validates whether the membership loss type is set.

        Membership date and type must both be either set or unset.
        """
        if (value['membership_loss_date'] is None) != \
                (value['membership_loss_type'] is None):
            exc = colander.Invalid(form)
            exc['membership_loss_type'] = \
                _(u'Date and type of membership loss must be set both or '
                  u'none.')
            exc['membership_loss_date'] = \
                _(u'Date and type of membership loss must be set both or '
                  u'none.')
            raise exc
Exemple #16
0
    def loss_type_and_date_set_validator(form, value):
        """
        Validates whether the membership loss type is set.

        Membership date and type must both be either set or unset.
        """
        if (value['membership_loss_date'] is None) != \
                (value['membership_loss_type'] is None):
            exc = colander.Invalid(form)
            exc['membership_loss_type'] = \
                _(u'Date and type of membership loss must be set both or '
                  u'none.')
            exc['membership_loss_date'] = \
                _(u'Date and type of membership loss must be set both or '
                  u'none.')
            raise exc
Exemple #17
0
 def membership_loss_type_entity_type_validator(form, value):
     """
     Validates that only natural persons can have loss type 'death' and
     only legal entites 'winding-up'.
     """
     if (
             value['membership_meta']['membership_loss_type'] == 'death' and
             value['membership_info']['entity_type'] != 'person'):
         exc_type = colander.Invalid(
             form['membership_meta']['membership_loss_type'],
             _(u'The membership loss type \'death\' is only allowed for '
               u'natural person members and not for legal entity members.'))
         exc_meta = colander.Invalid(form['membership_meta'])
         exc_meta.add(
             exc_type,
             get_child_position(
                 form['membership_meta'],
                 form['membership_meta']['membership_loss_type']))
         exc = colander.Invalid(form)
         exc.add(
             exc_meta,
             get_child_position(
                 form,
                 form['membership_meta']))
         raise exc
     if (
             value['membership_meta'][
                 'membership_loss_type'] == 'winding-up' and
             value['membership_info']['entity_type'] != 'legalentity'):
         exc_type = colander.Invalid(
             form['membership_meta']['membership_loss_type'],
             _(u'The membership loss type \'winding-up\' is only allowed '
               u'for legal entity members and not for natural person '
               u'members.'))
         exc_meta = colander.Invalid(form['membership_meta'])
         exc_meta.add(
             exc_type,
             get_child_position(
                 form['membership_meta'],
                 form['membership_meta']['membership_loss_type']))
         exc = colander.Invalid(form)
         exc.add(
             exc_meta,
             get_child_position(
                 form,
                 form['membership_meta']))
         raise exc
 class MembershipForm(colander.Schema):
     """
     The Form consists of
     - Personal Data
     - Membership Information
     - Shares
     """
     person = PersonalData(
         title=_(u"Personal Data"),
         # description=_(u"this is a test"),
         # css_class="thisisjustatest"
     )
     membership_info = MembershipInfo(
         title=_(u"Membership Requirements")
     )
     shares = Shares(
         title=_(u"Shares")
     )
Exemple #19
0
def show_success_pdf(request):
    """
    Given there is valid information in the session
    this view sends an encrypted mail to C3S staff with the users data set
    and returns a PDF for the user.

    It is called after visiting the verification URL/page/view in
    def success_verify_email below.
    """
    # check if user has used form or 'guessed' this URL
    if 'appstruct' in request.session:
        # we do have valid info from the form in the session
        # print("-- valid session with data found")
        # send mail to accountants // prepare a mailer
        mailer = get_mailer(request)
        # prepare mail
        appstruct = request.session['appstruct']
        message_recipient = request.registry.settings['c3smembership.mailaddr']
        appstruct['message_recipient'] = message_recipient
        the_mail = accountant_mail(appstruct)
        if 'true' in request.registry.settings['testing.mail_to_console']:
            print(the_mail.body)
        else:
            try:
                mailer.send(the_mail)
            except:
                # if mailout fails for some reason (gnupg, whatever), we need
                # 1) a mail to staff, so we take notice and fix it
                # 2) a message to $user, so she does not worry
                staff_mail = Message(
                    subject=_("[yes][ALERT] check the logs!"),
                    sender="*****@*****.**",
                    recipients=['*****@*****.**'],
                    body="""
problems! problems!

this is {}

a user could not download her pdf, because....
maybe gnupg failed? something else?

go fix it!
                    """.format(request.registry.settings['c3smembership.url']))
                mailer.send(staff_mail)

                request.session.flash(
                    u"Oops. we hit a bug. Staff will be "
                    u"informed. We will come back to you "
                    u"once we fixed it!",
                    'message_to_user'  # msg queue f. user
                )
                return HTTPFound(request.route_url('error_page'))

        return generate_pdf(request.session['appstruct'])
    # 'else': send user to the form
    # print("-- no valid session with data found")
    return HTTPFound(location=request.route_url('join'))
Exemple #20
0
 class MembershipForm(colander.Schema):
     """
     The Form consists of
     - Personal Data
     - Membership Information
     - Shares
     """
     person = PersonalData(title=_(u'Personal Data'), )
     if len(customization.membership_types
            ) > 1 or customization.enable_colsoc_association:
         membership_info = MembershipInfo(title=_(u'Membership Data'))
     shares = Shares(title=_(u'Shares'))
     try:
         customization.membership_fees
     except NameError:
         pass
     else:
         fees = Fees(title=_(u'Membership Fees'))
     acknowledge_terms = TermsInfo(title=_(u'Acknowledgement'))
Exemple #21
0
def deferred_dob_validator(node, keywords):
    """
    Deferred date of birth validator

    Needed for testing purposes.
    """
    kw_date = keywords['date']
    return colander.Range(
        min=kw_date(1913, 1, 1),
        # max 18th birthday, no minors through web formular
        max=kw_date(
            kw_date.today().year-18,
            kw_date.today().month,
            kw_date.today().day),
        min_err=_(
            u'Sorry, but we do not believe that the birthday you '
            u'entered is correct.'),
        max_err=_(
            u'Unfortunately, the membership application of an '
            u'underaged person is currently not possible via our web '
            u'form. Please send an email to [email protected].'))
Exemple #22
0
def success_check_email(request):
    """
    This view is called from the page that shows a user her data for correction
    by clicking a "send email" button.
    This view then sends out the email with a verification link
    and returns a note to go check mail.
    """
    # check if user has used the form (good) or 'guessed' this URL (bad)

    if 'appstruct' in request.session:
        # we do have valid info from the form in the session (good)
        appstruct = request.session['appstruct']
        from pyramid_mailer.message import Message
        try:
            mailer = get_mailer(request)
        except:
            return HTTPFound(location=request.route_url('join'))

        the_mail_body = customization.address_confirmation_mail.get(
            appstruct['person']['locale'], 'en')
        the_mail = Message(subject=request.localizer.translate(
            _('check-email-paragraph-check-email-subject',
              default=u'C3S: confirm your email address and load your PDF')),
                           sender="*****@*****.**",
                           recipients=[appstruct['person']['email']],
                           body=the_mail_body.format(
                               appstruct['person']['firstname'],
                               appstruct['person']['lastname'],
                               request.registry.settings['c3smembership.url'],
                               appstruct['person']['email'],
                               appstruct['email_confirm_code']))
        if 'true' in request.registry.settings['testing.mail_to_console']:
            # print(the_mail.body)
            log.info(the_mail.subject)
            log.info(the_mail.recipients)
            log.info(the_mail.body)
            # just logging, not printing, b/c test fails otherwise:
            # env/bin/nosetests
            #    c3smembership/tests/test_views_webdriver.py:
            #    JoinFormTests.test_form_submission_de
        else:
            mailer.send(the_mail)

        # make the session go away
        request.session.invalidate()
        return {
            'firstname': appstruct['person']['firstname'],
            'lastname': appstruct['person']['lastname'],
        }
    # 'else': send user to the form
    return HTTPFound(location=request.route_url('join'))
Exemple #23
0
 def membership_loss_type_entity_type_validator(form, value):
     """
     Validates that only natural persons can have loss type 'death' and
     only legal entites 'winding-up'.
     """
     if (value['membership_meta']['membership_loss_type'] == 'death'
             and value['membership_info']['entity_type'] != 'person'):
         exc_type = colander.Invalid(
             form['membership_meta']['membership_loss_type'],
             _(u'The membership loss type \'death\' is only allowed for '
               u'natural person members and not for legal entity members.'))
         exc_meta = colander.Invalid(form['membership_meta'])
         exc_meta.add(
             exc_type,
             get_child_position(
                 form['membership_meta'],
                 form['membership_meta']['membership_loss_type']))
         exc = colander.Invalid(form)
         exc.add(exc_meta, get_child_position(form,
                                              form['membership_meta']))
         raise exc
     if (value['membership_meta']['membership_loss_type'] == 'winding-up'
             and value['membership_info']['entity_type'] != 'legalentity'):
         exc_type = colander.Invalid(
             form['membership_meta']['membership_loss_type'],
             _(u'The membership loss type \'winding-up\' is only allowed '
               u'for legal entity members and not for natural person '
               u'members.'))
         exc_meta = colander.Invalid(form['membership_meta'])
         exc_meta.add(
             exc_type,
             get_child_position(
                 form['membership_meta'],
                 form['membership_meta']['membership_loss_type']))
         exc = colander.Invalid(form)
         exc.add(exc_meta, get_child_position(form,
                                              form['membership_meta']))
         raise exc
Exemple #24
0
    class Shares(colander.Schema):
        """
        the number of shares a member wants to hold

        this involves a slider widget: added to deforms widgets.
        see README.Slider.rst
        """
        num_shares = colander.SchemaNode(
            colander.Integer(),
            title=_(u"I want to buy the following number "
                    u"of Shares (50 € each, up to 3000 €, see "
                    u"C3S statute sec. 5)"),
            description=_(
                u'You can choose any amount of shares between 1 and 60.'),
            default="1",
            widget=TextInputSliderWidget(size=3, css_class='num_shares_input'),
            validator=colander.Range(
                min=1,
                max=60,
                min_err=_(u'You need at least one share of 50 €.'),
                max_err=_(u'You may choose 60 shares at most (3000 €).'),
            ),
            oid="num_shares")
class MembershipListingYearEnd(colander.Schema):
    """
    Provides a colander schema for selecting a year.
    """

    date = colander.SchemaNode(
        colander.Date(),
        title=_('Year'),
        widget=deform.widget.SelectWidget(
            values=[
                (datetime.date(year, 12, 31), str(year)) \
                for year in reversed(range(2013, datetime.date.today().year))
            ]
        )
    )
Exemple #26
0
    def loss_date_resignation_validator(form, value):
        """
        Validates that the membership loss date for resignations is the 31st
        of December of any year.

        Resignations are only allowed to the end of the year.
        """
        if (value.get('membership_loss_type', '') == 'resignation'
                and value['membership_loss_date'] is not None
                and not (value['membership_loss_date'].day == 31
                         and value['membership_loss_date'].month == 12)):
            exc = colander.Invalid(form)
            exc['membership_loss_date'] = \
                _(u'Resignations are only allowed to the 31st of December '
                  u'of a year.')
            raise exc
Exemple #27
0
    def loss_date_larger_acceptance_validator(form, value):
        """
        Validates that the membership loss date is not smaller than the
        membership acceptance date.

        As the membership can't be lost before it was granted the membership
        loss date must be larger than the membership acceptance date.
        """
        if (value['membership_loss_date'] is not None
                and (value['membership_loss_date'] < value['membership_date']
                     or not value['membership_accepted'])):
            exc = colander.Invalid(form)
            exc['membership_loss_date'] = \
                _(u'Date membership loss must be larger than membership '
                  u'acceptance date.')
            raise exc
Exemple #28
0
    def loss_date_resignation_validator(form, value):
        """
        Validates that the membership loss date for resignations is the 31st
        of December of any year.

        Resignations are only allowed to the end of the year.
        """
        if (value.get('membership_loss_type', '') == 'resignation' and
            value['membership_loss_date'] is not None and
            not (value['membership_loss_date'].day == 31 and
                 value['membership_loss_date'].month == 12)):
            exc = colander.Invalid(form)
            exc['membership_loss_date'] = \
                _(u'Resignations are only allowed to the 31st of December '
                  u'of a year.')
            raise exc
Exemple #29
0
    def loss_date_larger_acceptance_validator(form, value):
        """
        Validates that the membership loss date is not smaller than the
        membership acceptance date.

        As the membership can't be lost before it was granted the membership
        loss date must be larger than the membership acceptance date.
        """
        if (value['membership_loss_date'] is not None and
            (value['membership_loss_date'] < value['membership_date'] or
             not value['membership_accepted'])):
            exc = colander.Invalid(form)
            exc['membership_loss_date'] = \
                _(u'Date membership loss must be larger than membership '
                  u'acceptance date.')
            raise exc
Exemple #30
0
 class MembershipInfo(colander.Schema):
     """
     Basic member information.
     """
     yes_no = ((u'yes', _(u'Yes')), (u'no', _(u'No')))
     if len(customization.membership_types) > 1:
         membership_type = colander.SchemaNode(
             colander.String(),
             title=_(
                 u'I want to become a ... '
                 u'(choose membership type, see C3S SCE statute sec. 4)'),
             description=_(u'choose the type of membership.'),
             widget=deform.widget.RadioChoiceWidget(
                 values=((i['name'], i['description'])
                         for i in customization.membership_types), ),
             oid='membership_type')
     if customization.enable_colsoc_association:
         member_of_colsoc = colander.SchemaNode(
             colander.String(),
             title=_(u'Currently, I am a member of (at least) one other '
                     u'collecting society.'),
             validator=colander.OneOf([x[0] for x in yes_no]),
             widget=deform.widget.RadioChoiceWidget(values=yes_no),
             oid="other_colsoc",
             # validator=colsoc_validator
         )
         name_of_colsoc = colander.SchemaNode(
             colander.String(),
             title=_(u'If so, which one(s)? Please separate multiple '
                     u'collecting societies by comma.'),
             description=_(
                 u'Please tell us which collecting societies '
                 u'you are a member of. '
                 u'If more than one, please separate them by comma.'),
             missing=unicode(''),
             oid="colsoc_name",
         )
Exemple #31
0
def edit_member(request):
    """
    Let staff edit a member entry.
    """
    try:
        _id = request.matchdict['_id']
        assert(isinstance(int(_id), int))
        member = C3sMember.get_by_id(_id)
        if isinstance(member, NoneType):
            return HTTPFound(request.route_url('dashboard'))
    except:
        return HTTPFound(request.route_url('dashboard'))

    # if we have a valid id, we can load a members data from the db
    # and put the data in an appstruct to fill the form
    appstruct = {}
    email_is_confirmed = 'yes' if member.email_is_confirmed else 'no'
    appstruct['person'] = {
        'firstname': member.firstname,
        'lastname': member.lastname,
        'email': member.email,
        'email_is_confirmed': email_is_confirmed,
        'address1': member.address1,
        'address2': member.address2,
        'postcode': member.postcode,
        'city': member.city,
        'country': member.country,
        'date_of_birth': member.date_of_birth,
        'locale': member.locale,
    }

    appstruct['membership_meta'] = {
        'membership_accepted': member.membership_accepted,
        'membership_date': (
            # this is necessary because membership_date's default is
            # 1970-01-01 which should be changed to None in the future
            u''
            if member.membership_date == date(1970, 1, 1)
            else member.membership_date),
        'is_duplicate': member.is_duplicate,
        'is_duplicate_of': (
            u''
            if member.is_duplicate_of is None
            else member.is_duplicate_of),
        'accountant_comment': (
            u''
            if member.accountant_comment is None
            else member.accountant_comment),
        'signature_received': member.signature_received,
        'signature_received_date': member.signature_received_date,
        'payment_received': member.payment_received,
        'payment_received_date': member.payment_received_date,
        'membership_loss_date': member.membership_loss_date,
        'membership_loss_type': (
            u''
            if member.membership_loss_type is None
            else member.membership_loss_type),
    }
    appstruct['membership_info'] = {
        'membership_type': member.membership_type,
        'entity_type': u'legalentity' if member.is_legalentity else 'person',
        'member_of_colsoc': 'yes' if member.member_of_colsoc else 'no',
        'name_of_colsoc': member.name_of_colsoc,
    }
    appstruct['shares'] = {
        'num_shares': member.num_shares
    }
    membership_loss_types = (
        ('', _(u'(Select)')),
        ('resignation', _(u'Resignation')),
        ('expulsion', _(u'Expulsion')),
        ('death', _(u'Death')),
        ('bankruptcy', _(u'Bankruptcy')),
        ('winding-up', _(u'Winding-up')),
        ('shares_transfer', _(u'Transfer of remaining shares'))
    )

    class PersonalData(colander.MappingSchema):
        """
        Colander schema of the personal data for editing member data.
        """
        firstname = colander.SchemaNode(
            colander.String(),
            title=_(u'(Real) First Name'),
            oid='firstname',
        )
        lastname = colander.SchemaNode(
            colander.String(),
            title=_(u'(Real) Last Name'),
            oid='lastname',
        )
        email = colander.SchemaNode(
            colander.String(),
            title=_(u'Email Address'),
            validator=colander.Email(),
            oid='email',
        )
        email_is_confirmed = colander.SchemaNode(
            colander.String(),
            title=_(u'Email Address Confirmed'),
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'yes', _(u'Yes, confirmed')),
                    (u'no', _(u'No, not confirmed')),)),
            missing=u'',
            oid='email_is_confirmed',
        )

        passwort = colander.SchemaNode(
            colander.String(),
            widget=deform.widget.HiddenWidget(),
            default='NoneSet',
            missing='NoneSetPurposefully'
        )
        address1 = colander.SchemaNode(
            colander.String(),
            title=_(u'Addess Line 1'),
        )
        address2 = colander.SchemaNode(
            colander.String(),
            missing=u'',
            title=_(u'Address Line 2'),
        )
        postcode = colander.SchemaNode(
            colander.String(),
            title=_(u'Postal Code'),
            oid='postcode'
        )
        city = colander.SchemaNode(
            colander.String(),
            title=_(u'City'),
            oid='city',
        )
        country = colander.SchemaNode(
            colander.String(),
            title=_(u'Country'),
            default=COUNTRY_DEFAULT,
            widget=deform.widget.SelectWidget(
                values=country_codes),
            oid='country',
        )
        date_of_birth = colander.SchemaNode(
            colander.Date(),
            title=_(u'Date of Birth'),
            default=date(2013, 1, 1),
            validator=Range(
                min=date(1913, 1, 1),
                max=date(2000, 1, 1),
                min_err=_(u'${val} is earlier than earliest date ${min}.'),
                max_err=_(u'${val} is later than latest date ${max}.')
            ),
            oid='date_of_birth',
        )
        locale = colander.SchemaNode(
            colander.String(),
            title=_(u'Locale'),
            widget=deform.widget.SelectWidget(
                values=locale_codes),
            missing=u'',
        )

    @colander.deferred
    def membership_loss_date_widget(node, keywords):
        """
        Returns a text or hidden input depending on the value of
        membership_accepted within the keywords.
        """
        if keywords.get('membership_accepted'):
            return deform.widget.TextInputWidget()
        else:
            return deform.widget.HiddenWidget()

    @colander.deferred
    def membership_loss_type_widget(node, keywords):
        """
        Returns a select or hidden input depending on the value of
        membership_accepted within the keywords.
        """
        if keywords.get('membership_accepted'):
            return deform.widget.SelectWidget(values=membership_loss_types)
        else:
            return deform.widget.HiddenWidget()

    class MembershipMeta(colander.Schema):
        """
        Colander schema of the meta data for editing member data.
        """
        membership_accepted = colander.SchemaNode(
            colander.Boolean(),
            title=_(u'Membership Accepted')
        )
        membership_date = colander.SchemaNode(
            colander.Date(),
            title=_(u'Membership Acceptance Date'),
            validator=Range(
                min=date(2013, 9, 24),
                max=date.today(),
                min_err=_(u'${val} is earlier than earliest date ${min}.'),
                max_err=_(u'${val} is later than latest date ${max}.')
            ),
            missing=date(1970, 1, 1),
            oid='membership_date',
        )
        is_duplicate = colander.SchemaNode(
            colander.Boolean(),
            title=_(u'Is Duplicate'),
            oid='is_duplicate',
        )
        is_duplicate_of = colander.SchemaNode(
            colander.String(),
            title=_(u'Duplicate Id'),
            missing=u'',
            oid='duplicate_of',
        )
        signature_received = colander.SchemaNode(
            colander.Boolean(),
            title=_(u'Signature Received'),
            oid='signature_received',
        )
        signature_received_date = colander.SchemaNode(
            colander.Date(),
            title=_('Signature Receipt Date'),
            validator=Range(
                min=date(1070, 1, 1),
                max=date.today(),
                min_err=_(u'${val} is earlier than earliest date ${min}.'),
                max_err=_(u'${val} is later than latest date ${max}.')
            ),
            missing=date(1970, 1, 1),
        )
        payment_received = colander.SchemaNode(
            colander.Boolean(),
            title=_(u'Payment Received'),
        )
        payment_received_date = colander.SchemaNode(
            colander.Date(),
            title=_(u'Payment Receipt Date'),
            validator=Range(
                min=date(1970, 1, 1),
                max=date.today(),
                min_err=_(u'${val} is earlier than earliest date ${min}.'),
                max_err=_(u'${val} is later than latest date ${max}.')
            ),
            missing=date(1970, 1, 1),
            oid='_received_date',
        )
        membership_loss_date = colander.SchemaNode(
            colander.Date(),
            widget=membership_loss_date_widget,
            title=_(u'Date of the loss of membership'),
            default=None,
            missing=None,
            oid='membership_loss_date',
        )
        membership_loss_type = colander.SchemaNode(
            colander.String(),
            widget=membership_loss_type_widget,
            title=_(u'Type of membership loss'),
            default=None,
            missing=None,
            oid='membership_loss_type',
        )
        accountant_comment = colander.SchemaNode(
            colander.String(),
            title=_(u'Staff Comment: (255 letters)'),
            missing=u'',
            oid='accountant_comment',
        )

    class MembershipInfo(colander.Schema):
        """
        Colander schema of the additional data for editing member data.
        """
        yes_no = (
            (u'yes', _(u'Yes')),
            (u'no', _(u'No')),
            (u'dontknow', _(u'Unknwon')),
        )

        entity_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Member Category'),
            description=_(u'Please choose the member category.'),
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'person', _(u'Person')),
                    (u'legalentity', _(u'Legal Entity')),
                ),
            ),
            missing=u'',
            oid='entity_type',
        )
        membership_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Type of Membership (C3S Statute § 4)'),
            description=_(u'Please choose the type of membership.'),
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'normal', _(u'Member')),
                    (u'investing', _(u'Investing (non-user) member')),
                    (u'unknown', _(u'Unknown')),
                ),
            ),
            missing=u'',
            oid='membership_type',
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_('Member of a Collecting Society'),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            oid='other_colsoc',
            default=u'',
            missing=u'',
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(u'Names of Collecting Societies'),
            description=_(u'Please separate multiple collecting societies by '
                          u'comma.'),
            missing=u'',
            oid='colsoc_name',
        )

    class Shares(colander.Schema):
        """
        the number of shares a member wants to hold
        """
        num_shares = colander.SchemaNode(
            colander.Integer(),
            title=_('Number of Shares (1-60)'),
            default='1',
            validator=colander.Range(
                min=1,
                max=60,
                min_err=_(u'At least one share must be acquired.'),
                max_err=_(u'At most 60 shares can be acquired.'),
            ),
            oid='num_shares')

    def loss_type_and_date_set_validator(form, value):
        """
        Validates whether the membership loss type is set.

        Membership date and type must both be either set or unset.
        """
        if (value['membership_loss_date'] is None) != \
                (value['membership_loss_type'] is None):
            exc = colander.Invalid(form)
            exc['membership_loss_type'] = \
                _(u'Date and type of membership loss must be set both or '
                  u'none.')
            exc['membership_loss_date'] = \
                _(u'Date and type of membership loss must be set both or '
                  u'none.')
            raise exc

    def loss_date_larger_acceptance_validator(form, value):
        """
        Validates that the membership loss date is not smaller than the
        membership acceptance date.

        As the membership can't be lost before it was granted the membership
        loss date must be larger than the membership acceptance date.
        """
        if (value['membership_loss_date'] is not None and
            (value['membership_loss_date'] < value['membership_date'] or
             not value['membership_accepted'])):
            exc = colander.Invalid(form)
            exc['membership_loss_date'] = \
                _(u'Date membership loss must be larger than membership '
                  u'acceptance date.')
            raise exc

    def loss_date_resignation_validator(form, value):
        """
        Validates that the membership loss date for resignations is the 31st
        of December of any year.

        Resignations are only allowed to the end of the year.
        """
        if (value.get('membership_loss_type', '') == 'resignation' and
            value['membership_loss_date'] is not None and
            not (value['membership_loss_date'].day == 31 and
                 value['membership_loss_date'].month == 12)):
            exc = colander.Invalid(form)
            exc['membership_loss_date'] = \
                _(u'Resignations are only allowed to the 31st of December '
                  u'of a year.')
            raise exc

    class MembershipForm(colander.Schema):
        """
        The form for editing membership information combining all forms for
        the subject areas.
        """
        person = PersonalData(
            title=_(u'Personal Data'),
        )
        membership_meta = MembershipMeta(
            title=_(u'Membership Bureaucracy'),
            validator=colander.All(
                loss_type_and_date_set_validator,
                loss_date_larger_acceptance_validator,
                loss_date_resignation_validator)
        ).bind(
            membership_accepted=member.membership_accepted,
        )
        membership_info = MembershipInfo(
            title=_(u'Membership Requirements')
        )
        shares = Shares(
            title=_(u'Shares')
        )

    def membership_loss_type_entity_type_validator(form, value):
        """
        Validates that only natural persons can have loss type 'death' and
        only legal entites 'winding-up'.
        """
        if (
                value['membership_meta']['membership_loss_type'] == 'death' and
                value['membership_info']['entity_type'] != 'person'):
            exc_type = colander.Invalid(
                form['membership_meta']['membership_loss_type'],
                _(u'The membership loss type \'death\' is only allowed for '
                  u'natural person members and not for legal entity members.'))
            exc_meta = colander.Invalid(form['membership_meta'])
            exc_meta.add(
                exc_type,
                get_child_position(
                    form['membership_meta'],
                    form['membership_meta']['membership_loss_type']))
            exc = colander.Invalid(form)
            exc.add(
                exc_meta,
                get_child_position(
                    form,
                    form['membership_meta']))
            raise exc
        if (
                value['membership_meta'][
                    'membership_loss_type'] == 'winding-up' and
                value['membership_info']['entity_type'] != 'legalentity'):
            exc_type = colander.Invalid(
                form['membership_meta']['membership_loss_type'],
                _(u'The membership loss type \'winding-up\' is only allowed '
                  u'for legal entity members and not for natural person '
                  u'members.'))
            exc_meta = colander.Invalid(form['membership_meta'])
            exc_meta.add(
                exc_type,
                get_child_position(
                    form['membership_meta'],
                    form['membership_meta']['membership_loss_type']))
            exc = colander.Invalid(form)
            exc.add(
                exc_meta,
                get_child_position(
                    form,
                    form['membership_meta']))
            raise exc

    schema = MembershipForm(
        validator=colander.All(
            membership_loss_type_entity_type_validator,
        ))
    form = deform.Form(
        schema,
        buttons=[
            deform.Button('submit', _(u'Submit')),
            deform.Button('reset', _(u'Reset')),
        ],
        renderer=ZPT_RENDERER,
        use_ajax=True,
    )

    def clean_error_messages(error):
        if error.msg is not None and type(error.msg) == list:
            error.msg = list(set(error.msg))
            if None in error.msg:
                error.msg.remove(None)
            if '' in error.msg:
                error.msg.remove('')
            error.msg = ' '.join(list(set(error.msg)))
        for child in error.children:
            clean_error_messages(child)

    # if the form has NOT been used and submitted, remove error messages if
    # any
    if 'submit' not in request.POST:
        request.session.pop_flash()

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)
        except ValidationFailure as validationfailure:
            clean_error_messages(validationfailure.error)
            request.session.flash(
                _(u'Please note: There were errors, '
                  u'please check the form below.'),
                'message_above_form',
                allow_duplicate=False)
            return{'form': validationfailure.render()}

        # to store the data in the DB, the old object is updated
        listing = [  # map data attributes to appstruct items
            ('firstname', appstruct['person']['firstname']),
            ('lastname', appstruct['person']['lastname']),
            ('date_of_birth', appstruct['person']['date_of_birth']),
            ('email', appstruct['person']['email']),
            (
                'email_is_confirmed',
                1 if appstruct['person']['email_is_confirmed'] == 'yes'
                else 0
            ),
            ('address1', appstruct['person']['address1']),
            ('address2', appstruct['person']['address2']),
            ('postcode', appstruct['person']['postcode']),
            ('city', appstruct['person']['city']),
            ('country', appstruct['person']['country']),
            ('locale', appstruct['person']['locale']),
            (
                'membership_date',
                appstruct['membership_meta']['membership_date']
            ),
            ('is_duplicate', appstruct['membership_meta']['is_duplicate']),
            (
                'is_duplicate_of',
                appstruct['membership_meta']['is_duplicate_of']
            ),
            (
                'accountant_comment',
                appstruct['membership_meta']['accountant_comment']
            ),
            (
                'membership_type',
                appstruct['membership_info']['membership_type']
            ),
            (
                'is_legalentity',
                1 if (appstruct['membership_info']['entity_type'] ==
                      'legalentity')
                else 0
            ),
            (
                'name_of_colsoc',
                appstruct['membership_info']['name_of_colsoc']
            ),
            ('num_shares', appstruct['shares']['num_shares']),
            (
                'signature_received',
                appstruct['membership_meta']['signature_received']
            ),
            (
                'signature_received_date',
                appstruct['membership_meta']['signature_received_date']
            ),
            (
                'payment_received',
                appstruct['membership_meta']['payment_received']
            ),
            (
                'payment_received_date',
                appstruct['membership_meta']['payment_received_date']
            ),
            (
                'membership_loss_type',
                appstruct['membership_meta'].get('membership_loss_type', None)
            ),
            (
                'membership_loss_date',
                appstruct['membership_meta'].get('membership_loss_date', None)
            ),
        ]

        for thing in listing:
            attribute_name = thing[0]
            attribute_value = thing[1]

            if member.__getattribute__(attribute_name) == attribute_value:
                pass
            else:
                LOG.info(
                    u'{0} changes {1} of id {2} to {3}'.format(
                        authenticated_userid(request),
                        attribute_name,
                        member.id,
                        attribute_value
                    )
                )
                setattr(member, attribute_name, attribute_value)

        # membership acceptance status can be set or unset.
        if appstruct['membership_meta'][
                'membership_accepted'] == member.membership_accepted:
            pass
        else:
            member.membership_accepted = appstruct[
                'membership_meta']['membership_accepted']
            if isinstance(member.membership_number, NoneType) \
                    and member.membership_accepted:
                member.membership_number = \
                    C3sMember.get_next_free_membership_number()

        if appstruct['membership_info']['entity_type'] == 'legalentity':
            member.is_legalentity = True
        else:
            member.is_legalentity = False

        # empty the messages queue (as validation worked anyways)
        deleted_msg = request.session.pop_flash()
        del deleted_msg
        return HTTPFound(  # redirect to details page
            location=request.route_url(
                'detail',
                memberid=member.id),
        )

    form.set_appstruct(appstruct)
    html = form.render()

    return {'form': html}
def new_member(request):
    '''
    Let staff create a new member entry, when receiving input via dead wood
    '''

    class PersonalData(colander.MappingSchema):
        """
        Schema for personal data
        """
        firstname = colander.SchemaNode(
            colander.String(),
            title=u'Vorname (b. Körpersch.: Ansprechpartner)',
            oid="firstname",
        )
        lastname = colander.SchemaNode(
            colander.String(),
            title=u'Nachname (b. Körpersch.: Name der Körperschaft)',
            oid="lastname",
        )
        email = colander.SchemaNode(
            colander.String(),
            title=_(u'E-Mail'),
            validator=colander.Email(),
            oid="email",
        )
        passwort = colander.SchemaNode(
            colander.String(),
            widget=deform.widget.HiddenWidget(),
            default='NoneSet',
            missing='NoneSetPurposefully'
        )
        address1 = colander.SchemaNode(
            colander.String(),
            title='Adresse Zeile 1'
        )
        address2 = colander.SchemaNode(
            colander.String(),
            missing=unicode(''),
            title='Adresse Zeile 2'
        )
        postcode = colander.SchemaNode(
            colander.String(),
            title='Postleitzahl',
            oid="postcode"
        )
        city = colander.SchemaNode(
            colander.String(),
            title='Ort',
            oid="city",
        )
        country = colander.SchemaNode(
            colander.String(),
            title='Land',
            default=COUNTRY_DEFAULT,
            widget=deform.widget.SelectWidget(
                values=country_codes),
            oid="country",
        )
        date_of_birth = colander.SchemaNode(
            colander.Date(),
            title='Geburtsdatum',
            default=date(1970, 1, 1),
            oid="date_of_birth",
        )
        locale = colander.SchemaNode(
            colander.String(),
            widget=deform.widget.HiddenWidget(),
            default='de',
            missing='de',
        )

    class MembershipInfo(colander.Schema):
        """
        Schema for membership specific information
        """

        yes_no = ((u'yes', _(u'Yes')),
                  (u'no', _(u'No')),
                  (u'dontknow', _(u'Unknown')),)

        entity_type = colander.SchemaNode(
            colander.String(),
            title=(u'Person oder Körperschaft?'),
            description=u'Bitte die Kategorie des Mitglied auswählen.',
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'person',
                     (u'Person')),
                    (u'legalentity',
                     u'Körperschaft'),
                ),
            ),
            missing=unicode(''),
            oid='entity_type',
        )
        membership_type = colander.SchemaNode(
            colander.String(),
            title=(u'Art der Mitgliedschaft (lt. Satzung, §4)'),
            description=u'Bitte die Art der Mitgliedschaft auswählen.',
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'normal',
                     (u'Normales Mitglied')),
                    (u'investing',
                     u'Investierendes Mitglied'),
                    (u'unknown',
                     u'Unbekannt.'),
                ),
            ),
            missing=unicode(''),
            oid='membership_type',
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title='Mitglied einer Verwertungsgesellschaft?',
            validator=colander.OneOf([x[0] for x in yes_no]),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            missing=unicode(''),
            oid="other_colsoc",
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=(u'Falls ja, welche? (Kommasepariert)'),
            missing=unicode(''),
            oid="colsoc_name",
        )

    class Shares(colander.Schema):
        """
        the number of shares a member wants to hold
        """
        num_shares = colander.SchemaNode(
            colander.Integer(),
            title='Anzahl Anteile (1-60)',
            default="1",
            validator=colander.Range(
                min=1,
                max=60,
                min_err=u'mindestens 1',
                max_err=u'höchstens 60',
            ),
            oid="num_shares")

    class MembershipForm(colander.Schema):
        """
        The Form consists of
        - Personal Data
        - Membership Information
        - Shares
        """
        person = PersonalData(
            title=_(u"Personal Data"),
        )
        membership_info = MembershipInfo(
            title=_(u"Membership Requirements")
        )
        shares = Shares(
            title=_(u"Shares")
        )

    schema = MembershipForm()

    form = deform.Form(
        schema,
        buttons=[
            deform.Button('submit', _(u'Submit')),
            deform.Button('reset', _(u'Reset'))
        ],
        use_ajax=True,
        renderer=ZPT_RENDERER,
    )

    # if the form has NOT been used and submitted, remove error messages if any
    if 'submit' not in request.POST:
        request.session.pop_flash()

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

        except ValidationFailure as exception:
            request.session.flash(
                _(u"Please note: There were errors, "
                  "please check the form below."),
                'danger',
                allow_duplicate=False)
            return{'form': exception.render()}

        def make_random_string():
            """
            used as email confirmation code
            """
            return u''.join(
                random.choice(
                    string.ascii_uppercase + string.digits
                ) for x in range(10))

        randomstring = make_random_string()
        while C3sMember.check_for_existing_confirm_code(randomstring):
            randomstring = make_random_string()

        # to store the data in the DB, an objet is created
        member = C3sMember(
            firstname=appstruct['person']['firstname'],
            lastname=appstruct['person']['lastname'],
            email=appstruct['person']['email'],
            password='******',
            address1=appstruct['person']['address1'],
            address2=appstruct['person']['address2'],
            postcode=appstruct['person']['postcode'],
            city=appstruct['person']['city'],
            country=appstruct['person']['country'],
            locale=appstruct['person']['locale'],
            date_of_birth=appstruct['person']['date_of_birth'],
            email_is_confirmed=False,
            email_confirm_code=randomstring,
            date_of_submission=datetime.now(),
            membership_type=appstruct['membership_info']['membership_type'],
            member_of_colsoc=(
                appstruct['membership_info']['member_of_colsoc'] == u'yes'),
            name_of_colsoc=appstruct['membership_info']['name_of_colsoc'],
            num_shares=appstruct['shares']['num_shares'],
        )
        if 'legalentity' in appstruct['membership_info']['entity_type']:
            member.membership_type = u'investing'
            member.is_legalentity = True

        dbsession = DBSession()

        try:
            _temp = request.url.split('?')[1].split('=')
            if 'id' in _temp[0]:
                _id = _temp[1]

                # add a member with a DB id that had seen its entry deleted
                # before
                _mem = C3sMember.get_by_id(_id)
                if isinstance(_mem, NoneType):
                    member.id = _id
        except:
            pass

        # add member at next free DB id (default if member.id not set)
        try:
            dbsession.add(member)
            dbsession.flush()
            the_new_id = member.id
        except InvalidRequestError as exception:
            print("InvalidRequestError! %s") % exception
        except IntegrityError as exception:
            print("IntegrityError! %s") % exception

        # redirect to success page, then return the PDF
        # first, store appstruct in session
        request.session['appstruct'] = appstruct
        request.session[
            'appstruct']['locale'] = appstruct['person']['locale']

        # empty the messages queue (as validation worked anyways)
        request.session.pop_flash()
        return HTTPFound(  # redirect to success page
            location=request.route_url(
                'detail',
                member_id=the_new_id),
        )

    # if the form was submitted and gathered info shown on the success page,
    # BUT the user wants to correct their information:
    else:
        # remove annoying message from other session
        request.session.pop_flash()
        if 'appstruct' in request.session:
            appstruct = request.session['appstruct']
            # pre-fill the form with the values from last time
            form.set_appstruct(appstruct)

    html = form.render()

    return {'form': html}
Exemple #33
0
def join_c3s(request):
    """
    This is the main membership application form view: Join C3S as member
    """
    # if another language was chosen by clicking on a flag
    # the add_locale_to_cookie subscriber has planted an attr on the request
    if hasattr(request, '_REDIRECT_'):

        _query = request._REDIRECT_
        # set language cookie
        request.response.set_cookie('_LOCALE_', _query)
        request._LOCALE_ = _query
        locale_name = _query
        return HTTPFound(location=request.route_url('join'),
                         headers=request.response.headers)
    else:
        locale_name = get_locale_name(request)

    if DEBUG:
        print "-- locale_name: " + str(locale_name)

    from c3smembership.utils import country_codes
    # set default of Country select widget according to locale
    locale_country_mapping = {
        'de': 'DE',
        'en': 'GB',
    }
    country_default = locale_country_mapping.get(locale_name)
    if DEBUG:
        print("== locale is :" + str(locale_name))
        print("== choosing :" + str(country_default))

    class PersonalData(colander.MappingSchema):
        """
        colander schema for membership application form
        """
        firstname = colander.SchemaNode(
            colander.String(),
            title=_(u"(Real) First Name"),
            oid="firstname",
        )
        lastname = colander.SchemaNode(
            colander.String(),
            title=_(u"(Real) Last Name"),
            oid="lastname",
        )
        email = colander.SchemaNode(
            colander.String(),
            title=_(u'Email Address'),
            validator=colander.Email(),
            oid="email",
        )
        password = colander.SchemaNode(
            colander.String(),
            validator=colander.Length(min=5, max=100),
            widget=deform.widget.CheckedPasswordWidget(size=20),
            title=_(u'Password (to protect access to your data)'),
            description=_(u'We need a password to protect your data. After '
                          u'verifying your email you will have to enter it.'),
            oid='password',
        )
        address1 = colander.SchemaNode(
            colander.String(),
            title=_(u'Address Line 1')
        )
        address2 = colander.SchemaNode(
            colander.String(),
            missing=unicode(''),
            title=_(u'Address Line 2')
        )
        postcode = colander.SchemaNode(
            colander.String(),
            title=_(u'Postal Code'),
            oid="postcode"
        )
        city = colander.SchemaNode(
            colander.String(),
            title=_(u'City'),
            oid="city",
        )
        country = colander.SchemaNode(
            colander.String(),
            title=_(u'Country'),
            default=country_default,
            widget=deform.widget.SelectWidget(
                values=country_codes),
            oid="country",
        )
        date_of_birth = colander.SchemaNode(
            colander.Date(),
            title=_(u'Date of Birth'),
            # css_class="hasDatePicker",
            widget=deform.widget.DatePartsWidget(),
            default=date(2013, 1, 1),
            validator=Range(
                min=date(1913, 1, 1),
                max=date(2000, 1, 1),
                min_err=_(u'${val} is earlier than earliest date ${min}'),
                max_err=_(u'${val} is later than latest date ${max}')
            ),
            oid="date_of_birth",
        )
        _LOCALE_ = colander.SchemaNode(colander.String(),
                                       widget=deform.widget.HiddenWidget(),
                                       default=locale_name)

    class MembershipInfo(colander.Schema):
        """
        Basic member information.
        """
        yes_no = ((u'yes', _(u'Yes')),
                  (u'no', _(u'No')))
        membership_type = colander.SchemaNode(
            colander.String(),
            title=_(u'I want to become a ... '
                    u'(choose membership type, see C3S SCE statute sec. 4)'),
            description=_(u'choose the type of membership.'),
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (
                        u'normal',
                        _(u'FULL member. Full members have to be natural '
                          u'persons who register at least three works they '
                          u'created themselves with C3S. This applies to '
                          u'composers, lyricists and remixers. They get a '
                          u'vote.')),
                    (
                        u'investing',
                        _(u'INVESTING member. Investing members can be '
                          u'natural or legal entities or private companies '
                          u'that do not register works with C3S. They do '
                          u'not get a vote, but may counsel.'))
                ),
            )
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(
                u'Currently, I am a member of (at least) one other '
                u'collecting society.'),
            validator=colander.OneOf([x[0] for x in yes_no]),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            oid="other_colsoc",
            # validator=colsoc_validator
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(u'If so, which one(s)? Please separate multiple '
                    u'collecting societies by comma.'),
            description=_(
                u'Please tell us which collecting societies '
                u'you are a member of. '
                u'If more than one, please separate them by comma.'),
            missing=unicode(''),
            oid="colsoc_name",
            # validator=colander.All(
            #    colsoc_validator,
            # )
        )

        def statute_validator(node, value):
            """
            Validator for statute confirmation.
            """
            if not value:
                # raise without additional error message as the description
                # already explains the necessity of the checkbox
                raise Invalid(node, u'')

        got_statute = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            title=_(
                u'An electronic copy of the statute of the '
                u'C3S SCE has been made available to me (see link below).'),
            description=_(
                u'You must confirm to have access to the statute.'),
            widget=deform.widget.CheckboxWidget(),
            validator=statute_validator,
            required=True,
            label=_('Yes'),
        )

        def dues_regulations_validator(node, value):
            """
            Validator for dues regulations confirmation.
            """
            if not value:
                # raise without additional error message as the description
                # already explains the necessity of the checkbox
                raise Invalid(node, u'')

        got_dues_regulations = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            title=_(
                u'An electronic copy of the temporary membership dues '
                u'regulations of the C3S SCE has been made available to me '
                u'(see link below).'),
            description=_(
                u'You must confirm to have access to the temporary '
                u'membership dues regulations.'),
            widget=deform.widget.CheckboxWidget(),
            validator=dues_regulations_validator,
            required=True,
            label=_('Yes'),
        )

    class Shares(colander.Schema):
        """
        the number of shares a member wants to hold

        this involves a slider widget: added to deforms widgets.
        see README.Slider.rst
        """
        num_shares = colander.SchemaNode(
            colander.Integer(),
            title=_(u"I want to buy the following number "
                    u"of Shares (50€ each, up to 3000€, see "
                    u"C3S statute sec. 5)"),
            description=_(
                u'You can choose any amount of shares between 1 and 60.'),
            default="1",
            widget=TextInputSliderWidget(
                size=3, css_class='num_shares_input'),
            validator=colander.Range(
                min=1,
                max=60,
                min_err=_(u'You need at least one share of 50 €.'),
                max_err=_(u'You may choose 60 shares at most (3000 €).'),
            ),
            oid="num_shares")

    class MembershipForm(colander.Schema):
        """
        The Form consists of
        - Personal Data
        - Membership Information
        - Shares
        """
        person = PersonalData(
            title=_(u'Personal Data'),
        )
        membership_info = MembershipInfo(
            title=_(u'Membership Requirements')
        )
        shares = Shares(
            title=_(u'Shares')
        )

    schema = MembershipForm()

    form = deform.Form(
        schema,
        buttons=[
            deform.Button('submit', _(u'Next')),
            deform.Button('reset', _(u'Reset'))
        ],
        use_ajax=True,
        renderer=ZPT_RENDERER
    )

    # if the form has NOT been used and submitted, remove error messages if any
    if 'submit' not in request.POST:
        request.session.pop_flash()

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

            # data sanity: if not in collecting society, don't save
            #  collsoc name even if it was supplied through form
            if 'no' in appstruct['membership_info']['member_of_colsoc']:
                appstruct['membership_info']['name_of_colsoc'] = ''

        except ValidationFailure as validation_failure:
            request.session.flash(
                _(u'Please note: There were errors, '
                  u'please check the form below.'),
                'message_above_form',
                allow_duplicate=False)

            # If the validation error was not caused by the password field,
            # manually set an error to the password field because the user
            # needs to re-enter it after a validation error.
            form = validation_failure.field
            if form['person']['password'].error is None:
                form['person']['password'].error = Invalid(
                    None,
                    _(u'Please re-enter your password.'))
                validation_failure = ValidationFailure(form, None, form.error)

            return {'form': validation_failure.render()}

        def make_random_string():
            """
            used as email confirmation code
            """
            import random
            import string
            return u''.join(
                random.choice(
                    string.ascii_uppercase + string.digits
                ) for x in range(10))

        # make confirmation code and
        randomstring = make_random_string()
        # check if confirmation code is already used
        while C3sMember.check_for_existing_confirm_code(randomstring):
            # create a new one, if the new one already exists in the database
            randomstring = make_random_string()  # pragma: no cover

        # to store the data in the DB, an objet is created
        member = C3sMember(
            firstname=appstruct['person']['firstname'],
            lastname=appstruct['person']['lastname'],
            email=appstruct['person']['email'],
            password=appstruct['person']['password'],
            address1=appstruct['person']['address1'],
            address2=appstruct['person']['address2'],
            postcode=appstruct['person']['postcode'],
            city=appstruct['person']['city'],
            country=appstruct['person']['country'],
            locale=appstruct['person']['_LOCALE_'],
            date_of_birth=appstruct['person']['date_of_birth'],
            email_is_confirmed=False,
            email_confirm_code=randomstring,
            date_of_submission=datetime.now(),
            membership_type=appstruct['membership_info']['membership_type'],
            member_of_colsoc=(
                appstruct['membership_info']['member_of_colsoc'] == u'yes'),
            name_of_colsoc=appstruct['membership_info']['name_of_colsoc'],
            num_shares=appstruct['shares']['num_shares'],
        )
        dbsession = DBSession()
        try:
            dbsession.add(member)
            appstruct['email_confirm_code'] = randomstring
        except InvalidRequestError as ire:  # pragma: no cover
            print("InvalidRequestError! %s") % ire
        except IntegrityError as integrity_error:  # pragma: no cover
            print("IntegrityError! %s") % integrity_error

        # redirect to success page, then return the PDF
        # first, store appstruct in session
        request.session['appstruct'] = appstruct
        request.session['appstruct']['_LOCALE_'] = \
            appstruct['person']['_LOCALE_']
        # empty the messages queue (as validation worked anyways)
        deleted_msg = request.session.pop_flash()
        del deleted_msg
        return HTTPFound(  # redirect to success page
            location=request.route_url('success'),
        )

    # if the form was submitted and gathered info shown on the success page,
    # BUT the user wants to correct their information:
    else:
        if 'edit' in request.POST:
            print(request.POST['edit'])
        # remove annoying message from other session
        deleted_msg = request.session.pop_flash()
        del deleted_msg
        if 'appstruct' in request.session:
            appstruct = request.session['appstruct']
            # pre-fill the form with the values from last time
            form.set_appstruct(appstruct)

    html = form.render()

    return {'form': html}
Exemple #34
0
def success_verify_email(request):
    """
    This view is called via links sent in mails to verify mail addresses.
    It extracts both email and verification code from the URL.
    It will ask for a password
    and checks if there is a match in the database.

    If the password matches, and all is correct,
    the view shows a download link and further info.
    """
    # collect data from the URL/matchdict
    user_email = request.matchdict['email']
    confirm_code = request.matchdict['code']
    # if we want to ask the user for her password (through a form)
    # we need to have a url to send the form to
    post_url = '/verify/' + user_email + '/' + confirm_code

    if 'submit' in request.POST:
        # print("the form was submitted")
        request.session.pop_flash('message_above_form')
        request.session.pop_flash('message_above_login')
        # check for password ! ! !
        if 'password' in request.POST:
            _passwd = request.POST['password']

        # get matching dataset from DB
        member = C3sMember.get_by_code(confirm_code)  # returns member or None

        if isinstance(member, NoneType):
            # member not found: FAIL!
            not_found_msg = _(
                u"Not found. Check verification URL. "
                "If all seems right, please use the form again.")
            return {
                'correct': False,
                'namepart': '',
                'result_msg': not_found_msg,
            }

        # check if the password is valid
        try:
            correct = C3sMember.check_password(member.id, _passwd)
        except AttributeError:
            correct = False
            request.session.flash(
                _(u'Wrong Password!'),
                'message_above_login')

        # check if info from DB makes sense
        # -member

        if (member.email == user_email) and correct:
            # print("-- found member, code matches, password too. COOL!")
            # set the email_is_confirmed flag in the DB for this signee
            member.email_is_confirmed = True
            # dbsession.flush()
            namepart = member.firstname + member.lastname
            import re
            pdf_file_name_part = re.sub(  # replace characters
                '[^a-zA-Z0-9]',  # other than these
                '_',  # with an underscore
                namepart)

            appstruct = {
                'firstname': member.firstname,
                'lastname': member.lastname,
                'email': member.email,
                'email_confirm_code': member.email_confirm_code,
                'address1': member.address1,
                'address2': member.address2,
                'postcode': member.postcode,
                'city': member.city,
                'country': member.country,
                '_LOCALE_': member.locale,
                'date_of_birth': member.date_of_birth,
                'date_of_submission': member.date_of_submission,
                # 'activity': set(activities),
                # 'invest_member': u'yes' if member.invest_member else u'no',
                'membership_type': member.membership_type,
                'member_of_colsoc':
                    u'yes' if member.member_of_colsoc else u'no',
                'name_of_colsoc': member.name_of_colsoc,
                # 'opt_band': signee.opt_band,
                # 'opt_URL': signee.opt_URL,
                'num_shares': member.num_shares,
            }
            request.session['appstruct'] = appstruct

            # log this person in, using the session
            log.info('verified code and password for id %s', member.id)
            request.session.save()
            return {
                'firstname': member.firstname,
                'lastname': member.lastname,
                'code': member.email_confirm_code,
                'correct': True,
                'namepart': pdf_file_name_part,
                'result_msg': _("Success. Load your PDF!")
            }
    # else: code did not match OR SOMETHING...
    # just display the form
    request.session.flash(
        _(u"Please enter your password."),
        'message_above_login',
        allow_duplicate=False
    )
    return {
        'post_url': post_url,
        'firstname': '',
        'lastname': '',
        'namepart': '',
        'correct': False,
        'result_msg': "something went wrong."
    }
Exemple #35
0
def annual_report(request):  # pragma: no cover
    """
    Sift, sort, count and calculate data for the report of a given timeframe.

    XXX TODO write testcases for this
    """
    # defaults
    start_date = date(date.today().year, 1, 1)  # first day of this year
    end_date = date(date.today().year, 12, 31)  # and last
    appstruct = {
        'startdate': start_date,
        'enddate': end_date,
    }

    # construct a form
    class DatesForm(colander.Schema):
        """
        Defines the colander schema for start and end date.
        """
        startdate = colander.SchemaNode(
            colander.Date(),
            title='start date',
        )
        enddate = colander.SchemaNode(
            colander.Date(),
            title='end date',
        )

    schema = DatesForm()
    form = deform.Form(
        schema,
        buttons=[deform.Button('submit', _(u'Submit'))],
    )
    # form generation complete

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

            start = appstruct['startdate']
            end = appstruct['enddate']
            start_date = date(start.year, start.month, start.day)
            end_date = date(end.year, end.month, end.day)

        except ValidationFailure, validation_failure:  # pragma: no cover

            request.session.flash(
                _(u'Please note: There were errors, please check the form '
                  u'below.'),
                'warning',
                allow_duplicate=False)
            return {
                'form': validation_failure.render(),
                'start_date': start_date,
                'end_date': end_date,
                'datetime': datetime,
                'date': date,
                'new_members': [],
                'num_members': 0,
                'num_shares': 0,
                'sum_shares': 0,
                'new_shares': [],
                'shares_paid_unapproved_count': 0,
                'shares_paid_unapproved': [],
            }
Exemple #36
0
def success_verify_email(request):
    """
    This view is called via links sent in mails to verify mail addresses.
    It extracts both email and verification code from the URL.
    It will ask for a password
    and checks if there is a match in the database.

    If the password matches, and all is correct,
    the view shows a download link and further info.
    """
    # collect data from the URL/matchdict
    user_email = request.matchdict['email']
    confirm_code = request.matchdict['code']
    # if we want to ask the user for her password (through a form)
    # we need to have a url to send the form to
    post_url = '/verify/' + user_email + '/' + confirm_code

    # ToDo unify errors for not_found email, wrong password and wrong confirm code to avoid leaking
    error_message = _(
        u'Your email, password, or confirmation code could not be found')

    if 'submit' in request.POST:
        # print("the form was submitted")
        request.session.pop_flash('message_above_form')
        request.session.pop_flash('message_above_login')
        # check for password ! ! !
        if 'password' in request.POST:
            _passwd = request.POST['password']

        # get matching dataset from DB
        member = C3sMember.get_by_code(confirm_code)  # returns member or None

        if isinstance(member, NoneType):
            # member not found: FAIL!
            not_found_msg = _(u"Not found. Check verification URL. "
                              "If all seems right, please use the form again.")
            return {
                'correct': False,
                'namepart': '',
                'result_msg': not_found_msg,
            }

        # check if the password is valid
        try:
            correct = C3sMember.check_password(member.id, _passwd)
        except AttributeError:
            correct = False
            request.session.flash(_(u'Wrong Password!'), 'message_above_login')

        # check if info from DB makes sense
        # -member

        if (member.email == user_email) and correct:
            # print("-- found member, code matches, password too. COOL!")
            # set the email_is_confirmed flag in the DB for this signee
            member.email_is_confirmed = True
            # dbsession.flush()
            namepart = member.firstname + member.lastname
            import re
            pdf_file_name_part = re.sub(  # replace characters
                '[^a-zA-Z0-9]',  # other than these
                '_',  # with an underscore
                namepart)

            appstruct = {
                'firstname': member.firstname,
                'lastname': member.lastname,
                'email': member.email,
                'email_confirm_code': member.email_confirm_code,
                'address1': member.address1,
                'address2': member.address2,
                'postcode': member.postcode,
                'city': member.city,
                'country': member.country,
                'locale': member.locale,
                'date_of_birth': member.date_of_birth,
                'date_of_submission': member.date_of_submission,
                # 'activity': set(activities),
                # 'invest_member': u'yes' if member.invest_member else u'no',
                'membership_type': member.membership_type,
                'member_of_colsoc':
                u'yes' if member.member_of_colsoc else u'no',
                'name_of_colsoc': member.name_of_colsoc,
                # 'opt_band': signee.opt_band,
                # 'opt_URL': signee.opt_URL,
                'num_shares': member.num_shares,
            }
            request.session['appstruct'] = appstruct

            # log this person in, using the session
            log.info('verified code and password for id %s', member.id)
            request.session.save()
            return {
                'firstname': member.firstname,
                'lastname': member.lastname,
                'code': member.email_confirm_code,
                'correct': True,
                'namepart': pdf_file_name_part,
                'result_msg': _("Success. Load your PDF!")
            }
    # else: code did not match OR SOMETHING...
    # just display the form
    request.session.flash(_(u"Please enter your password."),
                          'message_above_login',
                          allow_duplicate=False)
    return {
        'post_url': post_url,
        'firstname': '',
        'lastname': '',
        'namepart': '',
        'correct': False,
        'result_msg': "something went wrong."
    }
def annual_report(request):  # pragma: no cover
    """
    Sift, sort, count and calculate data for the report of a given timeframe.

    XXX TODO write testcases for this
    """
    # defaults
    start_date = date(date.today().year, 1, 1)  # first day of this year
    end_date = date(date.today().year, 12, 31)  # and last
    appstruct = {
        'startdate': start_date,
        'enddate': end_date,
    }

    # construct a form
    class DatesForm(colander.Schema):
        """
        Defines the colander schema for start and end date.
        """
        startdate = colander.SchemaNode(
            colander.Date(),
            title='start date',
        )
        enddate = colander.SchemaNode(
            colander.Date(),
            title='end date',
        )

    schema = DatesForm()
    form = deform.Form(
        schema,
        buttons=[deform.Button('submit', _(u'Submit'))],
    )
    # form generation complete

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

            start = appstruct['startdate']
            end = appstruct['enddate']
            start_date = date(start.year, start.month, start.day)
            end_date = date(end.year, end.month, end.day)

        except ValidationFailure, validation_failure:  # pragma: no cover

            request.session.flash(_(
                u'Please note: There were errors, please check the form '
                u'below.'),
                                  'message_above_form',
                                  allow_duplicate=False)
            return {
                'form': validation_failure.render(),
                'start_date': start_date,
                'end_date': end_date,
                'datetime': datetime,
                'date': date,
                'new_members': [],
                'num_members': 0,
                'num_shares': 0,
                'sum_shares': 0,
                'new_shares': [],
                'shares_paid_unapproved_count': 0,
                'shares_paid_unapproved': [],
            }
Exemple #38
0
def shares_edit(request):
    """
    Edit details of a package of shares.
    """
    # print(request.matchdict['id'])
    from c3smembership.models import Shares

    # load info from DB -- if possible
    _s = Shares.get_by_id(request.matchdict["id"])

    if isinstance(_s, NoneType):
        # entry was not found in database
        return get_memberhip_listing_redirect(request)
    else:
        appstruct = {}
        appstruct = {"number": _s.number, "date_of_acquisition": _s.date_of_acquisition}

    # construct a form
    class Shares(colander.Schema):
        number = colander.SchemaNode(colander.Integer(), title=_("Number of Shares"))
        date_of_acquisition = colander.SchemaNode(colander.Date(), title=_("Date of Acquisition"))

    schema = Shares()
    form = deform.Form(schema, buttons=[deform.Button("submit", _(u"Submit"))])
    # form generation complete

    # if the form has been used and SUBMITTED, check contents
    if "submit" in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)
            # print("the appstruct from the form: %s \n") % appstruct
            # for thing in appstruct:
            #     print("the thing: %s") % thing
            #     print("type: %s") % type(thing)

        except ValidationFailure, e:  # pragma: no cover
            print(e)
            request.session.flash(
                _(u"Please note: There were errors, " "please check the form below."),
                "message_above_form",
                allow_duplicate=False,
            )
            return {"form": e.render()}

        # if no error occurred, persist the changed values info in database

        test1 = appstruct["number"] == _s.number  # changed value through form (different from db)?
        if not test1:
            log.info(
                "info about number of shares of %s changed by %s to %s"
                % (_s.id, request.user.login, appstruct["number"])
            )
            _s.number = appstruct["number"]
        test2 = (  # changed value through form (different from db)?
            appstruct["date_of_acquisition"] == _s.date_of_acquisition
        )
        if not test2:
            log.info(
                "info about date_of_acquisition of %s changed by %s to %s"
                % (_s.id, request.user.login, appstruct["date_of_acquisition"])
            )
            _s.date_of_acquisition = appstruct["date_of_acquisition"]
Exemple #39
0
def membership_status_fixer(request):
    '''
    Let a prospective member confirm her email address by filling a form.

    Was needed for crowdfunders from startnext: data was missing.
    '''
    user_email = request.matchdict['email']
    refcode = request.matchdict['refcode']
    token = request.matchdict['token']
    # try to get entry from DB
    afm = C3sMember.get_by_code(refcode)
    if isinstance(afm, NoneType):  # no entry?
        request.session.flash(
            'bad URL / bad codes. please contact [email protected]!',
            'message_above_form'
        )
        return {
            'form': '',
            'confirmed': False,
            'firstname': 'foo',
            'lastname': 'bar',
            'result_msg': 'bad URL / bad codes. please contact [email protected]!',
        }
    if (len(afm.mtype_confirm_token) == 0) or (
            afm.mtype_confirm_token.endswith('_used')):
        request.session.flash(
            'your token is invalid. please contact [email protected]!',
            'message_above_form'
        )
        return {
            'form': '',
            'confirmed': False,
            'result_msg': ('your token is invalid. please contact '
                           '[email protected]!'),
        }

    try:
        print "token: {}".format(token)
        assert(afm.mtype_confirm_token in token)
        assert(token in afm.mtype_confirm_token)
        assert(afm.email in user_email)
        assert(user_email in afm.email)
    except:
        request.session.flash(
            'bad token/email. please contact [email protected]!',
            'message_above_form')
        return {
            'form': '',
            'confirmed': False,
            'result_msg': 'bad token/email. please contact [email protected]!',
        }

    # construct a form
    class MembershipInfo(colander.Schema):
        yes_no = ((u'yes', _(u'Yes')),
                  (u'no', _(u'No')))
        membership_type = colander.SchemaNode(
            colander.String(),
            title=_(u'I want to become a ... (choose '
                    'membership type, see C3S SCE statute sec. 4)'),
            description=_(u'choose the type of membership.'),
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'normal',
                     _(u'FULL member. Full members have to be natural persons '
                       'who register at least three works with C3S they '
                       'created themselves. This applies to composers, '
                       'lyricists and remixers. They get a vote.')),
                    (u'investing',
                     _(u'INVESTING member. Investing members can be natural '
                       'or legal entities or private companies that do not '
                       'register works with C3S. They do not get a vote, '
                       'but may counsel.'))
                ),
            ),
            oid="mtype",
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(
                u'Currently, I am a member of (at least) one other '
                'collecting society.'),
            validator=colander.OneOf([x[0] for x in yes_no]),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            oid="other_colsoc",
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(u'If so, which one(s)? (comma separated)'),
            description=_(
                u'Please tell us which collecting societies '
                'you are a member of. '
                'If more than one, please separate them by comma(s).'),
            missing=unicode(''),
            oid="colsoc_name",
        )

    class MembershipForm(colander.Schema):
        """
        The Form consists of
        - Membership Information
        """
        membership_info = MembershipInfo(
            title=_(u"Membership Requirements")
        )

    schema = MembershipForm()

    form = deform.Form(
        schema,
        buttons=[
            deform.Button('submit', _(u'Submit')),
            deform.Button('reset', _(u'Reset'))
        ],
        renderer=ZPT_RENDERER
    )
    # if the form has NOT been used and submitted, remove error messages if any
    if 'submit' not in request.POST:
        request.session.pop_flash()

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

            # data sanity: if not in collecting society, don't save
            #  collsoc name even if it was supplied through form
            if 'no' in appstruct['membership_info']['member_of_colsoc']:
                appstruct['membership_info']['name_of_colsoc'] = ''
                print appstruct['membership_info']['name_of_colsoc']
                # print '-'*80

        except ValidationFailure, e:
            request.session.flash(
                _(u"Please note: There were errors, "
                  "please check the form below."),
                'message_above_form',
                allow_duplicate=False)
            return{
                'confirmed': True,
                'form': e.render()}
        # all good, store the information
        afm.membership_type = appstruct['membership_info']['membership_type']
        afm.member_of_colsoc = (
            appstruct['membership_info']['member_of_colsoc'] == u'yes')
        afm.name_of_colsoc = appstruct['membership_info']['name_of_colsoc']

        # remove old messages from the session
        request.session.pop_flash()

        # invalidate token
        afm.mtype_confirm_token += u'_used'
        # # notify staff
        message = Message(
            subject='[C3S Yes!] membership status confirmed',
            sender='*****@*****.**',
            recipients=[
                request.registry.settings['c3smembership.mailaddr'],
            ],
            body=u'see {}/detail/{}'.format(
                request.registry.settings['c3smembership.url'],
                afm.id)
        )
        mailer = get_mailer(request)
        mailer.send(message)

        return HTTPFound(request.route_url('mtype_thanks'))
def shares_edit(request):
    '''
    Edit details of a package of shares.
    '''
    # load info from DB -- if possible
    share = request.registry.share_information.get(request.matchdict['id'])

    if isinstance(share, NoneType):
        # entry was not found in database
        return get_memberhip_listing_redirect(request)
    else:
        appstruct = {}
        appstruct = {
            'number': share.number,
            'date_of_acquisition': share.date_of_acquisition,
        }

    # construct a form
    class SharesSchema(colander.Schema):
        """
        Defines the colander schema for shares.
        """
        number = colander.SchemaNode(
            colander.Integer(),
            title=_('Number of Shares'),
        )
        date_of_acquisition = colander.SchemaNode(
            colander.Date(), title=_('Date of Acquisition'))

    schema = SharesSchema()
    form = deform.Form(
        schema,
        buttons=[deform.Button('submit', _(u'Submit'))],
    )
    # form generation complete

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

        except ValidationFailure, validation_failure:  # pragma: no cover
            request.session.flash(_(u'Please note: There were errors, '
                                    'please check the form below.'),
                                  'message_above_form',
                                  allow_duplicate=False)
            return {'form': validation_failure.render()}

        # if no error occurred, persist the changed values info in database

        test1 = (  # changed value through form (different from db)?
            appstruct['number'] == share.number)
        if not test1:
            LOG.info('info about number of shares of %s changed by %s to %s',
                     share.id, request.user.login, appstruct['number'])
            share.number = appstruct['number']
        test2 = (  # changed value through form (different from db)?
            appstruct['date_of_acquisition'] == share.date_of_acquisition)
        if not test2:
            LOG.info(
                'info about date_of_acquisition of %s changed by %s to %s',
                share.id, request.user.login, appstruct['date_of_acquisition'])
            share.date_of_acquisition = appstruct['date_of_acquisition']
def new_member(request):
    '''
    let staff create a new member entry, when receiving input via dead wood
    '''

    # XXX check if submitted, etc...

    class PersonalData(colander.MappingSchema):
        """
        colander schema for membership application form
        """
        firstname = colander.SchemaNode(
            colander.String(),
            title=u'Vorname (b. Körpersch.: Ansprechpartner)',
            oid="firstname",
        )
        lastname = colander.SchemaNode(
            colander.String(),
            title=u'Nachname (b. Körpersch.: Name der Körperschaft)',
            oid="lastname",
        )
        email = colander.SchemaNode(
            colander.String(),
            title=_(u'E-Mail'),
            validator=colander.Email(),
            oid="email",
        )
        passwort = colander.SchemaNode(
            colander.String(),
            widget=deform.widget.HiddenWidget(),
            default='NoneSet',
            missing='NoneSetPurposefully'
        )
        address1 = colander.SchemaNode(
            colander.String(),
            title='Adresse Zeile 1'
        )
        address2 = colander.SchemaNode(
            colander.String(),
            missing=unicode(''),
            title='Adresse Zeile 2'
        )
        postcode = colander.SchemaNode(
            colander.String(),
            title='Postleitzahl',
            oid="postcode"
        )
        city = colander.SchemaNode(
            colander.String(),
            title='Ort',
            oid="city",
        )
        country = colander.SchemaNode(
            colander.String(),
            title='Land',
            default=country_default,
            widget=deform.widget.SelectWidget(
                values=country_codes),
            oid="country",
        )
        date_of_birth = colander.SchemaNode(
            colander.Date(),
            title='Geburtsdatum',
            # widget=deform.widget.DatePartsWidget(
            #    inline=True),
            default=date(1970, 1, 1),
            oid="date_of_birth",
        )
        locale = colander.SchemaNode(
            colander.String(),
            widget=deform.widget.HiddenWidget(),
            default='de',
            missing='de',
        )

    class MembershipInfo(colander.Schema):

        yes_no = ((u'yes', _(u'Yes')),
                  (u'no', _(u'No')),
                  (u'dontknow', _(u'Unknown')),)

        entity_type = colander.SchemaNode(
            colander.String(),
            title=(u'Person oder Körperschaft?'),
            description=u'Bitte die Kategorie des Mitglied auswählen.',
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'person',
                     (u'Person')),
                    (u'legalentity',
                     u'Körperschaft'),
                ),
            ),
            missing=unicode(''),
            oid='entity_type',
        )
        membership_type = colander.SchemaNode(
            colander.String(),
            title=(u'Art der Mitgliedschaft (lt. Satzung, §4)'),
            description=u'Bitte die Art der Mitgliedschaft auswählen.',
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'normal',
                     (u'Normales Mitglied')),
                    (u'investing',
                     u'Investierendes Mitglied'),
                    (u'unknown',
                     u'Unbekannt.'),
                ),
            ),
            missing=unicode(''),
            oid='membership_type',
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title='Mitglied einer Verwertungsgesellschaft?',
            validator=colander.OneOf([x[0] for x in yes_no]),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            missing=unicode(''),
            oid="other_colsoc",
            # validator=colsoc_validator
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=(u'Falls ja, welche? (Kommasepariert)'),
            missing=unicode(''),
            oid="colsoc_name",
            # validator=colander.All(
            #    colsoc_validator,
            # )
        )

    class Shares(colander.Schema):
        """
        the number of shares a member wants to hold
        """
        num_shares = colander.SchemaNode(
            colander.Integer(),
            title='Anzahl Anteile (1-60)',
            default="1",
            validator=colander.Range(
                min=1,
                max=60,
                min_err=u'mindestens 1',
                max_err=u'höchstens 60',
            ),
            oid="num_shares")

    class MembershipForm(colander.Schema):
        """
        The Form consists of
        - Personal Data
        - Membership Information
        - Shares
        """
        person = PersonalData(
            title=_(u"Personal Data"),
            # description=_(u"this is a test"),
            # css_class="thisisjustatest"
        )
        membership_info = MembershipInfo(
            title=_(u"Membership Requirements")
        )
        shares = Shares(
            title=_(u"Shares")
        )

    schema = MembershipForm()

    form = deform.Form(
        schema,
        buttons=[
            deform.Button('submit', _(u'Submit')),
            deform.Button('reset', _(u'Reset'))
        ],
        use_ajax=True,
        # renderer=zpt_renderer
    )

    # if the form has NOT been used and submitted, remove error messages if any
    if 'submit' not in request.POST:
        request.session.pop_flash()
        # print('ping!')

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)
            # print("the appstruct from the form: %s \n") % appstruct
            # for thing in appstruct:
            #    print("the thing: %s") % thing
            #    print("type: %s") % type(thing)

            # data sanity: if not in collecting society, don't save
            #  collsoc name even if it was supplied through form
            # if 'no' in appstruct['membership_info']['member_of_colsoc']:
            #    appstruct['membership_info']['name_of_colsoc'] = ''
            #    print appstruct['membership_info']['name_of_colsoc']
            # print '-'*80

        except ValidationFailure as e:
            # print("Validation Failure!")
            # print("the request.POST: %s \n" % request.POST)
            # for thing in request.POST:
            #    print("the thing: %s") % thing
            #    print("type: %s") % type(thing)
            # print(e.args)
            # print(e.error)
            # print(e.message)
            request.session.flash(
                _(u"Please note: There were errors, "
                  "please check the form below."),
                'message_above_form',
                allow_duplicate=False)
            return{'form': e.render()}

        def make_random_string():
            """
            used as email confirmation code
            """
            import random
            import string
            return u''.join(
                random.choice(
                    string.ascii_uppercase + string.digits
                ) for x in range(10))

        # make confirmation code and
        randomstring = make_random_string()
        # check if confirmation code is already used
        while (C3sMember.check_for_existing_confirm_code(randomstring)):
            # create a new one, if the new one already exists in the database
            randomstring = make_random_string()  # pragma: no cover

        # to store the data in the DB, an objet is created
        member = C3sMember(
            firstname=appstruct['person']['firstname'],
            lastname=appstruct['person']['lastname'],
            email=appstruct['person']['email'],
            password='******',
            address1=appstruct['person']['address1'],
            address2=appstruct['person']['address2'],
            postcode=appstruct['person']['postcode'],
            city=appstruct['person']['city'],
            country=appstruct['person']['country'],
            locale=appstruct['person']['locale'],
            date_of_birth=appstruct['person']['date_of_birth'],
            email_is_confirmed=False,
            email_confirm_code=randomstring,
            # is_composer=('composer' in appstruct['activity']),
            # is_lyricist=('lyricist' in appstruct['activity']),
            # is_producer=('music producer' in appstruct['activity']),
            # is_remixer=('remixer' in appstruct['activity']),
            # is_dj=('dj' in appstruct['activity']),
            date_of_submission=datetime.now(),
            # invest_member=(
            #    appstruct['membership_info']['invest_member'] == u'yes'),
            membership_type=appstruct['membership_info']['membership_type'],
            member_of_colsoc=(
                appstruct['membership_info']['member_of_colsoc'] == u'yes'),
            name_of_colsoc=appstruct['membership_info']['name_of_colsoc'],
            # opt_band=appstruct['opt_band'],
            # opt_URL=appstruct['opt_URL'],
            num_shares=appstruct['shares']['num_shares'],
        )
        if 'legalentity' in appstruct['membership_info']['entity_type']:
            # print "this is a legal entity"
            member.membership_type = u'investing'
            member.is_legalentity = True

        dbsession = DBSession()

        try:
            _temp = request.url.split('?')[1].split('=')
            if 'id' in _temp[0]:
                _id = _temp[1]
                # print("the id we want to recreate: %s" % _id)

            # add a member with a DB id that had seen its entry deleted before
                _mem = C3sMember.get_by_id(_id)  # load from id
                if isinstance(_mem, NoneType):  # check deletion status
                    member.id = _id  # set id as specified
        except:
            # print "no splitable url params found, creating new entry"
            pass

        # add member at next free DB id (default if member.id not set)
        try:
            dbsession.add(member)
            dbsession.flush()
            # print(member.id)
            the_new_id = member.id
            # appstruct['email_confirm_code'] = randomstring  # ???
        except InvalidRequestError, e:  # pragma: no cover
            print("InvalidRequestError! %s") % e
        except IntegrityError, ie:  # pragma: no cover
            print("IntegrityError! %s") % ie
Exemple #42
0
from c3smembership.presentation.i18n import _
from fdfgen import forge_fdf
from pyramid_mailer.message import (
    Message,
    Attachment,
)
import subprocess
import tempfile
import time

DEBUG = False
# DEBUG = True


country_codes = [
    ('AT', _(u'Austria')),
    ('BE', _(u'Belgium')),
    ('BG', _(u'Bulgaria')),
    ('CH', _(u'Switzerland')),
    ('CZ', _(u'Czech Republic')),
    ('DE', _(u'Germany')),
    ('DK', _(u'Denmark')),
    ('ES', _(u'Spain')),
    ('EE', _(u'Estonia')),
    ('FI', _(u'Finland')),
    ('FR', _(u'France')),
    ('GB', _(u'United Kingdom')),
    ('GR', _(u'Greece')),
    ('HU', _(u'Hungary')),
    ('HR', _(u'Croatia')),
    ('IE', _(u'Ireland')),
Exemple #43
0
def shares_edit(request):
    '''
    Edit details of a package of shares.
    '''
    # load info from DB -- if possible
    share = request.registry.share_information.get(request.matchdict['id'])

    if isinstance(share, NoneType):
        # entry was not found in database
        return get_memberhip_listing_redirect(request)
    else:
        appstruct = {}
        appstruct = {
            'number': share.number,
            'date_of_acquisition': share.date_of_acquisition,
        }

    # construct a form
    class SharesSchema(colander.Schema):
        """
        Defines the colander schema for shares.
        """
        number = colander.SchemaNode(
            colander.Integer(),
            title=_('Number of Shares'),
        )
        date_of_acquisition = colander.SchemaNode(
            colander.Date(),
            title=_('Date of Acquisition')
        )
    schema = SharesSchema()
    form = deform.Form(
        schema,
        buttons=[deform.Button('submit', _(u'Submit'))],
    )
    # form generation complete

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

        except ValidationFailure, validation_failure:  # pragma: no cover
            request.session.flash(
                _(u'Please note: There were errors, '
                  'please check the form below.'),
                'danger',
                allow_duplicate=False)
            return{'form': validation_failure.render()}

        # if no error occurred, persist the changed values info in database

        test1 = (  # changed value through form (different from db)?
            appstruct['number'] == share.number)
        if not test1:
            LOG.info(
                'info about number of shares of %s changed by %s to %s',
                share.id,
                request.user.login,
                appstruct['number'])
            share.number = appstruct['number']
        test2 = (  # changed value through form (different from db)?
            appstruct['date_of_acquisition'] == share.date_of_acquisition)
        if not test2:
            LOG.info(
                'info about date_of_acquisition of %s changed by %s to %s',
                share.id,
                request.user.login,
                appstruct['date_of_acquisition'])
            share.date_of_acquisition = appstruct['date_of_acquisition']
Exemple #44
0
    class MembershipInfo(colander.Schema):
        """
        Colander schema of the additional data for editing member data.
        """
        yes_no = (
            (u'yes', _(u'Yes')),
            (u'no', _(u'No')),
            (u'dontknow', _(u'Unknwon')),
        )

        entity_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Member Category'),
            description=_(u'Please choose the member category.'),
            widget=deform.widget.RadioChoiceWidget(values=(
                (u'person', _(u'Person')),
                (u'legalentity', _(u'Legal Entity')),
            ), ),
            missing=u'',
            oid='entity_type',
        )
        membership_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Type of Membership (C3S Statute § 4)'),
            description=_(u'Please choose the type of membership.'),
            widget=deform.widget.RadioChoiceWidget(values=(
                (u'normal', _(u'Member')),
                (u'investing', _(u'Investing (non-user) member')),
                (u'unknown', _(u'Unknown')),
            ), ),
            missing=u'',
            oid='membership_type',
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_('Member of a Collecting Society'),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            oid='other_colsoc',
            default=u'',
            missing=u'',
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(u'Names of Collecting Societies'),
            description=_(u'Please separate multiple collecting societies by '
                          u'comma.'),
            missing=u'',
            oid='colsoc_name',
        )
 class PersonalData(colander.MappingSchema):
     """
     colander schema for membership application form
     """
     firstname = colander.SchemaNode(
         colander.String(),
         title=u'Vorname (b. Körpersch.: Ansprechpartner)',
         oid="firstname",
     )
     lastname = colander.SchemaNode(
         colander.String(),
         title=u'Nachname (b. Körpersch.: Name der Körperschaft)',
         oid="lastname",
     )
     email = colander.SchemaNode(
         colander.String(),
         title=_(u'E-Mail'),
         validator=colander.Email(),
         oid="email",
     )
     passwort = colander.SchemaNode(
         colander.String(),
         widget=deform.widget.HiddenWidget(),
         default='NoneSet',
         missing='NoneSetPurposefully'
     )
     address1 = colander.SchemaNode(
         colander.String(),
         title='Adresse Zeile 1'
     )
     address2 = colander.SchemaNode(
         colander.String(),
         missing=unicode(''),
         title='Adresse Zeile 2'
     )
     postcode = colander.SchemaNode(
         colander.String(),
         title='Postleitzahl',
         oid="postcode"
     )
     city = colander.SchemaNode(
         colander.String(),
         title='Ort',
         oid="city",
     )
     country = colander.SchemaNode(
         colander.String(),
         title='Land',
         default=country_default,
         widget=deform.widget.SelectWidget(
             values=country_codes),
         oid="country",
     )
     date_of_birth = colander.SchemaNode(
         colander.Date(),
         title='Geburtsdatum',
         # widget=deform.widget.DatePartsWidget(
         #    inline=True),
         default=date(1970, 1, 1),
         oid="date_of_birth",
     )
     locale = colander.SchemaNode(
         colander.String(),
         widget=deform.widget.HiddenWidget(),
         default='de',
         missing='de',
     )
Exemple #46
0
def success_check_email(request):
    """
    This view is called from the page that shows a user her data for correction
    by clicking a "send email" button.
    This view then sends out the email with a verification link
    and returns a note to go check mail.
    """
    # check if user has used the form (good) or 'guessed' this URL (bad)

    if 'appstruct' in request.session:
        # we do have valid info from the form in the session (good)
        appstruct = request.session['appstruct']
        from pyramid_mailer.message import Message
        try:
            mailer = get_mailer(request)
        except:
            return HTTPFound(location=request.route_url('join'))

        if 'de' in appstruct['person']['_LOCALE_']:
            the_mail_body = u'''
Hallo {} {}!

bitte benutze diesen Link um deine E-Mail-Adresse zu bestätigen
und dein PDF herunterzuladen:

   {}/verify/{}/{}

Danke!

Dein C3S Team
            '''
        else:
            the_mail_body = u'''
Hello {} {}!

please use this link to verify your email address
and download your personalised PDF:

   {}/verify/{}/{}

thanks!

Your C3S team
            '''
        the_mail = Message(
            subject=request.localizer.translate(_(
                'check-email-paragraph-check-email-subject',
                default=u'C3S: confirm your email address and load your PDF')),
            sender="*****@*****.**",
            recipients=[appstruct['person']['email']],
            body=the_mail_body.format(
                appstruct['person']['firstname'],
                appstruct['person']['lastname'],
                request.registry.settings['c3smembership.url'],
                appstruct['person']['email'],
                appstruct['email_confirm_code']
            )
        )
        if 'true' in request.registry.settings['testing.mail_to_console']:
            # print(the_mail.body)
            log.info(the_mail.subject)
            log.info(the_mail.recipients)
            log.info(the_mail.body)
            # just logging, not printing, b/c test fails otherwise:
            # env/bin/nosetests
            #    c3smembership/tests/test_views_webdriver.py:
            #    JoinFormTests.test_form_submission_de
        else:
            mailer.send(the_mail)

        # make the session go away
        request.session.invalidate()
        return {
            'firstname': appstruct['person']['firstname'],
            'lastname': appstruct['person']['lastname'],
        }
    # 'else': send user to the form
    return HTTPFound(location=request.route_url('join'))
Exemple #47
0
def new_member(request):
    '''
    let staff create a new member entry, when receiving input via dead wood
    '''

    # XXX check if submitted, etc...

    class PersonalData(colander.MappingSchema):
        """
        colander schema for membership application form
        """
        firstname = colander.SchemaNode(
            colander.String(),
            title=u'Vorname (b. Körpersch.: Ansprechpartner)',
            oid="firstname",
        )
        lastname = colander.SchemaNode(
            colander.String(),
            title=u'Nachname (b. Körpersch.: Name der Körperschaft)',
            oid="lastname",
        )
        email = colander.SchemaNode(
            colander.String(),
            title=_(u'E-Mail'),
            validator=colander.Email(),
            oid="email",
        )
        passwort = colander.SchemaNode(
            colander.String(),
            widget=deform.widget.HiddenWidget(),
            default='NoneSet',
            missing='NoneSetPurposefully'
        )
        address1 = colander.SchemaNode(
            colander.String(),
            title='Adresse Zeile 1'
        )
        address2 = colander.SchemaNode(
            colander.String(),
            missing=unicode(''),
            title='Adresse Zeile 2'
        )
        postcode = colander.SchemaNode(
            colander.String(),
            title='Postleitzahl',
            oid="postcode"
        )
        city = colander.SchemaNode(
            colander.String(),
            title='Ort',
            oid="city",
        )
        country = colander.SchemaNode(
            colander.String(),
            title='Land',
            default=country_default,
            widget=deform.widget.SelectWidget(
                values=country_codes),
            oid="country",
        )
        date_of_birth = colander.SchemaNode(
            colander.Date(),
            title='Geburtsdatum',
            # widget=deform.widget.DatePartsWidget(
            #    inline=True),
            default=date(1970, 1, 1),
            validator=Range(
                min=date(1913, 1, 1),
                max=date(2000, 1, 1),
                min_err=_(u'${val} is earlier than earliest date ${min}'),
                max_err=_(u'${val} is later than latest date ${max}')
            ),
            oid="date_of_birth",
        )
        _LOCALE_ = colander.SchemaNode(
            colander.String(),
            widget=deform.widget.HiddenWidget(),
            default='de',
            missing='de',
        )

    class MembershipInfo(colander.Schema):

        yes_no = ((u'yes', _(u'Yes')),
                  (u'no', _(u'No')),
                  (u'dontknow', _(u'Unknown')),)

        entity_type = colander.SchemaNode(
            colander.String(),
            title=(u'Person oder Körperschaft?'),
            description=u'Bitte die Kategorie des Mitglied auswählen.',
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'person',
                     (u'Person')),
                    (u'legalentity',
                     u'Körperschaft'),
                ),
            ),
            missing=unicode(''),
            oid='entity_type',
        )
        membership_type = colander.SchemaNode(
            colander.String(),
            title=(u'Art der Mitgliedschaft (lt. Satzung, §4)'),
            description=u'Bitte die Art der Mitgliedschaft auswählen.',
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (u'normal',
                     (u'Normales Mitglied')),
                    (u'investing',
                     u'Investierendes Mitglied'),
                    (u'unknown',
                     u'Unbekannt.'),
                ),
            ),
            missing=unicode(''),
            oid='membership_type',
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title='Mitglied einer Verwertungsgesellschaft?',
            validator=colander.OneOf([x[0] for x in yes_no]),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            missing=unicode(''),
            oid="other_colsoc",
            # validator=colsoc_validator
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=(u'Falls ja, welche? (Kommasepariert)'),
            missing=unicode(''),
            oid="colsoc_name",
            # validator=colander.All(
            #    colsoc_validator,
            # )
        )

    class Shares(colander.Schema):
        """
        the number of shares a member wants to hold
        """
        num_shares = colander.SchemaNode(
            colander.Integer(),
            title='Anzahl Anteile (1-60)',
            default="1",
            validator=colander.Range(
                min=1,
                max=60,
                min_err=u'mindestens 1',
                max_err=u'höchstens 60',
            ),
            oid="num_shares")

    class MembershipForm(colander.Schema):
        """
        The Form consists of
        - Personal Data
        - Membership Information
        - Shares
        """
        person = PersonalData(
            title=_(u"Personal Data"),
            # description=_(u"this is a test"),
            # css_class="thisisjustatest"
        )
        membership_info = MembershipInfo(
            title=_(u"Membership Requirements")
        )
        shares = Shares(
            title=_(u"Shares")
        )

    schema = MembershipForm()

    form = deform.Form(
        schema,
        buttons=[
            deform.Button('submit', _(u'Submit')),
            deform.Button('reset', _(u'Reset'))
        ],
        use_ajax=True,
        # renderer=zpt_renderer
    )

    # if the form has NOT been used and submitted, remove error messages if any
    if 'submit' not in request.POST:
        request.session.pop_flash()
        # print('ping!')

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)
            # print("the appstruct from the form: %s \n") % appstruct
            # for thing in appstruct:
            #    print("the thing: %s") % thing
            #    print("type: %s") % type(thing)

            # data sanity: if not in collecting society, don't save
            #  collsoc name even if it was supplied through form
            # if 'no' in appstruct['membership_info']['member_of_colsoc']:
            #    appstruct['membership_info']['name_of_colsoc'] = ''
            #    print appstruct['membership_info']['name_of_colsoc']
            # print '-'*80

        except ValidationFailure as e:
            # print("Validation Failure!")
            # print("the request.POST: %s \n" % request.POST)
            # for thing in request.POST:
            #    print("the thing: %s") % thing
            #    print("type: %s") % type(thing)
            # print(e.args)
            # print(e.error)
            # print(e.message)
            request.session.flash(
                _(u"Please note: There were errors, "
                  "please check the form below."),
                'message_above_form',
                allow_duplicate=False)
            return{'form': e.render()}

        def make_random_string():
            """
            used as email confirmation code
            """
            import random
            import string
            return u''.join(
                random.choice(
                    string.ascii_uppercase + string.digits
                ) for x in range(10))

        # make confirmation code and
        randomstring = make_random_string()
        # check if confirmation code is already used
        while (C3sMember.check_for_existing_confirm_code(randomstring)):
            # create a new one, if the new one already exists in the database
            randomstring = make_random_string()  # pragma: no cover

        # to store the data in the DB, an objet is created
        member = C3sMember(
            firstname=appstruct['person']['firstname'],
            lastname=appstruct['person']['lastname'],
            email=appstruct['person']['email'],
            password='******',
            address1=appstruct['person']['address1'],
            address2=appstruct['person']['address2'],
            postcode=appstruct['person']['postcode'],
            city=appstruct['person']['city'],
            country=appstruct['person']['country'],
            locale=appstruct['person']['_LOCALE_'],
            date_of_birth=appstruct['person']['date_of_birth'],
            email_is_confirmed=False,
            email_confirm_code=randomstring,
            # is_composer=('composer' in appstruct['activity']),
            # is_lyricist=('lyricist' in appstruct['activity']),
            # is_producer=('music producer' in appstruct['activity']),
            # is_remixer=('remixer' in appstruct['activity']),
            # is_dj=('dj' in appstruct['activity']),
            date_of_submission=datetime.now(),
            # invest_member=(
            #    appstruct['membership_info']['invest_member'] == u'yes'),
            membership_type=appstruct['membership_info']['membership_type'],
            member_of_colsoc=(
                appstruct['membership_info']['member_of_colsoc'] == u'yes'),
            name_of_colsoc=appstruct['membership_info']['name_of_colsoc'],
            # opt_band=appstruct['opt_band'],
            # opt_URL=appstruct['opt_URL'],
            num_shares=appstruct['shares']['num_shares'],
        )
        if 'legalentity' in appstruct['membership_info']['entity_type']:
            # print "this is a legal entity"
            member.membership_type = u'investing'
            member.is_legalentity = True

        dbsession = DBSession()

        try:
            _temp = request.url.split('?')[1].split('=')
            if 'id' in _temp[0]:
                _id = _temp[1]
                # print("the id we want to recreate: %s" % _id)

            # add a member with a DB id that had seen its entry deleted before
                _mem = C3sMember.get_by_id(_id)  # load from id
                if isinstance(_mem, NoneType):  # check deletion status
                    member.id = _id  # set id as specified
        except:
            # print "no splitable url params found, creating new entry"
            pass

        # add member at next free DB id (default if member.id not set)
        try:
            dbsession.add(member)
            dbsession.flush()
            # print(member.id)
            the_new_id = member.id
            # appstruct['email_confirm_code'] = randomstring  # ???
        except InvalidRequestError, e:  # pragma: no cover
            print("InvalidRequestError! %s") % e
        except IntegrityError, ie:  # pragma: no cover
            print("IntegrityError! %s") % ie
Exemple #48
0
def join_c3s(request):
    """
    This is the main membership application form view: Join C3S as member
    """
    # if another language was chosen by clicking on a flag
    # the add_locale_to_cookie subscriber has planted an attr on the request
    if hasattr(request, '_REDIRECT_'):

        _query = request._REDIRECT_
        # set language cookie
        # ToDo: the proper cookie name is _LOCALE_ (pyramid)
        request.response.set_cookie('locale', _query)
        request.locale = _query
        locale_name = _query
        return HTTPFound(location=request.route_url('join'),
                         headers=request.response.headers)
    else:
        locale_name = get_locale_name(request)

    if DEBUG:
        print "-- locale_name: " + str(locale_name)

    # set default of Country select widget according to locale
    try:
        country_default = customization.locale_country_mapping.get(locale_name)
    except AttributeError:
        print(dir(customization))
        country_default = 'GB'
    if DEBUG:
        print("== locale is :" + str(locale_name))
        print("== choosing :" + str(country_default))

    class PersonalData(colander.MappingSchema):
        """
        colander schema for membership application form
        """
        firstname = colander.SchemaNode(
            colander.String(),
            title=_(u"(Real) First Name"),
            oid="firstname",
        )
        lastname = colander.SchemaNode(
            colander.String(),
            title=_(u"(Real) Last Name"),
            oid="lastname",
        )
        email = colander.SchemaNode(
            colander.String(),
            title=_(u'Email Address'),
            validator=colander.Email(),
            oid="email",
        )
        password = colander.SchemaNode(
            colander.String(),
            validator=colander.Length(min=5, max=100),
            widget=deform.widget.CheckedPasswordWidget(size=20),
            title=_(u'Password (to protect access to your data)'),
            description=_(u'We need a password to protect your data. After '
                          u'verifying your email you will have to enter it.'),
            oid='password',
        )
        address1 = colander.SchemaNode(colander.String(),
                                       title=_(u'Address Line 1'))
        address2 = colander.SchemaNode(colander.String(),
                                       missing=unicode(''),
                                       title=_(u'Address Line 2'))
        postcode = colander.SchemaNode(colander.String(),
                                       title=_(u'Postal Code'),
                                       oid="postcode")
        city = colander.SchemaNode(
            colander.String(),
            title=_(u'City'),
            oid="city",
        )
        country = colander.SchemaNode(
            colander.String(),
            title=_(u'Country'),
            default=country_default,
            widget=deform.widget.SelectWidget(values=country_codes),
            oid="country",
        )
        date_of_birth = colander.SchemaNode(
            colander.Date(),
            title=_(u'Date of Birth'),
            # css_class="hasDatePicker",
            widget=deform.widget.DatePartsWidget(),
            default=date(2013, 1, 1),
            validator=Range(
                min=date(1913, 1, 1),
                # max 18th birthday, no minors through web formular
                max=date(date.today().year - 18,
                         date.today().month,
                         date.today().day),
                min_err=_(u'Sorry, we do not believe that you are that old'),
                max_err=_(
                    u'Unfortunately, the membership application of an '
                    u'underaged person is currently not possible via our web '
                    u'form. Please send an email to [email protected].')),
            oid="date_of_birth",
        )
        locale = colander.SchemaNode(colander.String(),
                                     widget=deform.widget.HiddenWidget(),
                                     default=locale_name)

    class MembershipInfo(colander.Schema):
        """
        Basic member information.
        """
        yes_no = ((u'yes', _(u'Yes')), (u'no', _(u'No')))
        if len(customization.membership_types) > 1:
            membership_type = colander.SchemaNode(
                colander.String(),
                title=_(
                    u'I want to become a ... '
                    u'(choose membership type, see C3S SCE statute sec. 4)'),
                description=_(u'choose the type of membership.'),
                widget=deform.widget.RadioChoiceWidget(
                    values=((i['name'], i['description'])
                            for i in customization.membership_types), ),
                oid='membership_type')
        if customization.enable_colsoc_association:
            member_of_colsoc = colander.SchemaNode(
                colander.String(),
                title=_(u'Currently, I am a member of (at least) one other '
                        u'collecting society.'),
                validator=colander.OneOf([x[0] for x in yes_no]),
                widget=deform.widget.RadioChoiceWidget(values=yes_no),
                oid="other_colsoc",
                # validator=colsoc_validator
            )
            name_of_colsoc = colander.SchemaNode(
                colander.String(),
                title=_(u'If so, which one(s)? Please separate multiple '
                        u'collecting societies by comma.'),
                description=_(
                    u'Please tell us which collecting societies '
                    u'you are a member of. '
                    u'If more than one, please separate them by comma.'),
                missing=unicode(''),
                oid="colsoc_name",
            )

    class Fees(colander.Schema):
        member_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Please tell us wether you\'re an individual, '
                    u'freelancer or company or want to support us '
                    u'generously as a sustaining member'),
            widget=deform.widget.RadioChoiceWidget(
                values=[(member_type, t_description) for fee, member_type,
                        t_description in customization.membership_fees]),
            oid='member_type')

        # not validating here: depends on ^
        # http://deformdemo.repoze.org/require_one_or_another/
        member_custom_fee = colander.SchemaNode(
            colander.Decimal('1.00'),
            title=_(u'custom membership fee'),
            widget=deform.widget.MoneyInputWidget(
                symbol=customization.currency,
                showSymbol=True,
                defaultZero=True),
            description=_(
                u'Sustaining members: You can set your fees (minimum 100 €)'),
            oid='membership_custom_fee',
            default=customization.membership_fee_custom_min,
            validator=Range(
                min=customization.membership_fee_custom_min,
                max=None,
                min_err=
                _(u'please enter at least the minimum fee for sustaining members'
                  )))

    class Shares(colander.Schema):
        """
        the number of shares a member wants to hold

        this involves a slider widget: added to deforms widgets.
        see README.Slider.rst
        """
        num_shares = colander.SchemaNode(
            colander.Integer(),
            title=_(u"I want to buy the following number "
                    u"of Shares (50 € each, up to 3000 €, see "
                    u"C3S statute sec. 5)"),
            description=_(
                u'You can choose any amount of shares between 1 and 60.'),
            default="1",
            widget=TextInputSliderWidget(size=3, css_class='num_shares_input'),
            validator=colander.Range(
                min=1,
                max=60,
                min_err=_(u'You need at least one share of 50 €.'),
                max_err=_(u'You may choose 60 shares at most (3000 €).'),
            ),
            oid="num_shares")

    class TermsInfo(colander.Schema):
        """
        some legal requirements
        """
        def statute_validator(node, value):
            """
            Validator for statute confirmation.
            """
            if not value:
                # raise without additional error message as the description
                # already explains the necessity of the checkbox
                raise Invalid(node, u'')

        got_statute = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            #title=(u''),
            title=_(u'I acknowledge that the statutes and membership dues '
                    u'regulations determine periodic contributions '
                    u'for full members.'),
            label=_(
                u'An electronic copy of the statute of the '
                u'C3S SCE has been made available to me (see link below).'),
            description=_(u'You must confirm to have access to the statute.'),
            widget=deform.widget.CheckboxWidget(),
            validator=statute_validator,
            required=True,
            oid='got_statute',
            #label=_('Yes, really'),
        )

        def dues_regulations_validator(node, value):
            """
            Validator for dues regulations confirmation.
            """
            if not value:
                # raise without additional error message as the description
                # already explains the necessity of the checkbox
                raise Invalid(node, u'')

        got_dues_regulations = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            title=(u''),
            label=_(
                u'An electronic copy of the temporary membership dues '
                u'regulations of the C3S SCE has been made available to me '
                u'(see link below).'),
            description=_(u'You must confirm to have access to the temporary '
                          u'membership dues regulations.'),
            widget=deform.widget.CheckboxWidget(),
            validator=dues_regulations_validator,
            required=True,
            oid='got_dues_regulations',
            #label=_('Yes'),
        )

    class MembershipForm(colander.Schema):
        """
        The Form consists of
        - Personal Data
        - Membership Information
        - Shares
        """
        person = PersonalData(title=_(u'Personal Data'), )
        if len(customization.membership_types
               ) > 1 or customization.enable_colsoc_association:
            membership_info = MembershipInfo(title=_(u'Membership Data'))
        shares = Shares(title=_(u'Shares'))
        try:
            customization.membership_fees
        except NameError:
            pass
        else:
            fees = Fees(title=_(u'Membership Fees'))
        acknowledge_terms = TermsInfo(title=_(u'Acknowledgement'))

    schema = MembershipForm()

    form = deform.Form(schema,
                       buttons=[
                           deform.Button('submit', _(u'Next')),
                           deform.Button('reset', _(u'Reset'))
                       ],
                       use_ajax=True,
                       renderer=ZPT_RENDERER)

    # if the form has NOT been used and submitted, remove error messages if any
    if 'submit' not in request.POST:
        request.session.pop_flash()

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

            # data sanity: if not in collecting society, don't save
            #  collsoc name even if it was supplied through form
            if customization.membership_types and len(
                    customization.membership_types
            ) > 1 and 'no' in appstruct['membership_info']['member_of_colsoc']:
                appstruct['membership_info']['name_of_colsoc'] = ''

        except ValidationFailure as validation_failure:
            request.session.flash(_(u'Please note: There were errors, '
                                    u'please check the form below.'),
                                  'message_above_form',
                                  allow_duplicate=False)

            # If the validation error was not caused by the password field,
            # manually set an error to the password field because the user
            # needs to re-enter it after a validation error.
            form = validation_failure.field
            if form['person']['password'].error is None:
                form['person']['password'].error = Invalid(
                    None, _(u'Please re-enter your password.'))
                validation_failure = ValidationFailure(form, None, form.error)

            return {'form': validation_failure.render()}

        def make_random_string():
            """
            used as email confirmation code
            """
            import random
            import string
            return u''.join(
                random.choice(string.ascii_uppercase + string.digits)
                for x in range(10))

        # make confirmation code and
        randomstring = make_random_string()
        # check if confirmation code is already used
        while C3sMember.check_for_existing_confirm_code(randomstring):
            # create a new one, if the new one already exists in the database
            randomstring = make_random_string()  # pragma: no cover

        # to store the data in the DB, an objet is created
        coopMemberArgs = dict(
            firstname=appstruct['person']['firstname'],
            lastname=appstruct['person']['lastname'],
            email=appstruct['person']['email'],
            password=appstruct['person']['password'],
            address1=appstruct['person']['address1'],
            address2=appstruct['person']['address2'],
            postcode=appstruct['person']['postcode'],
            city=appstruct['person']['city'],
            country=appstruct['person']['country'],
            locale=appstruct['person']['locale'],
            date_of_birth=appstruct['person']['date_of_birth'],
            email_is_confirmed=False,
            email_confirm_code=randomstring,
            date_of_submission=datetime.now(),
            num_shares=appstruct['shares']['num_shares'],
        )

        if customization.enable_colsoc_association:
            coopMemberArgs['member_of_colsoc'] = (
                appstruct['membership_info']['member_of_colsoc'] == u'yes'),
            coopMemberArgs['name_of_colsoc'] = appstruct['membership_info'][
                'name_of_colsoc']

        if customization.membership_types and len(
                customization.membership_types) > 1:
            coopMemberArgs['membership_type'] = appstruct['membership_info'][
                'membership_type']

        member = C3sMember(**coopMemberArgs)
        dbsession = DBSession()
        try:
            dbsession.add(member)
            appstruct['email_confirm_code'] = randomstring
            if appstruct['fees']['member_type'] == 'sustaining':
                appstruct['fees']['fee'] = appstruct['fees'][
                    'member_custom_fee']
            else:
                appstruct['fees']['fee'] = [
                    v for v, t, d in customization.membership_fees
                    if t == appstruct['fees']['member_type']
                ][0]

        except InvalidRequestError as ire:  # pragma: no cover
            print("InvalidRequestError! %s") % ire
        except IntegrityError as integrity_error:  # pragma: no cover
            print("IntegrityError! %s") % integrity_error

        # redirect to success page, then return the PDF
        # first, store appstruct in session
        request.session['appstruct'] = appstruct
        request.session['appstruct']['locale'] = \
            appstruct['person']['locale']
        # empty the messages queue (as validation worked anyways)
        deleted_msg = request.session.pop_flash()
        del deleted_msg
        return HTTPFound(  # redirect to success page
            location=request.route_url('success'), )

    # if the form was submitted and gathered info shown on the success page,
    # BUT the user wants to correct their information:
    else:
        if 'edit' in request.POST:
            print(request.POST['edit'])
        # remove annoying message from other session
        deleted_msg = request.session.pop_flash()
        del deleted_msg
        if 'appstruct' in request.session:
            appstruct = request.session['appstruct']
            # pre-fill the form with the values from last time
            form.set_appstruct(appstruct)

    html = form.render()

    return {'form': html}
Exemple #49
0
import customization
from fdfgen import forge_fdf
from pyramid_mailer.message import (
    Message,
    Attachment,
)
import subprocess
import tempfile
import time

DEBUG = False
# DEBUG = True


country_codes = [
    ('AT', _(u'Austria')),
    ('BE', _(u'Belgium')),
    ('BG', _(u'Bulgaria')),
    ('CH', _(u'Switzerland')),
    ('CZ', _(u'Czech Republic')),
    ('DE', _(u'Germany')),
    ('DK', _(u'Denmark')),
    ('ES', _(u'Spain')),
    ('EE', _(u'Estonia')),
    ('FI', _(u'Finland')),
    ('FR', _(u'France')),
    ('GB', _(u'United Kingdom')),
    ('GR', _(u'Greece')),
    ('HU', _(u'Hungary')),
    ('HR', _(u'Croatia')),
    ('IE', _(u'Ireland')),
Exemple #50
0
 class PersonalData(colander.MappingSchema):
     """
     colander schema for membership application form
     """
     firstname = colander.SchemaNode(
         colander.String(),
         title=_(u"(Real) First Name"),
         oid="firstname",
     )
     lastname = colander.SchemaNode(
         colander.String(),
         title=_(u"(Real) Last Name"),
         oid="lastname",
     )
     email = colander.SchemaNode(
         colander.String(),
         title=_(u'Email Address'),
         validator=colander.Email(),
         oid="email",
     )
     password = colander.SchemaNode(
         colander.String(),
         validator=colander.Length(min=5, max=100),
         widget=deform.widget.CheckedPasswordWidget(size=20),
         title=_(u'Password (to protect access to your data)'),
         description=_(u'We need a password to protect your data. After '
                       u'verifying your email you will have to enter it.'),
         oid='password',
     )
     address1 = colander.SchemaNode(colander.String(),
                                    title=_(u'Address Line 1'))
     address2 = colander.SchemaNode(colander.String(),
                                    missing=unicode(''),
                                    title=_(u'Address Line 2'))
     postcode = colander.SchemaNode(colander.String(),
                                    title=_(u'Postal Code'),
                                    oid="postcode")
     city = colander.SchemaNode(
         colander.String(),
         title=_(u'City'),
         oid="city",
     )
     country = colander.SchemaNode(
         colander.String(),
         title=_(u'Country'),
         default=country_default,
         widget=deform.widget.SelectWidget(values=country_codes),
         oid="country",
     )
     date_of_birth = colander.SchemaNode(
         colander.Date(),
         title=_(u'Date of Birth'),
         # css_class="hasDatePicker",
         widget=deform.widget.DatePartsWidget(),
         default=date(2013, 1, 1),
         validator=Range(
             min=date(1913, 1, 1),
             # max 18th birthday, no minors through web formular
             max=date(date.today().year - 18,
                      date.today().month,
                      date.today().day),
             min_err=_(u'Sorry, we do not believe that you are that old'),
             max_err=_(
                 u'Unfortunately, the membership application of an '
                 u'underaged person is currently not possible via our web '
                 u'form. Please send an email to [email protected].')),
         oid="date_of_birth",
     )
     locale = colander.SchemaNode(colander.String(),
                                  widget=deform.widget.HiddenWidget(),
                                  default=locale_name)
Exemple #51
0
def success_verify_email(request):
    """
    This view is called via links sent in mails to verify mail addresses.
    It extracts both email and verification code from the URL.
    It will ask for a password
    and checks if there is a match in the database.

    If the password matches, and all is correct,
    the view shows a download link and further info.
    """
    user_email = request.matchdict['email']
    confirm_code = request.matchdict['code']
    # if we want to ask the user for her password (through a form)
    # we need to have a url to send the form to
    post_url = '/verify/' + user_email + '/' + confirm_code

    if 'submit' in request.POST:
        request.session.pop_flash('message_above_form')
        request.session.pop_flash('message_above_login')
        if 'password' in request.POST:
            _passwd = request.POST['password']

        # get matching dataset from DB
        member = C3sMember.get_by_code(confirm_code)

        if isinstance(member, NoneType):
            not_found_msg = _(
                u"Not found. Check verification URL. "
                "If all seems right, please use the form again.")
            return {
                'correct': False,
                'namepart': '',
                'result_msg': not_found_msg,
            }

        # check if the password is valid
        try:
            correct = C3sMember.check_password(member.id, _passwd)
        except AttributeError:
            correct = False
            request.session.flash(
                _(u'Wrong Password!'),
                'message_above_login')

        if (member.email == user_email) and correct:
            # set the email_is_confirmed flag in the DB for this signee
            member.email_is_confirmed = True
            namepart = member.firstname + member.lastname

            # Replace special characters with underscore
            import re
            pdf_file_name_part = re.sub(
                '[^a-zA-Z0-9]',
                '_',
                namepart)

            appstruct = {
                'firstname': member.firstname,
                'lastname': member.lastname,
                'email': member.email,
                'email_confirm_code': member.email_confirm_code,
                'address1': member.address1,
                'address2': member.address2,
                'postcode': member.postcode,
                'city': member.city,
                'country': member.country,
                'date_of_birth': member.date_of_birth,
                'date_of_submission': member.date_of_submission,
                'membership_type': member.membership_type,
                'member_of_colsoc':
                    u'yes' if member.member_of_colsoc else u'no',
                'name_of_colsoc': member.name_of_colsoc,
                'num_shares': member.num_shares,
            }
            request.session['appstruct'] = appstruct

            # log this person in, using the session
            LOG.info('verified code and password for id %s', member.id)
            request.session.save()
            return {
                'firstname': member.firstname,
                'lastname': member.lastname,
                'code': member.email_confirm_code,
                'correct': True,
                'namepart': pdf_file_name_part,
                'result_msg': _("Success. Load your PDF!")
            }
    request.session.flash(
        _(u"Please enter your password."),
        'message_above_login',
        allow_duplicate=False
    )
    return {
        'post_url': post_url,
        'firstname': '',
        'lastname': '',
        'namepart': '',
        'correct': False,
        'result_msg': "something went wrong."
    }
Exemple #52
0
def join_c3s(request):
    """
    This is the main membership application form view: Join C3S as member
    """
    # if another language was chosen by clicking on a flag
    # the add_locale_to_cookie subscriber has planted an attr on the request
    if hasattr(request, '_REDIRECT_'):

        _query = request._REDIRECT_
        # set language cookie
        request.response.set_cookie('locale', _query)
        request.locale = _query
        return HTTPFound(location=request.route_url('join'),
                         headers=request.response.headers)

    class MembershipInfo(colander.Schema):
        """
        Basic member information.
        """
        yes_no = ((u'yes', _(u'Yes')),
                  (u'no', _(u'No')))
        membership_type = colander.SchemaNode(
            colander.String(),
            title=_(u'I want to become a ... '
                    u'(choose membership type, see C3S SCE statute sec. 4)'),
            description=_(u'choose the type of membership.'),
            widget=deform.widget.RadioChoiceWidget(
                values=(
                    (
                        u'normal',
                        _(u'FULL member. Full members have to be natural '
                          u'persons who register at least three works they '
                          u'created themselves with C3S. This applies to '
                          u'composers, lyricists and remixers. They get a '
                          u'vote.')),
                    (
                        u'investing',
                        _(u'INVESTING member. Investing members can be '
                          u'natural or legal entities or private companies '
                          u'that do not register works with C3S. They do '
                          u'not get a vote, but may counsel.'))
                ),
            ),
            oid='membership_type'
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(
                u'Currently, I am a member of (at least) one other '
                u'collecting society.'),
            validator=colander.OneOf([x[0] for x in yes_no]),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            oid="other_colsoc",
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(u'If so, which one(s)? Please separate multiple '
                    u'collecting societies by comma.'),
            description=_(
                u'Please tell us which collecting societies '
                u'you are a member of. '
                u'If more than one, please separate them by comma.'),
            missing=unicode(''),
            oid="colsoc_name",
        )

    class Shares(colander.Schema):
        """
        the number of shares a member wants to hold

        this involves a slider widget: added to deforms widgets.
        see README.Slider.rst
        """
        num_shares = colander.SchemaNode(
            colander.Integer(),
            title=_(u"I want to buy the following number "
                    u"of Shares (50€ each, up to 3000€, see "
                    u"C3S statute sec. 5)"),
            description=_(
                u'You can choose any amount of shares between 1 and 60.'),
            default="1",
            widget=TextInputSliderWidget(
                size=3, css_class='num_shares_input'),
            validator=colander.Range(
                min=1,
                max=60,
                min_err=_(u'You need at least one share of 50 €.'),
                max_err=_(u'You may choose 60 shares at most (3000 €).'),
            ),
            oid="num_shares")

    def empty_message_validator(node, value):
        """
        Validator for statute confirmation.
        """
        if not value:
            # raise without additional error message as the description
            # already explains the necessity of the checkbox
            raise Invalid(node, u'')

    class TermsInfo(colander.Schema):
        """
        some legal requirements
        """

        got_statute = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            title=_(
                u'I acknowledge that the statutes and membership dues '
                u'regulations determine periodic contributions '
                u'for full members.'),
            label=_(
                u'An electronic copy of the statute of the '
                u'C3S SCE has been made available to me (see link below).'),
            description=_(
                u'You must confirm to have access to the statute.'),
            widget=deform.widget.CheckboxWidget(),
            validator=empty_message_validator,
            required=True,
            oid='got_statute',
        )
        got_dues_regulations = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            title=(u''),
            label=_(
                u'An electronic copy of the temporary membership dues '
                u'regulations of the C3S SCE has been made available to me '
                u'(see link below).'),
            description=_(
                u'You must confirm to have access to the temporary '
                u'membership dues regulations.'),
            widget=deform.widget.CheckboxWidget(),
            validator=empty_message_validator,
            required=True,
            oid='got_dues_regulations',
        )
        privacy_consent = colander.SchemaNode(
            colander.Bool(true_val=u'yes'),
            title=_(u'Privacy'),
            label=_(
                u'I hereby agree to my personal data entered in this form '
                u'being stored and processed for the purpose of membership '
                u'management. I have taken notice of the data privacy '
                u'statement. '
                u'https://www.c3s.cc/en/datenschutz/#dsgvo-membership (see '
                u'link below)'),
            widget=deform.widget.CheckboxWidget(),
            validator=empty_message_validator,
            required=True,
            oid='privacy_consent',
        )

    class MembershipForm(colander.Schema):
        """
        The Form consists of
        - Personal Data
        - Membership Information
        - Shares
        """
        # person = PersonalData(
        #     title=_(u'Personal Data'),
        # )
        person = PersonalDataJoin(
            title=_(u'Personal Data'),
        )
        membership_info = MembershipInfo(
            title=_(u'Membership Data')
        )
        shares = Shares(
            title=_(u'Shares')
        )
        acknowledge_terms = TermsInfo(
            title=_(u'Acknowledgement')
        )

    schema = MembershipForm()

    form = deform.Form(
        schema.bind(date=date),
        buttons=[
            deform.Button('reset', _(u'Reset'), type='reset'),
            deform.Button('submit', _(u'Next'))
        ],
        use_ajax=True,
        renderer=ZPT_RENDERER
    )

    # if the form has NOT been used and submitted, remove error messages if any
    if 'submit' not in request.POST:
        request.session.pop_flash()

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)

            # data sanity: if not in collecting society, don't save collsoc
            # name even if it was supplied through form
            if 'no' in appstruct['membership_info']['member_of_colsoc']:
                appstruct['membership_info']['name_of_colsoc'] = ''

        except ValidationFailure as validation_failure:
            request.session.flash(
                _(u'Please note: There were errors, '
                  u'please check the form below.'),
                'message_above_form',
                allow_duplicate=False)

            # If the validation error was not caused by the password field,
            # manually set an error to the password field because the user
            # needs to re-enter it after a validation error.
            form = validation_failure.field
            if form['person']['password'].error is None:
                form['person']['password'].error = Invalid(
                    None,
                    _(
                        u'Please re-enter your password. For security '
                        u'reasons your password is not cached and therefore '
                        u'needs to be re-entered in case of validation '
                        u'issues.'
                    ))
                validation_failure = ValidationFailure(form, None, form.error)

            return {'form': validation_failure.render()}

        appstruct['membership_info']['privacy_consent'] = datetime.now()
        request.session['appstruct'] = appstruct
        # empty the messages queue (as validation worked anyways)
        deleted_msg = request.session.pop_flash()
        del deleted_msg
        return HTTPFound(
            location=request.route_url('success'),
        )

    # if the form was submitted and gathered info shown on the success page,
    # BUT the user wants to correct their information:
    else:
        # remove annoying message from other session
        deleted_msg = request.session.pop_flash()
        del deleted_msg
        if 'appstruct' in request.session:
            appstruct = request.session['appstruct']
            # pre-fill the form with the values from last time
            form.set_appstruct(appstruct)

    html = form.render()

    return {'form': html}
Exemple #53
0
def edit_member(request):
    """
    Let staff edit a member entry.
    """
    try:
        _id = request.matchdict['_id']
        assert (isinstance(int(_id), int))
        member = C3sMember.get_by_id(_id)
        if isinstance(member, NoneType):
            return HTTPFound(request.route_url('dashboard'))
    except:
        return HTTPFound(request.route_url('dashboard'))

    # if we have a valid id, we can load a members data from the db
    # and put the data in an appstruct to fill the form
    appstruct = {}
    email_is_confirmed = 'yes' if member.email_is_confirmed else 'no'
    appstruct['person'] = {
        'firstname': member.firstname,
        'lastname': member.lastname,
        'email': member.email,
        'email_is_confirmed': email_is_confirmed,
        'address1': member.address1,
        'address2': member.address2,
        'postcode': member.postcode,
        'city': member.city,
        'country': member.country,
        'date_of_birth': member.date_of_birth,
        'locale': member.locale,
    }

    appstruct['membership_meta'] = {
        'membership_accepted':
        member.membership_accepted,
        'membership_date': (
            # this is necessary because membership_date's default is
            # 1970-01-01 which should be changed to None in the future
            u'' if member.membership_date == date(1970, 1, 1) else
            member.membership_date),
        'is_duplicate':
        member.is_duplicate,
        'is_duplicate_of':
        (u'' if member.is_duplicate_of is None else member.is_duplicate_of),
        'accountant_comment': (u'' if member.accountant_comment is None else
                               member.accountant_comment),
        'signature_received':
        member.signature_received,
        'signature_received_date':
        member.signature_received_date,
        'payment_received':
        member.payment_received,
        'payment_received_date':
        member.payment_received_date,
        'membership_loss_date':
        member.membership_loss_date,
        'membership_loss_type': (u'' if member.membership_loss_type is None
                                 else member.membership_loss_type),
    }
    appstruct['membership_info'] = {
        'membership_type': member.membership_type,
        'entity_type': u'legalentity' if member.is_legalentity else 'person',
        'member_of_colsoc': 'yes' if member.member_of_colsoc else 'no',
        'name_of_colsoc': member.name_of_colsoc,
    }
    membership_loss_types = (('', _(u'(Select)')), ('resignation',
                                                    _(u'Resignation')),
                             ('expulsion', _(u'Expulsion')),
                             ('death', _(u'Death')), ('bankruptcy',
                                                      _(u'Bankruptcy')),
                             ('winding-up', _(u'Winding-up')),
                             ('shares_transfer',
                              _(u'Transfer of remaining shares')))

    class PersonalData(colander.MappingSchema):
        """
        Colander schema of the personal data for editing member data.
        """
        firstname = colander.SchemaNode(
            colander.String(),
            title=_(u'(Real) First Name'),
            oid='firstname',
        )
        lastname = colander.SchemaNode(
            colander.String(),
            title=_(u'(Real) Last Name'),
            oid='lastname',
        )
        email = colander.SchemaNode(
            colander.String(),
            title=_(u'Email Address'),
            validator=colander.Email(),
            oid='email',
        )
        email_is_confirmed = colander.SchemaNode(
            colander.String(),
            title=_(u'Email Address Confirmed'),
            widget=deform.widget.RadioChoiceWidget(values=(
                (u'yes', _(u'Yes, confirmed')),
                (u'no', _(u'No, not confirmed')),
            )),
            missing=u'',
            oid='email_is_confirmed',
        )

        passwort = colander.SchemaNode(colander.String(),
                                       widget=deform.widget.HiddenWidget(),
                                       default='NoneSet',
                                       missing='NoneSetPurposefully')
        address1 = colander.SchemaNode(
            colander.String(),
            title=_(u'Addess Line 1'),
        )
        address2 = colander.SchemaNode(
            colander.String(),
            missing=u'',
            title=_(u'Address Line 2'),
        )
        postcode = colander.SchemaNode(colander.String(),
                                       title=_(u'Postal Code'),
                                       oid='postcode')
        city = colander.SchemaNode(
            colander.String(),
            title=_(u'City'),
            oid='city',
        )
        country = colander.SchemaNode(
            colander.String(),
            title=_(u'Country'),
            default=COUNTRY_DEFAULT,
            widget=deform.widget.SelectWidget(values=country_codes),
            oid='country',
        )
        date_of_birth = colander.SchemaNode(
            colander.Date(),
            title=_(u'Date of Birth'),
            default=date(2013, 1, 1),
            oid='date_of_birth',
        )
        locale = colander.SchemaNode(
            colander.String(),
            title=_(u'Locale'),
            widget=deform.widget.SelectWidget(values=locale_codes),
            missing=u'',
        )

    @colander.deferred
    def membership_loss_date_widget(node, keywords):
        """
        Returns a text or hidden input depending on the value of
        membership_accepted within the keywords.
        """
        if keywords.get('membership_accepted'):
            return deform.widget.TextInputWidget()
        else:
            return deform.widget.HiddenWidget()

    @colander.deferred
    def membership_loss_type_widget(node, keywords):
        """
        Returns a select or hidden input depending on the value of
        membership_accepted within the keywords.
        """
        if keywords.get('membership_accepted'):
            return deform.widget.SelectWidget(values=membership_loss_types)
        else:
            return deform.widget.HiddenWidget()

    class MembershipMeta(colander.Schema):
        """
        Colander schema of the meta data for editing member data.
        """
        membership_accepted = colander.SchemaNode(
            colander.Boolean(), title=_(u'Membership Accepted'))
        membership_date = colander.SchemaNode(
            colander.Date(),
            title=_(u'Membership Acceptance Date'),
            validator=Range(
                min=date(2013, 9, 24),
                max=date.today(),
                min_err=_(u'${val} is earlier than earliest date ${min}.'),
                max_err=_(u'${val} is later than latest date ${max}.')),
            missing=date(1970, 1, 1),
            oid='membership_date',
        )
        is_duplicate = colander.SchemaNode(
            colander.Boolean(),
            title=_(u'Is Duplicate'),
            oid='is_duplicate',
        )
        is_duplicate_of = colander.SchemaNode(
            colander.String(),
            title=_(u'Duplicate Id'),
            missing=u'',
            oid='duplicate_of',
        )
        signature_received = colander.SchemaNode(
            colander.Boolean(),
            title=_(u'Signature Received'),
            oid='signature_received',
        )
        signature_received_date = colander.SchemaNode(
            colander.Date(),
            title=_('Signature Receipt Date'),
            validator=Range(
                min=date(1070, 1, 1),
                max=date.today(),
                min_err=_(u'${val} is earlier than earliest date ${min}.'),
                max_err=_(u'${val} is later than latest date ${max}.')),
            missing=date(1970, 1, 1),
        )
        payment_received = colander.SchemaNode(
            colander.Boolean(),
            title=_(u'Payment Received'),
        )
        payment_received_date = colander.SchemaNode(
            colander.Date(),
            title=_(u'Payment Receipt Date'),
            validator=Range(
                min=date(1970, 1, 1),
                max=date.today(),
                min_err=_(u'${val} is earlier than earliest date ${min}.'),
                max_err=_(u'${val} is later than latest date ${max}.')),
            missing=date(1970, 1, 1),
            oid='_received_date',
        )
        membership_loss_date = colander.SchemaNode(
            colander.Date(),
            widget=membership_loss_date_widget,
            title=_(u'Date of the loss of membership'),
            default=None,
            missing=None,
            oid='membership_loss_date',
        )
        membership_loss_type = colander.SchemaNode(
            colander.String(),
            widget=membership_loss_type_widget,
            title=_(u'Type of membership loss'),
            default=None,
            missing=None,
            oid='membership_loss_type',
        )
        accountant_comment = colander.SchemaNode(
            colander.String(),
            title=_(u'Staff Comment: (255 letters)'),
            missing=u'',
            oid='accountant_comment',
        )

    class MembershipInfo(colander.Schema):
        """
        Colander schema of the additional data for editing member data.
        """
        yes_no = (
            (u'yes', _(u'Yes')),
            (u'no', _(u'No')),
            (u'dontknow', _(u'Unknwon')),
        )

        entity_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Member Category'),
            description=_(u'Please choose the member category.'),
            widget=deform.widget.RadioChoiceWidget(values=(
                (u'person', _(u'Person')),
                (u'legalentity', _(u'Legal Entity')),
            ), ),
            missing=u'',
            oid='entity_type',
        )
        membership_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Type of Membership (C3S Statute § 4)'),
            description=_(u'Please choose the type of membership.'),
            widget=deform.widget.RadioChoiceWidget(values=(
                (u'normal', _(u'Member')),
                (u'investing', _(u'Investing (non-user) member')),
                (u'unknown', _(u'Unknown')),
            ), ),
            missing=u'',
            oid='membership_type',
        )
        member_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_('Member of a Collecting Society'),
            widget=deform.widget.RadioChoiceWidget(values=yes_no),
            oid='other_colsoc',
            default=u'',
            missing=u'',
        )
        name_of_colsoc = colander.SchemaNode(
            colander.String(),
            title=_(u'Names of Collecting Societies'),
            description=_(u'Please separate multiple collecting societies by '
                          u'comma.'),
            missing=u'',
            oid='colsoc_name',
        )

    def loss_type_and_date_set_validator(form, value):
        """
        Validates whether the membership loss type is set.

        Membership date and type must both be either set or unset.
        """
        if (value['membership_loss_date'] is None) != \
                (value['membership_loss_type'] is None):
            exc = colander.Invalid(form)
            exc['membership_loss_type'] = \
                _(u'Date and type of membership loss must be set both or '
                  u'none.')
            exc['membership_loss_date'] = \
                _(u'Date and type of membership loss must be set both or '
                  u'none.')
            raise exc

    def loss_date_larger_acceptance_validator(form, value):
        """
        Validates that the membership loss date is not smaller than the
        membership acceptance date.

        As the membership can't be lost before it was granted the membership
        loss date must be larger than the membership acceptance date.
        """
        if (value['membership_loss_date'] is not None
                and (value['membership_loss_date'] < value['membership_date']
                     or not value['membership_accepted'])):
            exc = colander.Invalid(form)
            exc['membership_loss_date'] = \
                _(u'Date membership loss must be larger than membership '
                  u'acceptance date.')
            raise exc

    def loss_date_resignation_validator(form, value):
        """
        Validates that the membership loss date for resignations is the 31st
        of December of any year.

        Resignations are only allowed to the end of the year.
        """
        if (value.get('membership_loss_type', '') == 'resignation'
                and value['membership_loss_date'] is not None
                and not (value['membership_loss_date'].day == 31
                         and value['membership_loss_date'].month == 12)):
            exc = colander.Invalid(form)
            exc['membership_loss_date'] = \
                _(u'Resignations are only allowed to the 31st of December '
                  u'of a year.')
            raise exc

    class MembershipForm(colander.Schema):
        """
        The form for editing membership information combining all forms for
        the subject areas.
        """
        person = PersonalData(title=_(u'Personal Data'), )
        membership_meta = MembershipMeta(
            title=_(u'Membership Bureaucracy'),
            validator=colander.All(
                loss_type_and_date_set_validator,
                loss_date_larger_acceptance_validator,
                loss_date_resignation_validator)).bind(
                    membership_accepted=member.membership_accepted, )
        membership_info = MembershipInfo(title=_(u'Membership Requirements'))

    def membership_loss_type_entity_type_validator(form, value):
        """
        Validates that only natural persons can have loss type 'death' and
        only legal entites 'winding-up'.
        """
        if (value['membership_meta']['membership_loss_type'] == 'death'
                and value['membership_info']['entity_type'] != 'person'):
            exc_type = colander.Invalid(
                form['membership_meta']['membership_loss_type'],
                _(u'The membership loss type \'death\' is only allowed for '
                  u'natural person members and not for legal entity members.'))
            exc_meta = colander.Invalid(form['membership_meta'])
            exc_meta.add(
                exc_type,
                get_child_position(
                    form['membership_meta'],
                    form['membership_meta']['membership_loss_type']))
            exc = colander.Invalid(form)
            exc.add(exc_meta, get_child_position(form,
                                                 form['membership_meta']))
            raise exc
        if (value['membership_meta']['membership_loss_type'] == 'winding-up'
                and value['membership_info']['entity_type'] != 'legalentity'):
            exc_type = colander.Invalid(
                form['membership_meta']['membership_loss_type'],
                _(u'The membership loss type \'winding-up\' is only allowed '
                  u'for legal entity members and not for natural person '
                  u'members.'))
            exc_meta = colander.Invalid(form['membership_meta'])
            exc_meta.add(
                exc_type,
                get_child_position(
                    form['membership_meta'],
                    form['membership_meta']['membership_loss_type']))
            exc = colander.Invalid(form)
            exc.add(exc_meta, get_child_position(form,
                                                 form['membership_meta']))
            raise exc

    schema = MembershipForm(
        validator=colander.All(membership_loss_type_entity_type_validator, ))
    form = deform.Form(
        schema,
        buttons=[
            deform.Button('submit', _(u'Submit')),
            deform.Button('reset', _(u'Reset')),
        ],
        renderer=ZPT_RENDERER,
        use_ajax=True,
    )

    def clean_error_messages(error):
        if error.msg is not None and type(error.msg) == list:
            error.msg = list(set(error.msg))
            if None in error.msg:
                error.msg.remove(None)
            if '' in error.msg:
                error.msg.remove('')
            error.msg = ' '.join(list(set(error.msg)))
        for child in error.children:
            clean_error_messages(child)

    # if the form has NOT been used and submitted, remove error messages if
    # any
    if 'submit' not in request.POST:
        request.session.pop_flash()

    # if the form has been used and SUBMITTED, check contents
    if 'submit' in request.POST:
        controls = request.POST.items()
        try:
            appstruct = form.validate(controls)
        except ValidationFailure as validationfailure:
            clean_error_messages(validationfailure.error)
            request.session.flash(_(u'Please note: There were errors, '
                                    u'please check the form below.'),
                                  'message_above_form',
                                  allow_duplicate=False)
            return {'form': validationfailure.render()}

        # to store the data in the DB, the old object is updated
        listing = [  # map data attributes to appstruct items
            ('firstname', appstruct['person']['firstname']),
            ('lastname', appstruct['person']['lastname']),
            ('date_of_birth', appstruct['person']['date_of_birth']),
            ('email', appstruct['person']['email']),
            ('email_is_confirmed',
             1 if appstruct['person']['email_is_confirmed'] == 'yes' else 0),
            ('address1', appstruct['person']['address1']),
            ('address2', appstruct['person']['address2']),
            ('postcode', appstruct['person']['postcode']),
            ('city', appstruct['person']['city']),
            ('country', appstruct['person']['country']),
            ('locale', appstruct['person']['locale']),
            ('membership_date',
             appstruct['membership_meta']['membership_date']),
            ('is_duplicate', appstruct['membership_meta']['is_duplicate']),
            ('is_duplicate_of',
             appstruct['membership_meta']['is_duplicate_of']),
            ('accountant_comment',
             appstruct['membership_meta']['accountant_comment']),
            ('membership_type',
             appstruct['membership_info']['membership_type']),
            ('is_legalentity', 1 if
             (appstruct['membership_info']['entity_type']
              == 'legalentity') else 0),
            ('name_of_colsoc', appstruct['membership_info']['name_of_colsoc']),
            ('signature_received',
             appstruct['membership_meta']['signature_received']),
            ('signature_received_date',
             appstruct['membership_meta']['signature_received_date']),
            ('payment_received',
             appstruct['membership_meta']['payment_received']),
            ('payment_received_date',
             appstruct['membership_meta']['payment_received_date']),
            ('membership_loss_type',
             appstruct['membership_meta'].get('membership_loss_type', None)),
            ('membership_loss_date',
             appstruct['membership_meta'].get('membership_loss_date', None)),
        ]

        for thing in listing:
            attribute_name = thing[0]
            attribute_value = thing[1]

            if member.__getattribute__(attribute_name) == attribute_value:
                pass
            else:
                LOG.info(u'{0} changes {1} of id {2} to {3}'.format(
                    authenticated_userid(request), attribute_name, member.id,
                    attribute_value))
                setattr(member, attribute_name, attribute_value)

        # membership acceptance status can be set or unset.
        if appstruct['membership_meta'][
                'membership_accepted'] == member.membership_accepted:
            pass
        else:
            member.membership_accepted = appstruct['membership_meta'][
                'membership_accepted']
            if isinstance(member.membership_number, NoneType) \
                    and member.membership_accepted:
                member.membership_number = \
                    C3sMember.get_next_free_membership_number()

        if appstruct['membership_info']['entity_type'] == 'legalentity':
            member.is_legalentity = True
        else:
            member.is_legalentity = False

        # empty the messages queue (as validation worked anyways)
        deleted_msg = request.session.pop_flash()
        del deleted_msg
        return HTTPFound(  # redirect to details page
            location=request.route_url('detail', memberid=member.id), )

    form.set_appstruct(appstruct)
    html = form.render()

    return {'form': html}