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
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' )))
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
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())
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
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)
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")
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 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)
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)
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_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 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") )
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 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'))
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].'))
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'))
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 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)) ] ) )
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
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
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
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", )
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}
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}
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." }
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': [], }
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': [], }
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"]
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
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')),
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']
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', )
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'))
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
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}
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')),
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)
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." }
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}
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}