class LabelForm(forms.Form): name = forms.StringField( "", widget=forms.HiddenInput(), validators=[forms.validators.Optional()] ) title = forms.StringField( __("Label"), validators=[ forms.validators.DataRequired(__("This can’t be empty")), forms.validators.Length(max=250), ], filters=[forms.filters.strip()], ) icon_emoji = forms.StringField( "", validators=[forms.validators.Optional(), forms.validators.IsEmoji()] ) required = forms.BooleanField( __("Make this label mandatory in proposal forms"), default=False, description=__("If checked, proposers must select one of the options"), ) restricted = forms.BooleanField( __("Restrict use of this label to editors"), default=False, description=__( "If checked, only editors and reviewers can apply this label on proposals" ), )
class ProjectCrewMembershipForm(forms.Form): # add a member to a project user = forms.UserSelectField( __("User"), validators=[forms.validators.DataRequired(_(u"Please select a user"))], description=__("Find a user by their name or email address"), ) is_editor = forms.BooleanField( __("Editor"), default=False, description=__( "Can edit project details, proposal guidelines, schedule, labels and venues" ), ) is_concierge = forms.BooleanField( __("Concierge"), default=False, description=__("Can manage participants and see contact info"), ) is_usher = forms.BooleanField( __("Usher"), default=False, description=__( "Can check-in a participant using their badge at a physical event" ), ) def validate(self, extra_validators=None): is_valid = super(ProjectCrewMembershipForm, self).validate(extra_validators) if not any( [self.is_editor.data, self.is_concierge.data, self.is_usher.data]): self.is_usher.errors.append("Please select one or more roles") is_valid = False return is_valid
class BoardOptionsForm(forms.Form): restrict_listing = forms.BooleanField(__(u"Restrict direct posting on this board to owners and the following users"), default=True, description=__(u"As the owner of this board, you can always cross-add jobs from other boards on Hasjob")) posting_users = forms.UserSelectMultiField(__(u"Allowed users"), description=__(u"These users will be allowed to post jobs on this board under the following terms"), usermodel=User, lastuser=lastuser) # Allow turning this off only in siteadmin-approved boards (deleted in the view for non-siteadmins) require_pay = forms.BooleanField(__(u"Require pay data for posting on this board?"), default=True, description=__(u"Hasjob requires employers to reveal what they intend to pay, " u"but you can make it optional for jobs posted from this board. " u"Pay data is used to match candidates to jobs. We recommend you collect it")) newjob_headline = forms.StringField(__(u"Headline"), description=__(u"Optional – The sample headline shown to employers when posting a job"), validators=[ forms.validators.Length(min=0, max=100, message=__("%%(max)d characters maximum"))], filters=[forms.filters.strip(), forms.filters.none_if_empty()]) newjob_blurb = forms.TinyMce4Field(__(u"Posting instructions"), description=__(u"Optional – What should we tell employers when they post a job on your board? " u"Leave blank to use the default text"), content_css=content_css, validators=[forms.validators.AllUrlsValid()]) types = QuerySelectMultipleField(__("Job types"), widget=ListWidget(), option_widget=CheckboxInput(), query_factory=lambda: JobType.query.filter_by(private=False).order_by(JobType.seq), get_label=jobtype_label, validators=[forms.validators.DataRequired(__(u"You need to select at least one job type"))], description=__(u"Jobs listed directly on this board can use one of the types enabled here")) categories = QuerySelectMultipleField(__("Job categories"), widget=ListWidget(), option_widget=CheckboxInput(), query_factory=lambda: JobCategory.query.filter_by(private=False).order_by(JobCategory.seq), get_label='title', validators=[forms.validators.DataRequired(__(u"You need to select at least one category"))], description=__(u"Jobs listed directly on this board can use one of the categories enabled here"))
class ResourceForm(forms.Form): """ Edit a resource provided by an application """ name = forms.StringField(__("Resource name"), validators=[forms.validators.DataRequired()], description=__("Name of the resource as a single word in lower case. " "This is provided by applications as part of the scope " "when requesting access to a user's resources"), widget_attrs={'autocorrect': 'none', 'autocapitalize': 'none'}) title = forms.StringField(__("Title"), validators=[forms.validators.DataRequired()], description=__("Resource title that is displayed to users")) description = forms.TextAreaField(__("Description"), description=__("An optional description of what the resource is")) siteresource = forms.BooleanField(__("Site resource"), description=__("Enable if this resource is generic to the site and not owned by specific users")) restricted = forms.BooleanField(__("Restrict access to your apps"), description=__("Enable if access to the resource should be restricted to client apps " "that share the same owner. You may want to do this for sensitive resources " "that should only be available to your own apps")) def validate_name(self, field): field.data = field.data.lower() if not valid_username(field.data): raise forms.ValidationError(_("Name contains invalid characters")) if field.data in resource_registry: raise forms.ValidationError(_("This name is reserved for internal use")) existing = Resource.get(name=field.data, client=self.client) if existing and existing.id != self.edit_id: raise forms.ValidationError(_("A resource with that name already exists"))
class SessionForm(forms.Form): title = forms.StringField( __("Title"), validators=[forms.validators.DataRequired()], filters=[forms.filters.strip()], ) venue_room_id = forms.SelectField(__("Room"), choices=[], coerce=nullint, validators=[forms.validators.Optional()]) description = forms.MarkdownField(__("Description"), validators=[forms.validators.Optional()]) speaker = forms.StringField( __("Speaker"), validators=[ forms.validators.Optional(), forms.validators.Length(max=200) ], filters=[forms.filters.strip()], ) speaker_bio = forms.MarkdownField(__("Speaker bio"), validators=[forms.validators.Optional()]) banner_image_url = forms.URLField( __("Banner image URL"), description=__("From images.hasgeek.com, with 16:9 aspect ratio." " Should be < 50 kB in size"), validators=[ forms.validators.Optional(), forms.validators.Length(max=2000), image_url_validator(), ], ) is_break = forms.BooleanField(__("This session is a break period"), default=False) featured = forms.BooleanField(__("This is a featured session"), default=False) start_at = forms.HiddenField(__("Start Time"), validators=[forms.validators.DataRequired()]) end_at = forms.HiddenField(__("End Time"), validators=[forms.validators.DataRequired()]) video_url = forms.StringField( __("Video URL"), description=__("URL of the uploaded video after the session is over"), validators=[ forms.validators.Optional(), forms.validators.ValidUrl(), forms.validators.Length(max=2000), ], )
class ConfirmForm(forms.Form): terms_accepted = forms.BooleanField( "I accept the terms of service", validators=[ forms.validators.DataRequired( "You must accept the terms of service to publish this post") ])
class WithdrawForm(forms.Form): really_withdraw = forms.BooleanField( __("Yes, I really want to withdraw the job post"), validators=[ forms.validators.DataRequired(__("You must confirm withdrawal")) ], )
class CampaignForm(forms.Form): title = forms.StringField( __("Title"), description=__("A reference name for looking up this campaign again"), validators=[ forms.validators.DataRequired(__("A title is required")), forms.validators.StripWhitespace() ]) start_at = forms.DateTimeField(__("Start at"), timezone=lambda: g.user.timezone if g.user else None) end_at = forms.DateTimeField(__("End at"), timezone=lambda: g.user.timezone if g.user else None) public = forms.BooleanField(__("This campaign is live")) position = forms.RadioField(__("Display position"), choices=CAMPAIGN_POSITION.items(), coerce=int) priority = forms.IntegerField( __("Priority"), default=0, description=__( "A larger number is higher priority when multiple campaigns are running on the " "same dates. 0 implies lowest priority")) boards = QuerySelectMultipleField( __("Boards"), widget=ListWidget(), option_widget=CheckboxInput(), query_factory=lambda: Board.query.order_by('title'), get_label='title', validators=[forms.validators.Optional()], description=__(u"Select the boards this campaign is active on")) geonameids = forms.GeonameSelectMultiField( "Locations", description=__( "This campaign will be targetted at users and jobs with matching locations" )) user_required = forms.RadioField(__("User is required"), coerce=getbool, choices=[(None, __("N/A")), (True, __("Yes")), (False, __("No"))]) flags = forms.RadioMatrixField( "Flags", coerce=getbool, fields=Campaign.flag_choices, description=__( "All selected flags must match the logged in user for the campaign to be shown" ), choices=[('None', __("N/A")), ('True', __("True")), ('False', __("False"))]) content = forms.FormField(CampaignContentForm, __("Campaign content")) def validate_geonameids(self, field): field.data = [int(x) for x in field.data if x.isdigit()] def validate_end_at(self, field): if field.data <= self.start_at.data: raise forms.ValidationError( __(u"The campaign can’t end before it starts"))
class UpdateForm(forms.Form): title = forms.StringField( __("Title"), validators=[forms.validators.DataRequired()], filters=[forms.filters.strip()], ) body = forms.MarkdownField( __("Content"), validators=[forms.validators.DataRequired()], description=__("Markdown formatting is supported"), ) is_pinned = forms.BooleanField( __("Pin this update above other updates"), default=False ) is_restricted = forms.BooleanField( __("Limit visibility to participants only"), default=False )
class WithdrawForm(forms.Form): really_withdraw = forms.BooleanField( "Yes, I really want to withdraw the job post", validators=[ forms.validators.DataRequired( u"If you don’t want to withdraw the post, just close this page" ) ])
class ProposalSpaceForm(forms.Form): name = forms.StringField(__("URL name"), validators=[forms.validators.DataRequired(), forms.ValidName(), AvailableName()]) title = forms.StringField(__("Title"), validators=[forms.validators.DataRequired()]) datelocation = forms.StringField(__("Date and Location"), validators=[forms.validators.DataRequired(), forms.validators.Length(max=50)]) date = forms.DateField(__("Start date (for sorting)"), validators=[forms.validators.DataRequired(__("Enter a valid date in YYYY-MM-DD format"))]) date_upto = forms.DateField(__("End date (for sorting)"), validators=[forms.validators.DataRequired(__("Enter a valid date in YYYY-MM-DD format"))]) tagline = forms.StringField(__("Tagline"), validators=[forms.validators.DataRequired()], description=__("This is displayed on the card on the homepage")) website = forms.URLField(__("Website"), validators=[forms.validators.Optional()]) description = forms.MarkdownField(__("Description"), validators=[forms.validators.DataRequired()], description=__("About Event")) timezone = forms.SelectField(__("Timezone"), description=__("The timezone in which this event occurs"), validators=[forms.validators.DataRequired()], choices=sorted_timezones(), default=u'UTC') bg_image = forms.URLField(__("Background image URL"), description=u"Background image for the mobile app", validators=[forms.validators.Optional()]) bg_color = forms.StringField(__("Background color"), description=__("RGB color for the event, shown on the mobile app. Enter without the '#'. E.g. CCCCCC."), validators=[forms.validators.Optional(), forms.validators.Length(max=6)], default=u"CCCCCC") explore_url = forms.URLField(__("Explore tab URL"), description=__(u"Page containing the explore tab’s contents, for the mobile app"), validators=[forms.validators.Optional()]) parent_space = QuerySelectField(__(u"Parent space"), get_label='title', allow_blank=True, blank_text=__(u"None")) status = forms.SelectField(__("Status"), coerce=int, choices=[ (0, __("Draft")), (1, __("Open")), (2, __("Voting")), (3, __("Jury selection")), (4, __("Feedback")), (5, __("Closed")), (6, __("Withdrawn")), ], description=__(u"Proposals can only be submitted in the “Open” state. " u"“Closed” and “Withdrawn” are hidden from homepage")) admin_team = QuerySelectField(u"Admin Team", validators=[forms.validators.DataRequired(__(u"Please select a team"))], query_factory=profile_teams, get_label='title', allow_blank=False, description=__(u"The administrators of this proposal space")) review_team = QuerySelectField(u"Review Team", validators=[forms.validators.DataRequired(__(u"Please select a team"))], query_factory=profile_teams, get_label='title', allow_blank=False, description=__(u"Reviewers can see contact details of proposers, but can’t change settings")) allow_rsvp = forms.BooleanField(__("Allow site visitors to RSVP (login required)")) buy_tickets_url = forms.URLField(__("URL to buy tickets"), description=__(u"Eg: Explara, Instamojo"), validators=[forms.validators.Optional()]) def validate_date_upto(self, date_upto): if self.date_upto.data < self.date.data: raise forms.ValidationError(_("End date cannot be before start date")) def validate_bg_color(self, field): if not valid_color_re.match(field.data): raise forms.ValidationError("Please enter a valid color code")
class SavedProjectForm(forms.Form): save = forms.BooleanField( __("Save this project?"), validators=[forms.validators.InputRequired()] ) description = forms.StringField( __("Note to self"), validators=[forms.validators.Optional()], filters=[forms.filters.strip()], )
class SessionForm(forms.Form): title = forms.StringField(__("Title"), validators=[forms.validators.DataRequired()]) venue_room_id = forms.SelectField(__("Room"), choices=[], coerce=nullint, validators=[forms.validators.Optional()]) description = forms.MarkdownField(__("Description"), validators=[forms.validators.Optional()]) speaker = forms.StringField(__("Speaker"), validators=[forms.validators.Optional()]) speaker_bio = forms.MarkdownField(__("Speaker bio"), validators=[forms.validators.Optional()]) is_break = forms.BooleanField(__("This session is a break period"), default=False) start = forms.HiddenField(__("Start Time"), validators=[forms.validators.DataRequired()]) end = forms.HiddenField(__("End Time"), validators=[forms.validators.DataRequired()])
class ApplicationForm(forms.Form): apply_email = forms.RadioField(__("Email"), validators=[forms.validators.DataRequired(__("Pick an email address"))], description=__("Add new email addresses from your profile")) apply_phone = forms.StringField(__("Phone"), validators=[forms.validators.DataRequired(__("Specify a phone number")), forms.validators.Length(min=1, max=15, message=__("%%(max)d characters maximum"))], filters=[forms.filters.strip()], description=__("A phone number the employer can reach you at")) apply_message = forms.TinyMce4Field(__("Job application"), content_css=content_css, validators=[forms.validators.DataRequired(__("You need to say something about yourself")), forms.validators.AllUrlsValid()], description=__(u"Please provide all details the employer has requested. To add a resume, " u"post it on LinkedIn or host the file on Dropbox and insert the link here")) apply_optin = forms.BooleanField(__("Optional: sign me up for a better Hasjob experience"), description=__(u"Hasjob’s maintainers may contact you about new features and can see this application for reference")) def __init__(self, *args, **kwargs): super(ApplicationForm, self).__init__(*args, **kwargs) self.apply_email.choices = [] if g.user: self.apply_email.description = Markup( _(u'Add new email addresses from <a href="{}" target="_blank">your profile</a>').format( g.user.profile_url)) try: self.apply_email.choices = [(e, e) for e in lastuser.user_emails(g.user)] except LastuserResourceException: self.apply_email.choices = [(g.user.email, g.user.email)] # If choices is [] or [(None, None)] if not self.apply_email.choices or not self.apply_email.choices[0][0]: self.apply_email.choices = [ ('', Markup(_("<em>You have not verified your email address</em>"))) ] def validate_apply_message(form, field): words = get_word_bag(field.data) form.words = words similar = False for oldapp in JobApplication.query.filter(JobApplication.response.SPAM).all(): if oldapp.words: s = SequenceMatcher(None, words, oldapp.words) if s.ratio() > 0.8: similar = True break if similar: raise forms.ValidationError(_("Your application is very similar to one previously identified as spam")) # Check for email and phone numbers in the message # Prepare text by replacing non-breaking spaces with spaces (for phone numbers) and removing URLs. # URLs may contain numbers that are not phone numbers. phone_search_text = URL_RE.sub('', field.data.replace(' ', ' ').replace(' ', ' ').replace(u'\xa0', ' ')) if EMAIL_RE.search(field.data) is not None or PHONE_DETECT_RE.search(phone_search_text) is not None: raise forms.ValidationError(_("Do not include your email address or phone number in the application"))
class NewPhoneForm(forms.RecaptchaForm): phone = forms.TelField( __("Phone number"), validators=[forms.validators.DataRequired()], description=__( "Mobile numbers only, in Indian or international format"), ) # Temporarily removed since we only support mobile numbers at this time. When phone # call validation is added, we can ask for other types of numbers: # type = forms.RadioField(__("Type"), coerce=nullstr, # validators=[forms.validators.Optional()], # choices=[ # (__(u"Mobile"), __(u"Mobile")), # (__(u"Home"), __(u"Home")), # (__(u"Work"), __(u"Work")), # (__(u"Other"), __(u"Other"))]) enable_notifications = forms.BooleanField( __("Send notifications by SMS"), description=__( "Unsubscribe anytime, and control what notifications are sent from the" " Notifications tab under account settings"), default=True, ) def validate_phone(self, field): # Step 1: Validate number try: # Assume Indian number if no country code is specified # TODO: Guess country from IP address parsed_number = phonenumbers.parse(field.data, 'IN') if not phonenumbers.is_valid_number(parsed_number): raise ValueError("Invalid number") except (phonenumbers.NumberParseException, ValueError): raise forms.StopValidation( _("This does not appear to be a valid phone number")) number = phonenumbers.format_number( parsed_number, phonenumbers.PhoneNumberFormat.E164) # Step 2: Check if number has already been claimed existing = UserPhone.get(phone=number) if existing is not None: if existing.user == current_auth.user: raise forms.ValidationError( _("You have already registered this phone number")) else: raise forms.ValidationError( _("This phone number has already been claimed")) existing = UserPhoneClaim.get_for(user=current_auth.user, phone=number) if existing is not None: raise forms.ValidationError( _("This phone number is pending verification")) # Step 3: If validations pass, use the reformatted number field.data = number # Save stripped number
class FiltersetForm(forms.Form): title = forms.StringField(__("Title"), description=__("A title shown to viewers"), validators=[forms.validators.DataRequired()], filters=[forms.filters.strip()]) description = forms.TinyMce4Field( __("Description"), content_css=content_css, description=__("Description shown to viewers and search engines"), validators=[forms.validators.DataRequired()]) types = QuerySelectMultipleField(__("Job types"), widget=ListWidget(), option_widget=CheckboxInput(), get_label='title', validators=[forms.validators.Optional()]) categories = QuerySelectMultipleField( __("Job categories"), widget=ListWidget(), option_widget=CheckboxInput(), get_label='title', validators=[forms.validators.Optional()]) geonameids = forms.GeonameSelectMultiField("Locations", filters=[format_geonameids]) remote_location = forms.BooleanField(__("Match remote jobs")) pay_cash_currency = forms.RadioField( __("Currency"), choices=get_currency_choices(), default='', validators=[forms.validators.Optional()]) pay_cash = forms.IntegerField(__("Pay"), description=__("Minimum pay"), validators=[forms.validators.Optional()]) keywords = forms.StringField(__("Keywords"), validators=[forms.validators.Optional()], filters=[forms.filters.strip()]) auto_domains = forms.AutocompleteMultipleField( __("Domains"), autocomplete_endpoint='/api/1/domain/autocomplete', results_key='domains') auto_tags = forms.AutocompleteMultipleField( __("Tags"), autocomplete_endpoint='/api/1/tag/autocomplete', results_key='tags') def set_queries(self): if not self.edit_parent: self.edit_parent = g.board self.types.query = JobType.query.join(board_jobtype_table).filter( board_jobtype_table.c.board_id == self.edit_parent.id).order_by( 'title') self.categories.query = JobCategory.query.join( board_jobcategory_table).filter( board_jobcategory_table.c.board_id == self.edit_parent.id).order_by('title')
class ItemForm(forms.Form): title = forms.StringField(__("Item title"), validators=[forms.validators.DataRequired(__("Please specify a title")), forms.validators.Length(max=250)], filters=[forms.filters.strip()]) description = forms.TextAreaField(__("Description"), filters=[format_description], validators=[forms.validators.DataRequired(__("Please specify a description"))]) restricted_entry = forms.BooleanField(__("Restrict entry?")) seq = forms.IntegerField(__("Sequence"), description=__("The sequence of the ticket on the listing"), validators=[forms.validators.DataRequired(__("Please specify the sequence order"))]) category = QuerySelectField(__("Category"), get_label='title', validators=[forms.validators.DataRequired(__("Please select a category"))]) quantity_total = forms.IntegerField(__("Quantity available"), validators=[forms.validators.DataRequired(__("Please specify the quantity available for sale"))]) assignee_details = forms.TextAreaField(__("Assignee details"), filters=[format_json], validators=[validate_json], default=ASSIGNEE_DETAILS_PLACEHOLDER) event_date = forms.DateField(__("Event date"), description=__("The date on which this item will be invoiced"), validators=[forms.validators.DataRequired(__("Please specify a date for the event"))]) transferable_until = forms.DateTimeField(__("Transferable until"), validators=[forms.validators.Optional()], naive=False) cancellable_until = forms.DateTimeField(__("Cancellable until"), validators=[forms.validators.Optional()], naive=False) place_supply_state_code = forms.SelectField(__("State"), description=__("State of supply"), coerce=int, default=indian_states_dict['KA']['short_code'], validators=[forms.validators.DataRequired(__("Please select a state"))]) place_supply_country_code = forms.SelectField(__("Country"), description=__("Country of supply"), default='IN', validators=[forms.validators.DataRequired(__("Please select a country"))]) def set_queries(self): self.place_supply_state_code.choices = [(0, '')] + [(state['short_code'], state['name']) for state in sorted(indian_states, key=lambda k: k['name'])] self.place_supply_country_code.choices = [('', '')] + localized_country_list() self.category.query = Category.query.join(ItemCollection).filter( Category.item_collection == self.edit_parent).options(db.load_only('id', 'title')) def validate_place_supply_state_code(self, field): if field.data <= 0: # state short codes start from 1, # and 0 means empty value as mentioned above in set_queries raise forms.ValidationError(__("Please select a state")) def validate_transferable_until(self, field): if field.data and field.data.date() > self.event_date.data: raise forms.ValidationError(__("Ticket transfer deadline cannot be after event date"))
class TeamForm(forms.Form): title = forms.StringField( __("Team name"), validators=[ forms.validators.DataRequired(), forms.validators.Length(max=Team.__title_length__), ], ) users = forms.UserSelectMultiField( __("Users"), validators=[forms.validators.DataRequired()], description=__("Lookup a user by their username or email address"), ) is_public = forms.BooleanField( __("Make this team public"), description=__( "Team members will be listed on the organization’s profile page"), default=True, )
class BoardTaggingForm(forms.Form): auto_domains = forms.TextListField(__("Email Domains"), description=__("Jobs from any of these email domains will be automatically added to this board. " "One per line. Do NOT add the www prefix")) auto_geonameids = forms.GeonameSelectMultiField(__("Locations"), description=__("Jobs in any of these locations will be automatically added to this board")) auto_keywords = forms.AutocompleteMultipleField(__("Tags"), autocomplete_endpoint='/api/1/tag/autocomplete', results_key='tags', description=__("Jobs tagged with these keywords will be automatically added to this board")) auto_types = QuerySelectMultipleField(__("Job types"), query_factory=lambda: JobType.query.filter_by(private=False).order_by(JobType.seq), get_label='title', description=__(u"Jobs of this type will be automatically added to this board")) auto_categories = QuerySelectMultipleField(__("Job categories"), query_factory=lambda: JobCategory.query.filter_by(private=False).order_by(JobCategory.seq), get_label='title', description=__(u"Jobs of this category will be automatically added to this board")) auto_all = forms.BooleanField(__("All of the above criteria must match"), description=__(u"Select this if, for example, you want to match all programming jobs in Bangalore " u"and not all programming jobs or Bangalore-based jobs.")) def validate_auto_domains(self, field): relist = [] for item in field.data: item = item.strip() if u',' in item: relist.extend([x.strip() for x in item.split(',')]) elif u' ' in item: relist.extend([x.strip() for x in item.split(' ')]) else: relist.append(item) domains = set() for item in relist: if item: # FIXME: This will break domains where the subdomain handles email r = tldextract.extract(item.lower()) d = u'.'.join([r.domain, r.suffix]) if not is_public_email_domain(d, default=False): domains.add(d) field.data = list(domains) def validate_auto_geonameids(self, field): field.data = [int(x) for x in field.data if x.isdigit()]
class BoardForm(forms.Form): """ Edit board settings. """ title = forms.StringField(__("Title"), validators=[ forms.validators.DataRequired(__("The board needs a name")), forms.validators.Length(min=1, max=80, message=__("%%(max)d characters maximum"))], filters=[forms.filters.strip()]) caption = forms.StringField(__("Caption"), description=__("The title and caption appear at the top of the page. Keep them concise"), validators=[ forms.validators.Optional(), forms.validators.Length(min=0, max=80, message=__("%%(max)d characters maximum"))], filters=[forms.filters.strip(), forms.filters.none_if_empty()]) name = forms.AnnotatedTextField(__("URL Name"), prefix='https://', suffix=u'.hasjob.co', description=__(u"Optional — Will be autogenerated if blank"), validators=[ forms.validators.ValidName(), forms.validators.Length(min=0, max=63, message=__("%%(max)d characters maximum")), AvailableName(__(u"This name has been taken by another board"), model=Board)]) description = forms.TinyMce4Field(__(u"Description"), description=__(u"The description appears at the top of the board, above all jobs. " u"Use it to introduce your board and keep it brief"), content_css=content_css, validators=[forms.validators.DataRequired(__("A description of the job board is required")), forms.validators.AllUrlsValid()]) userid = forms.SelectField(__(u"Owner"), validators=[forms.validators.DataRequired(__("Select an owner"))], description=__(u"Select the user, organization or team who owns this board. " "Owners can add jobs to the board and edit these settings")) require_login = forms.BooleanField(__(u"Prompt users to login"), default=True, description=__(u"If checked, users must login to see all jobs available. " u"Logging in provides users better filtering for jobs that may be of interest to them, " u"and allows employers to understand how well their post is performing")) options = forms.FormField(BoardOptionsForm, __(u"Direct posting options")) autotag = forms.FormField(BoardTaggingForm, __(u"Automatic posting options")) def validate_name(self, field): if field.data: if field.data in Board.reserved_names: raise forms.ValidationError(_(u"This name is reserved. Please use another name"))
class CampaignActionForm(forms.Form): title = forms.StringField( __("Title"), description=__("Contents of the call to action button"), validators=[ forms.validators.DataRequired("You must provide some text"), forms.validators.StripWhitespace() ]) icon = forms.NullTextField( __("Icon"), validators=[forms.validators.Optional()], description=__("Optional Font-Awesome icon name")) public = forms.BooleanField(__("This action is live")) type = forms.RadioField( __("Type"), choices=CAMPAIGN_ACTION.items(), validators=[forms.validators.DataRequired(__("This is required"))]) group = forms.NullTextField( __("RSVP group"), validators=[forms.validators.Optional()], description=__( "If you have multiple RSVP actions, add an optional group name")) category = forms.RadioField( __("Category"), validators=[forms.validators.DataRequired(__("This is required"))], widget=forms.InlineListWidget(class_='button-bar', class_prefix='btn btn-'), choices=[ (u'default', __(u"Default")), (u'primary', __(u"Primary")), (u'success', __(u"Success")), (u'info', __(u"Info")), (u'warning', __(u"Warning")), (u'danger', __(u"Danger")), ]) message = forms.TinyMce4Field( __("Message"), description=__( "Message shown after the user has performed an action (for forms and RSVP type)" ), content_css=content_css, validators=[ forms.validators.Optional(), forms.validators.AllUrlsValid() ]) link = forms.URLField( __("Link"), description=__(u"URL to redirect to, if type is “follow link”"), validators=[ forms.validators.StripWhitespace(), optional_url, forms.validators.Length(min=0, max=250, message=__("%%(max)d characters maximum")), forms.validators.ValidUrl() ]) form = forms.TextAreaField( __("Form JSON"), description=__("Form definition (for form type)"), validators=[forms.validators.Optional()]) seq = forms.IntegerField( __("Sequence #"), validators=[forms.validators.DataRequired(__("This is required"))], description=__( "Sequence number for displaying this action when multiple actions are available to the user" ))
class ProposalSubspaceForm(ProposalSpaceForm): inherit_sections = forms.BooleanField( __("Inherit sections from parent space?"), default=True)
class CampaignForm(forms.Form): title = forms.StringField( __("Title"), description=__("A reference name for looking up this campaign again"), validators=[forms.validators.DataRequired(__("A title is required"))], filters=[forms.filters.strip()], ) start_at = forms.DateTimeField(__("Start at"), naive=False) end_at = forms.DateTimeField( __("End at"), validators=[ forms.validators.GreaterThan( 'start_at', __("The campaign can’t end before it starts") ) ], naive=False, ) public = forms.BooleanField(__("This campaign is live")) position = forms.RadioField( __("Display position"), choices=list(CAMPAIGN_POSITION.items()), coerce=int ) priority = forms.IntegerField( __("Priority"), default=0, description=__( "A larger number is higher priority when multiple campaigns are running on the " "same dates. 0 implies lowest priority" ), ) boards = QuerySelectMultipleField( __("Boards"), widget=ListWidget(), option_widget=CheckboxInput(), query_factory=lambda: Board.query.order_by(Board.featured.desc(), Board.title), get_label='title_and_name', validators=[forms.validators.Optional()], description=__("Select the boards this campaign is active on"), ) geonameids = forms.GeonameSelectMultiField( "Locations", description=__( "This campaign will be targetted at users and jobs with matching locations" ), ) user_required = forms.RadioField( __("User is required"), coerce=getbool, choices=[ (None, __("N/A – Don’t target by login status")), (True, __("Yes – Show to logged in users only")), (False, __("No – Show to anonymous users only")), ], ) flags = forms.RadioMatrixField( "Flags", coerce=getbool, fields=Campaign.flag_choices, description=__( "All selected flags must match the logged in user for the campaign to be shown" ), choices=[('None', __("N/A")), ('True', __("True")), ('False', __("False"))], ) content = forms.FormField(CampaignContentForm, __("Campaign content")) def validate_geonameids(self, field): field.data = [int(x) for x in field.data if x.isdigit()]
class PinnedForm(forms.Form): pinned = forms.BooleanField(__("Pin this"))
class ListingForm(forms.Form): """Form for new job posts""" job_headline = forms.StringField( __("Headline"), description=Markup( __( "A single-line summary. This goes to the front page and across the network. " """<a id="abtest" class="no-jshidden" href="#">A/B test it?</a>""" ) ), validators=[ forms.validators.DataRequired(__("A headline is required")), forms.validators.Length( min=1, max=100, message=__("%(max)d characters maximum") ), forms.validators.NoObfuscatedEmail( __("Do not include contact information in the post") ), ], filters=[forms.filters.strip()], ) job_headlineb = forms.StringField( __("Headline B"), description=__( "An alternate headline that will be shown to 50%% of users. " "You’ll get a count of views per headline" ), validators=[ forms.validators.Optional(), forms.validators.Length( min=1, max=100, message=__("%(max)d characters maximum") ), forms.validators.NoObfuscatedEmail( __("Do not include contact information in the post") ), ], filters=[forms.filters.strip(), forms.filters.none_if_empty()], ) job_type = forms.RadioField( __("Type"), coerce=int, validators=[ forms.validators.InputRequired(__("The job type must be specified")) ], ) job_category = forms.RadioField( __("Category"), coerce=int, validators=[forms.validators.InputRequired(__("Select a category"))], ) job_location = forms.StringField( __("Location"), description=__( '“Bangalore”, “Chennai”, “Pune”, etc or “Anywhere” (without quotes)' ), validators=[ forms.validators.DataRequired( __("If this job doesn’t have a fixed location, use “Anywhere”") ), forms.validators.Length( min=3, max=80, message=__("%(max)d characters maximum") ), ], filters=[forms.filters.strip()], ) job_relocation_assist = forms.BooleanField(__("Relocation assistance available")) job_description = forms.TinyMce4Field( __("Description"), content_css=content_css, description=__( "Don’t just describe the job, tell a compelling story for why someone should work for you" ), validators=[ forms.validators.DataRequired(__("A description of the job is required")), forms.validators.AllUrlsValid(invalid_urls=invalid_urls), forms.validators.NoObfuscatedEmail( __("Do not include contact information in the post") ), ], tinymce_options={'convert_urls': True}, ) job_perks = forms.BooleanField(__("Job perks are available")) job_perks_description = forms.TinyMce4Field( __("Describe job perks"), content_css=content_css, description=__("Stock options, free lunch, free conference passes, etc"), validators=[ forms.validators.AllUrlsValid(invalid_urls=invalid_urls), forms.validators.NoObfuscatedEmail( __("Do not include contact information in the post") ), ], ) job_pay_type = forms.RadioField( __("What does this job pay?"), coerce=int, validators=[ forms.validators.InputRequired(__("You need to specify what this job pays")) ], choices=list(PAY_TYPE.items()), ) job_pay_currency = ListingPayCurrencyField( __("Currency"), choices=list(CURRENCY.items()), default=CURRENCY.INR ) job_pay_cash_min = forms.StringField(__("Minimum")) job_pay_cash_max = forms.StringField(__("Maximum")) job_pay_equity = forms.BooleanField(__("Equity compensation is available")) job_pay_equity_min = forms.StringField(__("Minimum")) job_pay_equity_max = forms.StringField(__("Maximum")) job_how_to_apply = forms.TextAreaField( __("What should a candidate submit when applying for this job?"), description=__( "Example: “Include your LinkedIn and GitHub profiles.” " "We now require candidates to apply through the job board only. " "Do not include any contact information here. Candidates CANNOT " "attach resumes or other documents, so do not ask for that" ), validators=[ forms.validators.DataRequired( __( "We do not offer screening services. Please specify what candidates should submit" ) ), forms.validators.NoObfuscatedEmail( __("Do not include contact information in the post") ), ], ) company_name = forms.StringField( __("Employer name"), description=__( "The name of the organization where the position is. " "If your stealth startup doesn't have a name yet, use your own. " "We do not accept posts from third parties such as recruitment consultants. " "Such posts may be removed without notice" ), validators=[ forms.validators.DataRequired( __( "This is required. Posting any name other than that of the actual organization is a violation of the ToS" ) ), forms.validators.Length( min=4, max=80, message=__("The name must be within %(min)d to %(max)d characters"), ), ], filters=[forms.filters.strip()], ) company_logo = forms.FileField( __("Logo"), description=__( "Optional — Your organization’s logo will appear at the top of your post." ), # validators=[file_allowed(uploaded_logos, "That image type is not supported")]) ) company_logo_remove = forms.BooleanField(__("Remove existing logo")) company_url = forms.URLField( __("URL"), description=__("Your organization’s website"), validators=[ forms.validators.DataRequired(), optional_url, forms.validators.Length(max=255, message=__("%(max)d characters maximum")), forms.validators.ValidUrl(), ], filters=[forms.filters.strip()], ) hr_contact = forms.RadioField( __( "Is it okay for recruiters and other " "intermediaries to contact you about this post?" ), coerce=getbool, description=__("We’ll display a notice to this effect on the post"), default=0, choices=[ (0, __("No, it is NOT OK")), (1, __("Yes, recruiters may contact me")), ], ) # Deprecated 2013-11-20 # poster_name = forms.StringField(__("Name"), # description=__(u"This is your name, for our records. Will not be revealed to applicants"), # validators=[forms.validators.DataRequired(__("We need your name"))]) poster_email = forms.EmailField( __("Email"), description=Markup( __( "This is where we’ll send your confirmation email and all job applications. " "We recommend using a shared email address such as [email protected]. " "<strong>Listings are classified by your email domain,</strong> " "so use a work email address. " "Your email address will not be revealed to applicants until you respond" ) ), validators=[ forms.validators.DataRequired( __("We need to confirm your email address before the job can be listed") ), forms.validators.Length( min=5, max=80, message=__("%(max)d characters maximum") ), forms.validators.ValidEmail( __("This does not appear to be a valid email address") ), ], filters=[forms.filters.strip()], ) twitter = forms.AnnotatedTextField( __("Twitter"), description=__( "Optional — your organization’s Twitter account. " "We’ll tweet mentioning you so you get included on replies" ), prefix='@', validators=[ forms.validators.Optional(), forms.validators.Length( min=0, max=15, message=__("Twitter accounts can’t be over %(max)d characters long"), ), ], filters=[forms.filters.strip(), forms.filters.none_if_empty()], ) collaborators = forms.UserSelectMultiField( __("Collaborators"), description=__( "If someone is helping you evaluate candidates, type their names here. " "They must have a Hasgeek account. They will not receive email notifications " "— use a shared email address above for that — but they will be able to respond " "to candidates who apply" ), usermodel=User, lastuser=lastuser, ) def validate_twitter(self, field): if field.data.startswith('@'): field.data = field.data[1:] if INVALID_TWITTER_RE.search(field.data): raise forms.ValidationError( _("That does not appear to be a valid Twitter account") ) def validate_poster_email(self, field): field.data = field.data.lower() def validate_job_type(self, field): # This validator exists primarily for this assignment, used later in the form by other validators self.job_type_ob = JobType.query.get(field.data) if not self.job_type_ob: raise forms.ValidationError(_("Please select a job type")) def validate_company_name(self, field): if len(field.data) > 6: caps = len(CAPS_RE.findall(field.data)) # small = len(SMALL_RE.findall(field.data)) # deprecated on 30-11-2018 # if small == 0 or caps / float(small) > 0.8: # deprecated on 30-11-2018 # For now, only 6 capital letters are allowed in company name if caps > 6: raise forms.ValidationError( _("Surely your organization isn’t named in uppercase?") ) def validate_company_logo(self, field): if not ('company_logo' in request.files and request.files['company_logo']): return try: g.company_logo = process_image(request.files['company_logo']) except IOError as e: raise forms.ValidationError(e.message) except KeyError: raise forms.ValidationError(_("Unknown file format")) except UploadNotAllowed: raise forms.ValidationError( _("Unsupported file format. We accept JPEG, PNG and GIF") ) def validate_job_headline(self, field): if simplify_text(field.data) in ( 'awesome coder wanted at awesome company', 'pragmatic programmer wanted at outstanding organisation', 'pragmatic programmer wanted at outstanding organization', ) or ( g.board and g.board.newjob_headline and simplify_text(field.data) == simplify_text(g.board.newjob_headline) ): raise forms.ValidationError( _( "Come on, write your own headline. You aren’t just another run-of-the-mill employer, right?" ) ) caps = len(CAPS_RE.findall(field.data)) small = len(SMALL_RE.findall(field.data)) if small == 0 or caps / float(small) > 1.0: raise forms.ValidationError( _( "No shouting, please. Reduce the number of capital letters in your headline" ) ) for word_list, message in app.config.get('BANNED_WORDS', []): for word in word_list: if word in field.data.lower(): raise forms.ValidationError(message) def validate_job_headlineb(self, field): return self.validate_job_headline(field) def validate_job_location(self, field): if QUOTES_RE.search(field.data) is not None: raise forms.ValidationError(_("Don’t use quotes in the location name")) caps = len(CAPS_RE.findall(field.data)) small = len(SMALL_RE.findall(field.data)) if small == 0 or caps / float(small) > 1.0: raise forms.ValidationError( _("Surely this location isn't named in uppercase?") ) def validate_job_pay_cash_min(self, field): if self.job_pay_type.data in (PAY_TYPE.ONETIME, PAY_TYPE.RECURRING): data = field.data.strip() if not data: raise forms.ValidationError(_("Please specify what this job pays")) data = string_to_number(data) if data is None: raise forms.ValidationError(_("Unrecognised value %s") % field.data) else: field.data = data else: field.data = None def validate_job_pay_cash_max(self, field): if self.job_pay_type.data in (PAY_TYPE.ONETIME, PAY_TYPE.RECURRING): data = string_to_number(field.data.strip()) if data is None: raise forms.ValidationError(_("Unrecognised value %s") % field.data) else: field.data = data else: field.data = None def validate_job_pay_equity_min(self, field): if self.job_pay_equity.data: data = field.data.strip() if data: if not data[-1].isdigit(): data = field.data[:-1] # Remove % symbol data = data.replace(',', '').strip() # Remove thousands separator try: field.data = Decimal(data) except InvalidOperation: raise forms.ValidationError( _("Please enter a percentage between 0%% and 100%%") ) else: raise forms.ValidationError(_("Unrecognised value %s") % field.data) else: # Discard submission if equity checkbox is unchecked field.data = None def validate_job_pay_equity_max(self, field): if self.job_pay_equity.data: data = field.data.strip() if data: if not data[-1].isdigit(): data = field.data[:-1] # Remove % symbol data = data.replace(',', '').strip() # Remove thousands separator try: field.data = Decimal(data) except InvalidOperation: raise forms.ValidationError( _("Please enter a percentage between 0%% and 100%%") ) else: raise forms.ValidationError(_("Unrecognised value %s") % field.data) else: # Discard submission if equity checkbox is unchecked field.data = None def validate(self): success = super(ListingForm, self).validate(send_signals=False) if success: if ( not self.job_type_ob.nopay_allowed ) and self.job_pay_type.data == PAY_TYPE.NOCASH: self.job_pay_type.errors.append( _("“%s” cannot pay nothing") % self.job_type_ob.title ) success = False domain_name = get_email_domain(self.poster_email.data) domain = Domain.get(domain_name) if domain and domain.is_banned: self.poster_email.errors.append( _("%s is banned from posting jobs on Hasjob") % domain_name ) success = False elif (not self.job_type_ob.webmail_allowed) and is_public_email_domain( domain_name, default=False ): self.poster_email.errors.append( _( "Public webmail accounts like Gmail are not accepted. Please use your corporate email address" ) ) success = False # Check for cash pay range if self.job_pay_type.data in (PAY_TYPE.ONETIME, PAY_TYPE.RECURRING): if self.job_pay_cash_min.data == 0: if self.job_pay_cash_max.data == 10000000: self.job_pay_cash_max.errors.append(_("Please select a range")) success = False else: self.job_pay_cash_min.errors.append( _("Please specify a minimum non-zero pay") ) success = False else: if self.job_pay_cash_max.data == 10000000: if self.job_pay_currency.data == 'INR': figure = _("1 crore") else: figure = _("10 million") self.job_pay_cash_max.errors.append( _( "You’ve selected an upper limit of {figure}. That can’t be right" ).format(figure=figure) ) success = False elif ( self.job_pay_type.data == PAY_TYPE.RECURRING and self.job_pay_currency.data == 'INR' and self.job_pay_cash_min.data < 60000 ): self.job_pay_cash_min.errors.append( _( "That’s rather low. Did you specify monthly pay instead of annual pay? Multiply by 12" ) ) success = False elif self.job_pay_cash_max.data > self.job_pay_cash_min.data * 4: self.job_pay_cash_max.errors.append( _( "Please select a narrower range, with maximum within 4× minimum" ) ) success = False if self.job_pay_equity.data: if self.job_pay_equity_min.data == 0: if self.job_pay_equity_max.data == 100: self.job_pay_equity_max.errors.append( _("Please select a range") ) success = False else: if self.job_pay_equity_min.data <= Decimal('1.0'): multiplier = 10 elif self.job_pay_equity_min.data <= Decimal('2.0'): multiplier = 8 elif self.job_pay_equity_min.data <= Decimal('3.0'): multiplier = 6 else: multiplier = 4 if ( self.job_pay_equity_max.data > self.job_pay_equity_min.data * multiplier ): self.job_pay_equity_max.errors.append( _( "Please select a narrower range, with maximum within %d× minimum" ) % multiplier ) success = False self.send_signals() return success def populate_from(self, post): self.job_headline.data = post.headline self.job_headlineb.data = post.headlineb self.job_type.data = post.type_id self.job_category.data = post.category_id self.job_location.data = post.location self.job_relocation_assist.data = post.relocation_assist self.job_description.data = post.description self.job_perks.data = True if post.perks else False self.job_perks_description.data = post.perks self.job_how_to_apply.data = post.how_to_apply self.company_name.data = post.company_name self.company_url.data = post.company_url self.poster_email.data = post.email self.twitter.data = post.twitter self.hr_contact.data = int(post.hr_contact or False) self.collaborators.data = post.admins self.job_pay_type.data = post.pay_type if post.pay_type is None: # This kludge required because WTForms doesn't know how to handle None in forms self.job_pay_type.data = -1 self.job_pay_currency.data = post.pay_currency self.job_pay_cash_min.data = post.pay_cash_min self.job_pay_cash_max.data = post.pay_cash_max self.job_pay_equity.data = bool(post.pay_equity_min and post.pay_equity_max) self.job_pay_equity_min.data = post.pay_equity_min self.job_pay_equity_max.data = post.pay_equity_max
class SavedSessionForm(forms.Form): save = forms.BooleanField(__("Save this session?"), validators=[forms.validators.InputRequired()]) description = forms.StringField(__("Note to self"))
class AuthClientForm(forms.Form): """ Register a new OAuth client application """ title = forms.StringField( __("Application title"), validators=[forms.validators.DataRequired()], description=__("The name of your application"), ) description = forms.TextAreaField( __("Description"), validators=[forms.validators.DataRequired()], description=__( "A description to help users recognize your application"), ) client_owner = forms.RadioField( __("Owner"), validators=[forms.validators.DataRequired()], description=__( "User or organization that owns this application. Changing the owner " "will revoke all currently assigned permissions for this app"), ) confidential = forms.RadioField( __("Application type"), coerce=getbool, default=True, choices=[ ( True, __("Confidential (server-hosted app, capable of storing secret key securely)" ), ), ( False, __("Public (native or in-browser app, not capable of storing secret key securely)" ), ), ], ) website = forms.URLField( __("Application website"), validators=[forms.validators.DataRequired(), forms.validators.URL()], description=__("Website where users may access this application"), ) namespace = forms.StringField( __("Client namespace"), validators=[forms.validators.Optional()], filters=[forms.filters.none_if_empty()], description=Markup( __("A dot-based namespace that uniquely identifies your client application. " "For example, if your client website is <code>https://auth.hasgeek.com</code>, " "use <code>com.hasgeek.auth</code>. Only required if your client app provides resources" )), widget_attrs={ 'autocorrect': 'none', 'autocapitalize': 'none' }, ) redirect_uris = forms.TextListField( __("Redirect URLs"), validators=[ forms.validators.OptionalIf('confidential'), forms.ForEach([forms.URL()]), ], filters=[forms.strip_each()], description=__( "OAuth2 Redirect URL. If your app is available on multiple hostnames, " "list each redirect URL on a separate line"), ) notification_uri = forms.URLField( __("Notification URL"), validators=[forms.validators.Optional(), forms.validators.URL()], description=__( "When the user's data changes, Lastuser will POST a notice to this URL. " "Other notices may be posted too"), ) allow_any_login = forms.BooleanField( __("Allow anyone to login"), default=True, description=__( "If your application requires access to be restricted to specific users, uncheck this, " "and only users who have been assigned a permission to the app will be able to login" ), ) def validate_client_owner(self, field): if field.data == self.edit_user.buid: self.user = self.edit_user self.organization = None else: orgs = [ org for org in self.edit_user.organizations_as_owner if org.buid == field.data ] if len(orgs) != 1: raise forms.ValidationError(_("Invalid owner")) self.user = None self.organization = orgs[0] def _urls_match(self, url1, url2): p1 = urlparse(url1) p2 = urlparse(url2) return ((p1.netloc == p2.netloc) and (p1.scheme == p2.scheme) and (p1.username == p2.username) and (p1.password == p2.password)) def validate_redirect_uri(self, field): if self.confidential.data and not self._urls_match( self.website.data, field.data): raise forms.ValidationError( _("The scheme, domain and port must match that of the website URL" )) def validate_notification_uri(self, field): if not self._urls_match(self.website.data, field.data): raise forms.ValidationError( _("The scheme, domain and port must match that of the website URL" )) def validate_resource_uri(self, field): if not self._urls_match(self.website.data, field.data): raise forms.ValidationError( _("The scheme, domain and port must match that of the website URL" )) def validate_namespace(self, field): if field.data: if not domain_namespace_match(self.website.data, field.data): raise forms.ValidationError( _("The namespace should be derived from your application’s website domain" )) auth_client = self.edit_model.get(namespace=field.data) if auth_client: if auth_client == self.edit_obj: return raise forms.ValidationError( _("This namespace has been claimed by another client app"))
class ListingForm(forms.Form): """Form for new job posts""" job_headline = forms.StringField(__("Headline"), description=Markup(__("A single-line summary. This goes to the front page and across the network. " """<a id="abtest" class="no-jshidden" href="#">A/B test it?</a>""")), validators=[forms.validators.DataRequired(__("A headline is required")), forms.validators.Length(min=1, max=100, message=__("%%(max)d characters maximum")), forms.validators.NoObfuscatedEmail(__(u"Do not include contact information in the post"))], filters=[forms.filters.strip()]) job_headlineb = forms.StringField(__("Headline B"), description=__(u"An alternate headline that will be shown to 50%% of users. " u"You’ll get a count of views per headline"), validators=[forms.validators.Optional(), forms.validators.Length(min=1, max=100, message=__("%%(max)d characters maximum")), forms.validators.NoObfuscatedEmail(__(u"Do not include contact information in the post"))], filters=[forms.filters.strip(), forms.filters.none_if_empty()]) job_type = forms.RadioField(__("Type"), coerce=int, validators=[forms.validators.InputRequired(__("The job type must be specified"))]) job_category = forms.RadioField(__("Category"), coerce=int, validators=[forms.validators.InputRequired(__("Select a category"))]) job_location = forms.StringField(__("Location"), description=__(u'“Bangalore”, “Chennai”, “Pune”, etc or “Anywhere” (without quotes)'), validators=[forms.validators.DataRequired(__(u"If this job doesn’t have a fixed location, use “Anywhere”")), forms.validators.Length(min=3, max=80, message=__("%%(max)d characters maximum"))], filters=[forms.filters.strip()]) job_relocation_assist = forms.BooleanField(__("Relocation assistance available")) job_description = forms.TinyMce4Field(__("Description"), content_css=content_css, description=__(u"Don’t just describe the job, tell a compelling story for why someone should work for you"), validators=[forms.validators.DataRequired(__("A description of the job is required")), forms.validators.AllUrlsValid(invalid_urls=invalid_urls), forms.validators.NoObfuscatedEmail(__(u"Do not include contact information in the post"))], tinymce_options={'convert_urls': True}) job_perks = forms.BooleanField(__("Job perks are available")) job_perks_description = forms.TinyMce4Field(__("Describe job perks"), content_css=content_css, description=__(u"Stock options, free lunch, free conference passes, etc"), validators=[forms.validators.AllUrlsValid(invalid_urls=invalid_urls), forms.validators.NoObfuscatedEmail(__(u"Do not include contact information in the post"))]) job_pay_type = forms.RadioField(__("What does this job pay?"), coerce=int, validators=[forms.validators.InputRequired(__("You need to specify what this job pays"))], choices=PAY_TYPE.items()) job_pay_currency = ListingPayCurrencyField(__("Currency"), choices=CURRENCY.items(), default=CURRENCY.INR) job_pay_cash_min = forms.StringField(__("Minimum")) job_pay_cash_max = forms.StringField(__("Maximum")) job_pay_equity = forms.BooleanField(__("Equity compensation is available")) job_pay_equity_min = forms.StringField(__("Minimum")) job_pay_equity_max = forms.StringField(__("Maximum")) job_how_to_apply = forms.TextAreaField(__("What should a candidate submit when applying for this job?"), description=__(u"Example: “Include your LinkedIn and GitHub profiles.” " u"We now require candidates to apply through the job board only. " u"Do not include any contact information here. Candidates CANNOT " u"attach resumes or other documents, so do not ask for that"), validators=[ forms.validators.DataRequired(__(u"We do not offer screening services. Please specify what candidates should submit")), forms.validators.NoObfuscatedEmail(__(u"Do not include contact information in the post"))]) company_name = forms.StringField(__("Employer name"), description=__(u"The name of the organization where the position is. " u"If your stealth startup doesn't have a name yet, use your own. " u"We do not accept posts from third parties such as recruitment consultants. " u"Such posts may be removed without notice"), validators=[forms.validators.DataRequired(__(u"This is required. Posting any name other than that of the actual organization is a violation of the ToS")), forms.validators.Length(min=4, max=80, message=__("The name must be within %%(min)d to %%(max)d characters"))], filters=[forms.filters.strip()]) company_logo = forms.FileField(__("Logo"), description=__(u"Optional — Your organization’s logo will appear at the top of your post."), ) # validators=[file_allowed(uploaded_logos, "That image type is not supported")]) company_logo_remove = forms.BooleanField(__("Remove existing logo")) company_url = forms.URLField(__("URL"), description=__(u"Your organization’s website"), validators=[forms.validators.DataRequired(), optional_url, forms.validators.Length(max=255, message=__("%%(max)d characters maximum")), forms.validators.ValidUrl()], filters=[forms.filters.strip()]) hr_contact = forms.RadioField(__(u"Is it okay for recruiters and other " u"intermediaries to contact you about this post?"), coerce=getbool, description=__(u"We’ll display a notice to this effect on the post"), default=0, choices=[(0, __(u"No, it is NOT OK")), (1, __(u"Yes, recruiters may contact me"))]) # Deprecated 2013-11-20 # poster_name = forms.StringField(__("Name"), # description=__(u"This is your name, for our records. Will not be revealed to applicants"), # validators=[forms.validators.DataRequired(__("We need your name"))]) poster_email = forms.EmailField(__("Email"), description=Markup(__(u"This is where we’ll send your confirmation email and all job applications. " u"We recommend using a shared email address such as [email protected]. " u"<strong>Listings are classified by your email domain,</strong> " u"so use a work email address. " u"Your email address will not be revealed to applicants until you respond")), validators=[ forms.validators.DataRequired(__("We need to confirm your email address before the job can be listed")), forms.validators.Length(min=5, max=80, message=__("%%(max)d characters maximum")), forms.validators.ValidEmail(__("This does not appear to be a valid email address"))], filters=[forms.filters.strip()]) twitter = forms.AnnotatedTextField(__("Twitter"), description=__(u"Optional — your organization’s Twitter account. " u"We’ll tweet mentioning you so you get included on replies"), prefix='@', validators=[ forms.validators.Optional(), forms.validators.Length(min=0, max=15, message=__(u"Twitter accounts can’t be over %%(max)d characters long"))], filters=[forms.filters.strip(), forms.filters.none_if_empty()]) collaborators = forms.UserSelectMultiField(__(u"Collaborators"), description=__(u"If someone is helping you evaluate candidates, type their names here. " u"They must have a HasGeek account. They will not receive email notifications " u"— use a shared email address above for that — but they will be able to respond " u"to candidates who apply"), usermodel=User, lastuser=lastuser) def validate_twitter(self, field): if field.data.startswith('@'): field.data = field.data[1:] if INVALID_TWITTER_RE.search(field.data): raise forms.ValidationError(_("That does not appear to be a valid Twitter account")) def validate_poster_email(form, field): field.data = field.data.lower() def validate_job_type(form, field): # This validator exists primarily for this assignment, used later in the form by other validators form.job_type_ob = JobType.query.get(field.data) if not form.job_type_ob: raise forms.ValidationError(_("Please select a job type")) def validate_company_name(form, field): if len(field.data) > 6: caps = len(CAPS_RE.findall(field.data)) # small = len(SMALL_RE.findall(field.data)) # deprecated on 30-11-2018 # if small == 0 or caps / float(small) > 0.8: # deprecated on 30-11-2018 # For now, only 6 capital letters are allowed in company name if caps > 6: raise forms.ValidationError(_(u"Surely your organization isn’t named in uppercase?")) def validate_company_logo(form, field): if not ('company_logo' in request.files and request.files['company_logo']): return try: g.company_logo = process_image(request.files['company_logo']) except IOError, e: raise forms.ValidationError(e.message) except KeyError, e: raise forms.ValidationError(_("Unknown file format"))
class TicketParticipantForm(forms.Form): __returns__ = ('user', ) fullname = forms.StringField( __("Fullname"), validators=[forms.validators.DataRequired()], filters=[forms.filters.strip()], ) email = forms.EmailField( __("Email"), validators=[ forms.validators.DataRequired(), forms.validators.ValidEmail() ], filters=[forms.filters.strip()], ) phone = forms.StringField( __("Phone number"), validators=[forms.validators.Length(max=80)], filters=[forms.filters.strip()], ) city = forms.StringField( __("City"), validators=[forms.validators.Length(max=80)], filters=[forms.filters.strip()], ) company = forms.StringField( __("Company"), validators=[forms.validators.Length(max=80)], filters=[forms.filters.strip()], ) job_title = forms.StringField( __("Job title"), validators=[forms.validators.Length(max=80)], filters=[forms.filters.strip()], ) twitter = forms.StringField( __("Twitter"), validators=[forms.validators.Length(max=15)], filters=[forms.filters.strip()], ) badge_printed = forms.BooleanField(__("Badge is printed")) ticket_events = QuerySelectMultipleField( __("Events"), widget=ListWidget(), option_widget=CheckboxInput(), get_label='title', validators=[ forms.validators.DataRequired("Select at least one event") ], ) def set_queries(self): if self.edit_parent is not None: self.ticket_events.query = self.edit_parent.ticket_events def validate(self): result = super().validate() with db.session.no_autoflush: useremail = UserEmail.get(email=self.email.data) if useremail: self.user = useremail.user else: self.user = None return result
class RegisterClientForm(forms.Form): """ Register a new OAuth client application """ title = forms.StringField(__("Application title"), validators=[forms.validators.DataRequired()], description=__("The name of your application")) description = forms.TextAreaField(__("Description"), validators=[forms.validators.DataRequired()], description=__("A description to help users recognize your application")) client_owner = forms.RadioField(__("Owner"), validators=[forms.validators.DataRequired()], description=__("User or organization that owns this application. Changing the owner " "will revoke all currently assigned permissions for this app")) website = forms.URLField(__("Application website"), validators=[forms.validators.DataRequired(), forms.validators.URL()], description=__("Website where users may access this application")) namespace = forms.NullTextField(__("Client namespace"), validators=[forms.validators.Optional()], description=Markup(__(u"A dot-based namespace that uniquely identifies your client application. " u"For example, if your client website is <code>https://auth.hasgeek.com</code>, " u"use <code>com.hasgeek.auth</code>. Only required if your client app provides resources")), widget_attrs={'autocorrect': 'none', 'autocapitalize': 'none'}) redirect_uri = forms.URLField(__("Redirect URL"), validators=[forms.validators.Optional(), forms.validators.URL()], description=__("OAuth2 Redirect URL")) notification_uri = forms.URLField(__("Notification URL"), validators=[forms.validators.Optional(), forms.validators.URL()], description=__("When the user's data changes, Lastuser will POST a notice to this URL. " "Other notices may be posted too")) iframe_uri = forms.URLField(__("IFrame URL"), validators=[forms.validators.Optional(), forms.validators.URL()], description=__("Front-end notifications URL. This is loaded in a hidden iframe to notify the app that the " "user updated their profile in some way (not yet implemented)")) allow_any_login = forms.BooleanField(__("Allow anyone to login"), default=True, description=__("If your application requires access to be restricted to specific users, uncheck this, " "and only users who have been assigned a permission to the app will be able to login")) team_access = forms.BooleanField(__("Requires access to teams"), default=False, description=__("If your application is capable of assigning access permissions to teams, check this. " "Organization owners will then able to grant access to teams in their organizations")) def validate_client_owner(self, field): if field.data == self.edit_user.userid: self.user = self.edit_user self.org = None else: orgs = [org for org in self.edit_user.organizations_owned() if org.userid == field.data] if len(orgs) != 1: raise forms.ValidationError(_("Invalid owner")) self.user = None self.org = orgs[0] def _urls_match(self, url1, url2): p1 = urlparse(url1) p2 = urlparse(url2) return (p1.netloc == p2.netloc) and (p1.scheme == p2.scheme) and ( p1.username == p2.username) and (p1.password == p2.password) def validate_redirect_uri(self, field): if not self._urls_match(self.website.data, field.data): raise forms.ValidationError(_("The scheme, domain and port must match that of the website URL")) def validate_notification_uri(self, field): if not self._urls_match(self.website.data, field.data): raise forms.ValidationError(_("The scheme, domain and port must match that of the website URL")) def validate_resource_uri(self, field): if not self._urls_match(self.website.data, field.data): raise forms.ValidationError(_("The scheme, domain and port must match that of the website URL")) def validate_namespace(self, field): if field.data: if not domain_namespace_match(self.website.data, field.data): raise forms.ValidationError(_(u"The namespace should be derived from your application’s website domain")) client = self.edit_model.get(namespace=field.data) if client: if client == self.edit_obj: return raise forms.ValidationError(_("This namespace has been claimed by another client app"))