Beispiel #1
0
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"
        ),
    )
Beispiel #2
0
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
Beispiel #3
0
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"))
Beispiel #4
0
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"))
Beispiel #5
0
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),
        ],
    )
Beispiel #6
0
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")
        ])
Beispiel #7
0
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"))
        ],
    )
Beispiel #8
0
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"))
Beispiel #9
0
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
    )
Beispiel #10
0
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"
            )
        ])
Beispiel #11
0
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")
Beispiel #12
0
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()],
    )
Beispiel #13
0
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()])
Beispiel #14
0
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('&nbsp;', ' ').replace('&#160;', ' ').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"))
Beispiel #15
0
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
Beispiel #16
0
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')
Beispiel #17
0
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"))
Beispiel #18
0
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,
    )
Beispiel #19
0
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()]
Beispiel #20
0
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"))
Beispiel #21
0
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"
        ))
Beispiel #22
0
class ProposalSubspaceForm(ProposalSpaceForm):
    inherit_sections = forms.BooleanField(
        __("Inherit sections from parent space?"), default=True)
Beispiel #23
0
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()]
Beispiel #24
0
class PinnedForm(forms.Form):
    pinned = forms.BooleanField(__("Pin this"))
Beispiel #25
0
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
Beispiel #26
0
class SavedSessionForm(forms.Form):
    save = forms.BooleanField(__("Save this session?"),
                              validators=[forms.validators.InputRequired()])
    description = forms.StringField(__("Note to self"))
Beispiel #27
0
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"))
Beispiel #28
0
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"))
Beispiel #29
0
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
Beispiel #30
0
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"))