示例#1
0
文件: forms.py 项目: jostw/zamboni
    def __init__(self, *args, **kwargs):
        super(ApiReviewersSearchForm, self).__init__(*args, **kwargs)

        # Mobile form, to render, expects choices from the Django field.
        BOOL_CHOICES = ((u"", _lazy("Unknown")), (u"true", _lazy("Yes")), (u"false", _lazy("No")))
        for field_name, field in self.fields.iteritems():
            if isinstance(field, forms.NullBooleanField):
                self.fields[field_name].choices = BOOL_CHOICES
示例#2
0
 def render_additional_info(self, row):
     info = []
     if row.is_site_specific:
         info.append(_lazy(u'Site Specific'))
     if row.external_software:
         info.append(_lazy(u'Requires External Software'))
     if row.binary or row.binary_components:
         info.append(_lazy(u'Binary Components'))
     return u', '.join([jinja2.escape(i) for i in info])
示例#3
0
文件: api.py 项目: 1234-/kitsune
 def data(self):
     topics = super(RootTopicSerializer, self).data
     return {
         'subtopics': topics,
         'documents': [],
         'title': _lazy('All Topics'),
         'slug': '',
         'description': _lazy('All Topics'),
         'parent': None,
         'visible': True,
         'product': None,
         'path': '/',
     }
示例#4
0
 def _validate_body(self, content_type):
     """Try rendering the body template and capture any errors."""
     assert content_type in EmailTemplate.CONTENT_TYPES, _lazy("Invalid content type.")
     if content_type == EmailTemplate.CONTENT_TYPE_PLAIN:
         field_name = 'body_text'
     if content_type == EmailTemplate.CONTENT_TYPE_HTML:
         field_name = 'body_html'
     try:
         self.render_body({}, content_type=content_type)
     except TemplateDoesNotExist as ex:
         return {field_name: _lazy("Template does not exist: {}".format(ex))}
     except TemplateSyntaxError as ex:
         return {field_name: str(ex)}
     else:
         return {}
示例#5
0
文件: helpers.py 项目: Ritsyy/fjord
def country_name(country, native=False, default=_lazy(u'Unknown')):
    """Convert a country code into a human readable country name"""
    if country in CODE_TO_COUNTRY:
        display_locale = 'native' if native else 'English'
        return CODE_TO_COUNTRY[country][display_locale]
    else:
        return default
示例#6
0
文件: events.py 项目: 1234-/kitsune
    def _mails(self, users_and_watches):
        revision = self.revision
        document = revision.document
        log.debug('Sending ready for review email for revision (id=%s)' %
                  revision.id)
        subject = _lazy(u'{title} is ready for review ({creator})')
        url = reverse('wiki.review_revision',
                      locale=document.locale,
                      args=[document.slug, revision.id])

        context = context_dict(revision)
        context['revision_url'] = add_utm(url, 'wiki-ready-review')
        context['locale'] = document.locale
        context['title'] = document.title
        context['creator'] = revision.creator
        context['comment'] = revision.comment

        users = []
        for u, w in users_and_watches:
            if document.allows(u, 'review_revision'):
                users.append((u, w))

        return email_utils.emails_with_users_and_watches(
            subject=subject,
            text_template='wiki/email/ready_for_review.ltxt',
            html_template='wiki/email/ready_for_review.html',
            context_vars=context,
            users_and_watches=users,
            default_locale=document.locale)
示例#7
0
    def _hook_video(self, parser, space, title):
        """Handles [[Video:video title]] with locale from parser."""
        message = _lazy(u'The video "%s" does not exist.') % title

        # params, only modal supported for now
        title, params = build_hook_params(title, self.locale, VIDEO_PARAMS)

        # If this is a youtube video, return the youtube embed
        if _is_youtube_url(title):
            parsed_url = urlparse(title)
            netloc = parsed_url.netloc
            if netloc == 'youtu.be':
                # The video id is the path minus the leading /
                video_id = parsed_url.path[1:]
            else:
                # The video id is in the v= query param
                video_id = parse_qs(parsed_url.query)['v'][0]

            self.youtube_videos.append(video_id)

            return YOUTUBE_PLACEHOLDER % video_id

        v = get_object_fallback(Video, title, self.locale, message)
        if isinstance(v, basestring):
            return v

        return generate_video(v, params)
示例#8
0
def locale_name(locale, native=False, default=_lazy(u'Unknown')):
    """Convert a locale code into a human readable locale name"""
    if locale in product_details.languages:
        display_locale = 'native' if native else 'English'
        return product_details.languages[locale][display_locale]
    else:
        return default
示例#9
0
def editor_page_title(context, title=None, addon=None):
    """Wrapper for editor page titles.  Eerily similar to dev_page_title."""
    if addon:
        title = u'%s :: %s' % (title, addon.name)
    else:
        section = _lazy('Editor Tools')
        title = u'%s :: %s' % (title, section) if title else section
    return page_title(context, title)
示例#10
0
 def render_body(self, context, content_type=CONTENT_TYPE_PLAIN, processors=CONTEXT_PROCESSORS):
     """Render email body in plain text or HTML format."""
     assert content_type in EmailTemplate.CONTENT_TYPES, _lazy("Invalid content type.")
     if content_type == EmailTemplate.CONTENT_TYPE_PLAIN:
         ctx = Context(helpers.patch_context(context, processors), autoescape=False)
         return Template(self.body_text).render(ctx)
     if content_type == EmailTemplate.CONTENT_TYPE_HTML:
         ctx = Context(helpers.patch_context(context, processors))
         return Template(self.body_html).render(ctx)
示例#11
0
    def _hook_button(self, parser, space, btn_type):
        btn_type, params = build_hook_params(btn_type, self.locale)

        if btn_type == 'refresh':
            template = 'wikiparser/hook_refresh_button.html'
        else:
            return _lazy(u'Button of type "%s" does not exist.') % btn_type

        return jingo.get_env().get_template(template).render({'params': params})
示例#12
0
文件: utils.py 项目: 1234-/kitsune
def check_file_size(f, max_allowed_size):
    """Check the file size of f is less than max_allowed_size

    Raise FileTooLargeError if the check fails.

    """
    if f.size > max_allowed_size:
        message = _lazy(u'"%s" is too large (%sKB), the limit is %sKB') % (
            f.name, f.size >> 10, max_allowed_size >> 10)
        raise FileTooLargeError(message)
示例#13
0
文件: base.py 项目: kn17/remo
def _request_args():
    from django.conf import settings
    from django.utils.translation import ugettext_lazy as _lazy

    args = {'siteName': _lazy('Mozilla Reps'), }

    if settings.SITE_URL.startswith('https'):
        args['siteLogo'] = '/static/base/img/remo/remo_logo_medium.png'

    return args
示例#14
0
 def _validate_subject(self):
     """Try rendering the body template and capture any errors."""
     try:
         self.render_subject({})
     except TemplateDoesNotExist as ex:
         return {'subject': _lazy("Template does not exist: {}".format(ex))}
     except TemplateSyntaxError as ex:
         return {'subject': str(ex)}
     else:
         return {}
示例#15
0
    def __init__(self, user, *args, **kwargs):
        super(BaseZendeskForm, self).__init__(*args, **kwargs)

        self.user = user

        # Add email field for users not logged in.
        if not user.is_authenticated():
            email = forms.EmailField(
                label=_lazy(u"Email:"), widget=forms.TextInput(attrs={"placeholder": EMAIL_PLACEHOLDER})
            )
            self.fields["email"] = email
示例#16
0
def _browserid_request_args():
    from django.conf import settings
    from django.utils.translation import ugettext_lazy as _lazy

    args = {
        'siteName': _lazy('Mozillians'),
    }

    if settings.SITE_URL.startswith('https'):
        args['siteLogo'] = urljoin(STATIC_URL, "mozillians/img/apple-touch-icon-144.png")

    return args
示例#17
0
文件: forms.py 项目: MarsWan/Booktype
    def extra_context(cls, book, request):
        from django.utils.translation import ugettext_lazy as _lazy
        all_statuses = (BookStatus.objects
                        .filter(book=book)
                        .annotate(num_chapters=Count('chapter'))
                        .order_by('-weight'))
        all_statuses = [{'pk': status.pk, 'name': _lazy(status.name)} for status in all_statuses]

        return {
            'roles_permissions': security.get_user_permissions(request.user, book),
            'status_list': all_statuses,
        }
示例#18
0
    def group_tier_choices(self):
        """Creates tier choices with optgroups based on payment methods"""
        price_choices = [
            ('free', _('Free (with in-app payments)')),
        ]
        card_billed = []
        operator_billed = []
        card_and_operator_billed = []

        for price in Price.objects.active():
            choice = (price.pk, unicode(price))
            # Special case price tier 0.
            if price.price == Decimal('0.00'):
                price_choices.append((price.pk, '%s (%s)' %
                                      (unicode(price),
                                       _('Promotional Pricing'))))
            # Tiers that can only be operator billed.
            elif price.method == PAYMENT_METHOD_OPERATOR:
                operator_billed.append(choice)
            # Tiers that can only be card billed.
            elif price.method == PAYMENT_METHOD_CARD:
                card_billed.append(choice)
            # Tiers that are can generally be billed by either
            # operator or card.
            elif price.method == PAYMENT_METHOD_ALL:
                card_and_operator_billed.append(choice)

        if operator_billed:
            price_choices.append((_lazy('Only supports carrier billing'),
                                  operator_billed))
        if card_billed:
            price_choices.append((_lazy('Only supports credit-card billing'),
                                  card_billed))
        if card_and_operator_billed:
            price_choices.append(
                (_lazy('Supports all billing methods'),
                 card_and_operator_billed))

        return price_choices
示例#19
0
    def get_actions(self, request, addon):
        actions = SortedDict()
        if request is None:
            # If request is not set, it means we are just (ab)using the
            # ReviewHelper for its `handler` attribute and we don't care about
            # the actions.
            return actions
        labels, details = self._review_actions()
        reviewable_because_complete = addon.status not in (
            amo.STATUS_NULL, amo.STATUS_DELETED)
        reviewable_because_admin = (
            not addon.admin_review or
            acl.action_allowed(request, 'ReviewerAdminTools', 'View'))
        reviewable_because_submission_time = (
            not is_limited_reviewer(request) or
            (addon.latest_version is not None and
                addon.latest_version.nomination is not None and
                (datetime.datetime.now() - addon.latest_version.nomination >=
                    datetime.timedelta(hours=REVIEW_LIMITED_DELAY_HOURS))))
        reviewable_because_pending = addon.latest_version is not None and (
            len(addon.latest_version.is_unreviewed) > 0 or
            addon.status == amo.STATUS_LITE_AND_NOMINATED)
        if (reviewable_because_complete and
                reviewable_because_admin and
                reviewable_because_submission_time and
                reviewable_because_pending):
            if self.review_type != 'preliminary':
                if addon.is_listed:
                    label = _lazy('Push to public')
                else:
                    label = _lazy('Grant full review')
                actions['public'] = {'method': self.handler.process_public,
                                     'minimal': False,
                                     'label': label}
            # An unlisted sideload add-on, which requests a full review, cannot
            # be granted a preliminary review.
            prelim_allowed = not waffle.flag_is_active(
                request, 'no-prelim-review') and addon.is_listed
            if prelim_allowed or self.review_type == 'preliminary':
                actions['prelim'] = {
                    'method': self.handler.process_preliminary,
                    'label': labels['prelim'],
                    'minimal': False}
            actions['reject'] = {'method': self.handler.process_sandbox,
                                 'label': _lazy('Reject'),
                                 'minimal': False}
        actions['info'] = {'method': self.handler.request_information,
                           'label': _lazy('Request more information'),
                           'minimal': True}
        actions['super'] = {'method': self.handler.process_super_review,
                            'label': _lazy('Request super-review'),
                            'minimal': True}
        actions['comment'] = {'method': self.handler.process_comment,
                              'label': _lazy('Comment'),
                              'minimal': True}
        for k, v in actions.items():
            v['details'] = details.get(k)

        return actions
示例#20
0
def _request_args():
    from django.contrib.staticfiles import finders
    from django.utils.translation import ugettext_lazy as _lazy

    site_logo = open(finders.find('img/qa-logo.png'), 'rb').read().encode('base64')
    logo_uri = urllib.quote('data:image/png;base64,{image}'.format(image=site_logo), safe=',:;/')
    return {
        'privacyPolicy': 'https://www.mozilla.org/privacy/websites/',
        'siteName': _lazy(u'One and Done'),
        'termsOfService': 'https://www.mozilla.org/about/legal/',
        'siteLogo': mark_safe(logo_uri),
        'backgroundColor': '#E0DDD4',
    }
示例#21
0
    def flags(self):
        props = (
            ("admin_review", "admin-review", _lazy("Admin Review")),
            ("is_jetpack", "jetpack", _lazy("Jetpack Add-on")),
            ("is_traditional_restartless", "restartless", _lazy("Restartless Add-on")),
            ("has_info_request", "info", _lazy("More Information Requested")),
            ("has_editor_comment", "editor", _lazy("Contains Editor Comment")),
            ("sources_provided", "sources-provided", _lazy("Sources provided")),
            ("is_webextension", "webextension", _lazy("WebExtension")),
        )

        return [(cls, title) for (prop, cls, title) in props if getattr(self, prop)]
示例#22
0
    def __init__(self, *args, **kw):
        super(ReviewLogForm, self).__init__(*args, **kw)

        # L10n: start, as in "start date"
        self.fields['start'].widget.attrs = {'placeholder': _('start'),
                                             'size': 10}

        # L10n: end, as in "end date"
        self.fields['end'].widget.attrs = {'size': 10, 'placeholder': _('end')}

        self.fields['search'].widget.attrs = {
            # L10n: Descript of what can be searched for.
            'placeholder': _lazy(u'app, reviewer, or comment'),
            'size': 30}
示例#23
0
文件: forms.py 项目: jostw/zamboni
    def __init__(self, *args, **kw):
        super(ReviewLogForm, self).__init__(*args, **kw)

        # L10n: start, as in "start date"
        self.fields["start"].widget.attrs = {"placeholder": _("start"), "size": 10}

        # L10n: end, as in "end date"
        self.fields["end"].widget.attrs = {"size": 10, "placeholder": _("end")}

        self.fields["search"].widget.attrs = {
            # L10n: Descript of what can be searched for.
            "placeholder": _lazy(u"app, reviewer, or comment"),
            "size": 30,
        }
示例#24
0
    def _hook_image_tag(self, parser, space, name):
        """Adds syntax for inserting images."""
        title, params = build_hook_params(name, self.locale, IMAGE_PARAMS,
                                          IMAGE_PARAM_VALUES)

        message = _lazy(u'The image "%s" does not exist.') % title
        image = get_object_fallback(Image, title, self.locale, message)
        if isinstance(image, basestring):
            return image

        template = jingo.get_env().get_template(self.image_template)
        r_kwargs = {'image': image, 'params': params,
                    'STATIC_URL': settings.STATIC_URL}
        return template.render(r_kwargs)
示例#25
0
    def process_request(self, request):
        try:
            auth = request.GET.get('auth')
        except IOError:
            # Django can throw an IOError when trying to read the GET
            # data.
            return

        if auth is None or (request.user and request.user.is_authenticated()):
            return
        user = authenticate(auth=auth)
        if user and user.is_active:
            login(request, user)
            msg = _lazy(u'You have been automatically logged in.')
            messages.success(request, msg)
示例#26
0
 def render_waiting_time_min(self, row):
     if row.waiting_time_min == 0:
         r = _lazy('moments ago')
     elif row.waiting_time_hours == 0:
         # L10n: first argument is number of minutes
         r = ngettext(u'{0} minute', u'{0} minutes',
                      row.waiting_time_min).format(row.waiting_time_min)
     elif row.waiting_time_days == 0:
         # L10n: first argument is number of hours
         r = ngettext(u'{0} hour', u'{0} hours',
                      row.waiting_time_hours).format(row.waiting_time_hours)
     else:
         # L10n: first argument is number of days
         r = ngettext(u'{0} day', u'{0} days',
                      row.waiting_time_days).format(row.waiting_time_days)
     return jinja2.escape(r)
示例#27
0
 def notify_email(self, template, subject):
     """Notify the authors that their addon has been reviewed."""
     emails = [a.email for a in self.addon.authors.all()]
     data = self.data.copy() if self.data else {}
     data.update(self.get_context_data())
     data['tested'] = ''
     os, app = data.get('operating_systems'), data.get('applications')
     if os and app:
         data['tested'] = 'Tested on %s with %s' % (os, app)
     elif os and not app:
         data['tested'] = 'Tested on %s' % os
     elif not os and app:
         data['tested'] = 'Tested with %s' % app
     data['addon_type'] = (_lazy('add-on'))
     send_mail('editors/emails/%s.ltxt' % template,
               subject % (self.addon.name, self.version.version),
               emails, Context(data), perm_setting='editor_reviewed')
示例#28
0
    def flags(self):
        props = (
            ('admin_review', 'admin-review', _lazy('Admin Review')),
            ('is_jetpack', 'jetpack', _lazy('Jetpack Add-on')),
            ('is_traditional_restartless', 'restartless',
             _lazy('Restartless Add-on')),
            ('has_info_request', 'info', _lazy('More Information Requested')),
            ('has_editor_comment', 'editor', _lazy('Contains Editor Comment')),
            ('sources_provided', 'sources-provided',
             _lazy('Sources provided')),
            ('is_webextension', 'webextension', _lazy('WebExtension')),
        )

        return [(cls, title) for (prop, cls, title) in props
                if getattr(self, prop)]
示例#29
0
文件: events.py 项目: 1234-/kitsune
    def _mails(self, users_and_watches):
        post_url = add_utm(self.reply.get_absolute_url(), 'forums-post')

        c = {'post': self.reply.content,
             'post_html': self.reply.content_parsed,
             'author': self.reply.author,
             'host': Site.objects.get_current().domain,
             'thread': self.reply.thread.title,
             'forum': self.reply.thread.forum.name,
             'post_url': post_url}

        return emails_with_users_and_watches(
            subject=_lazy(u'Re: {forum} - {thread}'),
            text_template='forums/email/new_post.ltxt',
            html_template='forums/email/new_post.html',
            context_vars=c,
            users_and_watches=users_and_watches)
示例#30
0
def get_flags(record):
    """Return a list of tuples (indicating which flags should be displayed for
    a particular add-on."""
    props = (
        ('admin_review', 'admin-review', _lazy('Admin Review')),
        ('is_jetpack', 'jetpack', _lazy('Jetpack Add-on')),
        ('requires_restart', 'requires_restart',
         _lazy('Requires Restart')),
        ('has_info_request', 'info', _lazy('More Information Requested')),
        ('has_editor_comment', 'editor', _lazy('Contains Editor Comment')),
        ('sources_provided', 'sources-provided',
         _lazy('Sources provided')),
        ('is_webextension', 'webextension', _lazy('WebExtension')),
    )

    return [(cls, title) for (prop, cls, title) in props
            if getattr(record, prop)]
示例#31
0
class AdvancedSearchForm(forms.Form):
    """Django form for handling display and validation"""

    # Common fields
    q = forms.CharField(required=False, max_length=MAX_QUERY_LENGTH)

    w = forms.TypedChoiceField(
        required=False,
        coerce=int,
        widget=forms.HiddenInput,
        empty_value=constants.WHERE_BASIC,
        choices=(
            (constants.WHERE_SUPPORT, None),
            (constants.WHERE_WIKI, None),
            (constants.WHERE_BASIC, None),
            (constants.WHERE_DISCUSSION, None),
        ),
    )

    # TODO: get rid of this.
    a = forms.IntegerField(required=False, widget=forms.HiddenInput)

    # KB fields
    topics = forms.MultipleChoiceField(required=False,
                                       widget=forms.CheckboxSelectMultiple(),
                                       label=_lazy("Topics"))

    language = forms.ChoiceField(required=False,
                                 label=_lazy("Language"),
                                 choices=SEARCH_LANGUAGES)

    category = TypedMultipleChoiceField(
        required=False,
        coerce=int,
        widget=forms.CheckboxSelectMultiple,
        label=_lazy("Category"),
        choices=CATEGORIES,
        coerce_only=True,
    )

    product = forms.MultipleChoiceField(required=False,
                                        label=_lazy("Relevant to"),
                                        widget=forms.CheckboxSelectMultiple())

    include_archived = forms.BooleanField(
        required=False, label=_lazy("Include obsolete articles?"))

    sortby_documents = forms.TypedChoiceField(
        required=False,
        empty_value=constants.SORTBY_DOCUMENTS_CHOICES[0][0],
        label=_lazy("Sort results by"),
        choices=constants.SORTBY_DOCUMENTS_CHOICES,
    )

    # Support questions and discussion forums fields
    created = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        label=_lazy("Created"),
        choices=constants.DATE_LIST,
    )

    created_date = forms.CharField(required=False)

    updated = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        label=_lazy("Last updated"),
        choices=constants.DATE_LIST,
    )
    updated_date = forms.CharField(required=False)

    user_widget = forms.TextInput(attrs={
        "placeholder": _lazy("username"),
        "class": "auto-fill"
    })
    # Discussion forums fields
    author = forms.CharField(required=False, widget=user_widget)

    sortby = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        label=_lazy("Sort results by"),
        choices=constants.SORTBY_FORUMS,
    )

    thread_type = TypedMultipleChoiceField(
        required=False,
        coerce=int,
        widget=forms.CheckboxSelectMultiple,
        label=_lazy("Thread type"),
        choices=constants.DISCUSSION_STATUS_LIST,
        coerce_only=True,
    )

    forum = TypedMultipleChoiceField(
        required=False,
        coerce=int,
        label=_lazy("Search in forum"),
        choices=[],  # Note: set choices with set_allowed_forums
        coerce_only=True,
    )

    # Support questions fields
    asked_by = forms.CharField(required=False, widget=user_widget)
    answered_by = forms.CharField(required=False, widget=user_widget)

    sortby_questions = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        label=_lazy("Sort results by"),
        choices=constants.SORTBY_QUESTIONS,
    )

    is_locked = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        widget=forms.RadioSelect,
        label=_lazy("Locked"),
        choices=constants.TERNARY_LIST,
    )

    is_archived = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        widget=forms.RadioSelect,
        label=_lazy("Archived"),
        choices=constants.TERNARY_LIST,
    )

    is_solved = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        widget=forms.RadioSelect,
        label=_lazy("Solved"),
        choices=constants.TERNARY_LIST,
    )

    has_answers = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        widget=forms.RadioSelect,
        label=_lazy("Has answers"),
        choices=constants.TERNARY_LIST,
    )

    has_helpful = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        widget=forms.RadioSelect,
        label=_lazy("Has helpful answers"),
        choices=constants.TERNARY_LIST,
    )

    num_voted = forms.TypedChoiceField(
        required=False,
        coerce=int,
        empty_value=0,
        label=_lazy("Votes"),
        choices=constants.NUMBER_LIST,
    )
    num_votes = forms.IntegerField(required=False)

    tag_widget = forms.TextInput(attrs={
        "placeholder": _lazy("tag1, tag2"),
        "class": "auto-fill"
    })
    q_tags = forms.CharField(label=_lazy("Tags"),
                             required=False,
                             widget=tag_widget)

    def __init__(self, *args, **kwargs):
        super(AdvancedSearchForm, self).__init__(*args, **kwargs)

        product_field = self.fields["product"]
        product_field.choices = Product.objects.values_list("slug", "title")

        topics_field = self.fields["topics"]
        topics_field.choices = Topic.objects.values_list("slug",
                                                         "title").distinct()

    def clean(self):
        """Clean up data and set defaults"""
        c = self.cleaned_data

        # Validate created and updated dates
        date_fields = (("created", "created_date"), ("updated",
                                                     "updated_date"))
        for field_option, field_date in date_fields:
            if c[field_date] != "":
                try:
                    created_timestamp = time.mktime(
                        time.strptime(c[field_date], "%m/%d/%Y"))
                    c[field_date] = int(created_timestamp)
                except (ValueError, OverflowError):
                    c[field_option] = None
            else:
                c[field_option] = None

        # Empty value defaults to int
        c["num_votes"] = c.get("num_votes") or 0
        return c

    def clean_category(self):
        category = self.cleaned_data["category"]
        if not category:
            category = settings.SEARCH_DEFAULT_CATEGORIES
        return category

    def set_allowed_forums(self, user):
        """Sets the 'forum' field choices to forums the user can see."""
        forums = [(f.id, f.name)
                  for f in DiscussionForum.authorized_forums_for_user(user)]
        self.fields["forum"].choices = forums
示例#32
0
class AppFormBasic(AddonFormBase):
    """Form to edit basic app info."""
    slug = forms.CharField(max_length=30, widget=forms.TextInput)
    manifest_url = forms.URLField()
    description = TransField(
        required=True,
        label=_lazy(u'Provide a detailed description of your app'),
        help_text=_lazy(u'This description will appear on the details page.'),
        widget=TransTextarea)
    tags = forms.CharField(
        label=_lazy(u'Search Keywords:'),
        required=False,
        widget=forms.Textarea(attrs={'rows': 3}),
        help_text=_lazy(
            u'The search keywords are used to return search results in the '
            u'Firefox Marketplace. Be sure to include a keywords that '
            u'accurately reflect your app.'))

    class Meta:
        model = Webapp
        fields = ('slug', 'manifest_url', 'description', 'tags')

    def __init__(self, *args, **kw):
        # Force the form to use app_slug. We want to keep
        # this under "slug" so all the js continues to work.
        kw.setdefault('initial', {})['slug'] = kw['instance'].app_slug

        super(AppFormBasic, self).__init__(*args, **kw)

        self.old_manifest_url = self.instance.manifest_url

        if self.instance.is_packaged:
            # Manifest URL cannot be changed for packaged apps.
            del self.fields['manifest_url']

        self.initial['tags'] = ', '.join(self.get_tags(self.instance))

    def clean_tags(self):
        return clean_tags(self.request, self.cleaned_data['tags'])

    def get_tags(self, addon):
        if can_edit_restricted_tags(self.request):
            return list(addon.tags.values_list('tag_text', flat=True))
        else:
            return list(
                addon.tags.filter(restricted=False).values_list('tag_text',
                                                                flat=True))

    def _post_clean(self):
        # Switch slug to app_slug in cleaned_data and self._meta.fields so
        # we can update the app_slug field for webapps.
        try:
            self._meta.fields = list(self._meta.fields)
            slug_idx = self._meta.fields.index('slug')
            data = self.cleaned_data
            if 'slug' in data:
                data['app_slug'] = data.pop('slug')
            self._meta.fields[slug_idx] = 'app_slug'
            self.fields['app_slug'] = self.fields['slug']
            super(AppFormBasic, self)._post_clean()
        finally:
            self._meta.fields[slug_idx] = 'slug'
            del self.fields['app_slug']

    def clean_slug(self):
        slug = self.cleaned_data['slug']
        slug_validator(slug, lower=False)

        if slug != self.instance.app_slug:
            if Webapp.objects.filter(app_slug=slug).exists():
                raise forms.ValidationError(
                    _('This slug is already in use. Please choose another.'))

            if BlockedSlug.blocked(slug):
                raise forms.ValidationError(
                    _('The slug cannot be "%s". '
                      'Please choose another.' % slug))

        return slug.lower()

    def clean_manifest_url(self):
        manifest_url = self.cleaned_data['manifest_url']
        # Only verify if manifest changed.
        if 'manifest_url' in self.changed_data:
            verify_app_domain(manifest_url, exclude=self.instance)
        return manifest_url

    def save(self, addon, commit=False):
        # We ignore `commit`, since we need it to be `False` so we can save
        # the ManyToMany fields on our own.
        addonform = super(AppFormBasic, self).save(commit=False)
        addonform.save()

        if 'manifest_url' in self.changed_data:
            before_url = self.old_manifest_url
            after_url = self.cleaned_data['manifest_url']

            # If a non-admin edited the manifest URL, add to Re-review Queue.
            if not acl.action_allowed(self.request, 'Admin', '%'):
                log.info(u'[Webapp:%s] (Re-review) Manifest URL changed '
                         u'from %s to %s' %
                         (self.instance, before_url, after_url))

                msg = (_(u'Manifest URL changed from {before_url} to '
                         u'{after_url}').format(before_url=before_url,
                                                after_url=after_url))

                RereviewQueue.flag(self.instance,
                                   mkt.LOG.REREVIEW_MANIFEST_URL_CHANGE, msg)

            # Refetch the new manifest.
            log.info('Manifest %s refreshed for %s' %
                     (addon.manifest_url, addon))
            update_manifests.delay([self.instance.id])

        tags_new = self.cleaned_data['tags']
        tags_old = [slugify(t, spaces=True) for t in self.get_tags(addon)]

        add_tags = set(tags_new) - set(tags_old)
        del_tags = set(tags_old) - set(tags_new)

        # Add new tags.
        for t in add_tags:
            Tag(tag_text=t).save_tag(addon)

        # Remove old tags.
        for t in del_tags:
            Tag(tag_text=t).remove_tag(addon)

        return addonform
示例#33
0
def yesno(boolean_value):
    return jinja2.Markup(_lazy(u'Yes') if boolean_value else _lazy(u'No'))
示例#34
0
 def __init__(self, *args, **kwargs):
     super(SearchFilter, self).__init__(*args, **kwargs)
     self.filters['timezone'].field.choices.insert(
         0, ('', _lazy(u'All timezones')))
示例#35
0
class RegionForm(forms.Form):
    regions = forms.MultipleChoiceField(
        required=False,
        choices=[],
        widget=forms.CheckboxSelectMultiple,
        label=_lazy(u'Choose the regions your app will be listed in:'),
        error_messages={
            'required': _lazy(u'You must select at least one region.')
        })
    enable_new_regions = forms.BooleanField(required=False,
                                            label=_lazy(u'Enable new regions'))
    restricted = forms.TypedChoiceField(
        required=False,
        initial=0,
        coerce=int,
        choices=[(0, _lazy('Make my app available in most regions')),
                 (1, _lazy('Choose where my app is made available'))],
        widget=forms.RadioSelect(attrs={'class': 'choices'}))

    def __init__(self, *args, **kw):
        self.product = kw.pop('product', None)
        self.request = kw.pop('request', None)
        super(RegionForm, self).__init__(*args, **kw)

        self.fields['regions'].choices = REGIONS_CHOICES_SORTED_BY_NAME()

        # This is the list of the user's exclusions as we don't
        # want the user's choices to be altered by external
        # exclusions e.g. payments availability.
        user_exclusions = list(
            self.product.addonexcludedregion.values_list('region', flat=True))

        # If we have excluded regions, uncheck those.
        # Otherwise, default to everything checked.
        self.regions_before = self.product.get_region_ids(
            restofworld=True, excluded=user_exclusions)

        self.initial = {
            'regions': sorted(self.regions_before),
            'restricted': int(self.product.geodata.restricted),
            'enable_new_regions': self.product.enable_new_regions,
        }

    @property
    def regions_by_id(self):
        return mkt.regions.REGIONS_CHOICES_ID_DICT

    def is_toggling(self):
        if not self.request or not hasattr(self.request, 'POST'):
            return False
        return self.product.premium_type != mkt.ADDON_FREE

    def _product_is_paid(self):
        return (self.product.premium_type in mkt.ADDON_PREMIUMS
                or self.product.premium_type == mkt.ADDON_FREE_INAPP)

    def clean_regions(self):
        regions = self.cleaned_data['regions']
        if not self.is_toggling():
            if not regions:
                raise forms.ValidationError(
                    _('You must select at least one region.'))
        return regions

    def save(self):
        # Don't save regions if we are toggling.
        if self.is_toggling():
            return

        regions = [int(x) for x in self.cleaned_data['regions']]
        restricted = int(self.cleaned_data['restricted'] or 0)

        if restricted:
            before = set(self.regions_before)
            after = set(regions)

            log.info(u'[Webapp:%s] App marked as restricted.' % self.product)

            # Add new region exclusions.
            to_add = before - after
            for region in to_add:
                aer, created = self.product.addonexcludedregion.get_or_create(
                    region=region)
                if created:
                    log.info(u'[Webapp:%s] Excluded from new region (%s).' %
                             (self.product, region))

            # Remove old region exclusions.
            to_remove = after - before
            for region in to_remove:
                self.product.addonexcludedregion.filter(region=region).delete()
                log.info(u'[Webapp:%s] No longer excluded from region (%s).' %
                         (self.product, region))

            # If restricted, check how we should handle new regions.
            if self.cleaned_data['enable_new_regions']:
                self.product.update(enable_new_regions=True)
                log.info(u'[Webapp:%s] will be added to future regions.' %
                         self.product)
            else:
                self.product.update(enable_new_regions=False)
                log.info(u'[Webapp:%s] will not be added to future regions.' %
                         self.product)
        else:
            # If not restricted, set `enable_new_regions` to True and remove
            # currently excluded regions.
            self.product.update(enable_new_regions=True)
            self.product.addonexcludedregion.all().delete()
            log.info(u'[Webapp:%s] App marked as unrestricted.' % self.product)

        self.product.geodata.update(restricted=restricted)
示例#36
0
class LICENSE_CC_BY_NC_SA(_LicenseBase):
    id = 5
    icons = 'cc-attrib cc-noncom cc-share'
    name = _lazy(u'Creative Commons Attribution-NonCommercial-Share Alike 3.0')
    url = 'http://creativecommons.org/licenses/by-nc-sa/3.0/'
示例#37
0
class UserRegisterForm(happyforms.ModelForm, UsernameMixin, PasswordMixin):
    """
    For registering users.  We're not building off
    d.contrib.auth.forms.UserCreationForm because it doesn't do a lot of the
    details here, so we'd have to rewrite most of it anyway.
    """
    username = forms.CharField(max_length=50, required=False)
    email = forms.EmailField(widget=RequiredEmailInput)
    display_name = forms.CharField(label=_lazy(u'Display Name'),
                                   max_length=50,
                                   required=False)
    location = forms.CharField(label=_lazy(u'Location'),
                               max_length=100,
                               required=False)
    occupation = forms.CharField(label=_lazy(u'Occupation'),
                                 max_length=100,
                                 required=False)
    password = forms.CharField(max_length=255,
                               min_length=PasswordMixin.min_length,
                               error_messages=PasswordMixin.error_msg,
                               widget=PasswordMixin.widget(render_value=False,
                                                           required=True))
    password2 = forms.CharField(max_length=255,
                                widget=PasswordMixin.widget(render_value=False,
                                                            required=True))
    recaptcha = ReCaptchaField()
    homepage = forms.URLField(label=_lazy(u'Homepage'), required=False)

    class Meta:
        model = UserProfile
        fields = ('username', 'display_name', 'location', 'occupation',
                  'password', 'password2', 'recaptcha', 'homepage', 'email')

    def __init__(self, *args, **kwargs):
        instance = kwargs.get('instance')
        if instance and instance.has_anonymous_username():
            kwargs.setdefault('initial', {})
            kwargs['initial']['username'] = ''

        super(UserRegisterForm, self).__init__(*args, **kwargs)

        if not settings.NOBOT_RECAPTCHA_PRIVATE_KEY:
            del self.fields['recaptcha']

        errors = {
            'invalid':
            _('This URL has an invalid format. '
              'Valid URLs look like '
              'http://example.com/my_page.')
        }
        self.fields['homepage'].error_messages = errors

    def clean_email(self):
        d = self.cleaned_data['email'].split('@')[-1]
        if BlacklistedEmailDomain.blocked(d):
            raise forms.ValidationError(
                _('Please use an email address from a '
                  'different provider to complete '
                  'your registration.'))
        return self.cleaned_data['email']

    def clean_display_name(self):
        name = self.cleaned_data['display_name']
        if BlacklistedName.blocked(name):
            raise forms.ValidationError(_('This display name cannot be used.'))
        return name

    def clean(self):
        super(UserRegisterForm, self).clean()

        data = self.cleaned_data

        # Passwords
        p1 = data.get('password')
        p2 = data.get('password2')

        # If p1 is invalid because its blocked, this message is non sensical.
        if p1 and p1 != p2:
            msg = _('The passwords did not match.')
            self._errors['password2'] = ErrorList([msg])
            if p2:
                del data['password2']

        return data
示例#38
0
)
from kitsune.questions.models import Answer, AnswerVote, Question, QuestionLocale, QuestionVote
from kitsune.questions.utils import get_mobile_product_from_ua, get_featured_articles
from kitsune.sumo.decorators import ratelimit, ssl_required
from kitsune.sumo.templatetags.jinja_helpers import urlparams
from kitsune.sumo.urlresolvers import reverse, split_path
from kitsune.sumo.utils import build_paged_url, is_ratelimited, paginate, simple_paginate
from kitsune.tags.utils import add_existing_tag
from kitsune.upload.models import ImageAttachment
from kitsune.upload.views import upload_imageattachment
from kitsune.users.models import Setting
from kitsune.wiki.facets import topics_for

log = logging.getLogger("k.questions")

UNAPPROVED_TAG = _lazy("That tag does not exist.")
NO_TAG = _lazy("Please provide a tag.")
IMG_LIMIT = settings.IMAGE_ATTACHMENT_USER_LIMIT

FILTER_GROUPS = {
    "all":
    OrderedDict([
        ("recently-unanswered", _lazy("Recently unanswered")),
    ]),
    "needs-attention":
    OrderedDict([
        ("new", _lazy("New")),
        ("unhelpful-answers", _lazy("Answers didn't help")),
    ]),
    "responded":
    OrderedDict([
示例#39
0
class GroupBase(models.Model):
    """Base class for groups in Mozillians."""
    name = models.CharField(db_index=True,
                            max_length=100,
                            unique=True,
                            verbose_name=_lazy(u'Name'))
    url = models.SlugField(blank=True)

    objects = GroupBaseManager.from_queryset(GroupQuerySet)()

    class Meta:
        abstract = True
        ordering = ['name']

    def get_absolute_url(self):
        cls_name = self.__class__.__name__
        url_pattern = 'groups:show_{0}'.format(cls_name.lower())
        return absolutify(reverse(url_pattern, args=[self.url]))

    def clean(self):
        """Verify that name is unique in ALIAS_MODEL."""

        super(GroupBase, self).clean()
        query = self.ALIAS_MODEL.objects.filter(name=self.name)
        if self.pk:
            query = query.exclude(alias=self)
        if query.exists():
            raise ValidationError({'name': _('This name already exists.')})
        return self.name

    @classmethod
    def search(cls, query):
        query = query.lower()
        results = cls.objects.filter(aliases__name__contains=query)
        results = results.distinct()
        return results

    def save(self, *args, **kwargs):
        """Override save method."""

        self.name = self.name.lower()
        super(GroupBase, self).save()
        if not self.url:
            alias = self.ALIAS_MODEL.objects.create(name=self.name, alias=self)
            self.url = alias.url
            super(GroupBase, self).save()

    def __unicode__(self):
        return self.name

    def merge_groups(self, group_list):
        """Merge two groups."""
        for group in group_list:
            map(lambda x: self.add_member(x), group.members.all())
            group.aliases.update(alias=self)
            group.delete()

    def user_can_leave(self, userprofile):
        """Checks if a member of a group can leave."""

        curators = self.curators.all()
        return (
            # some groups don't allow leaving
            getattr(self, 'members_can_leave', True)
            # We need at least one curator
            and (curators.count() > 1 or userprofile not in curators)
            # only makes sense to leave a group they belong to (at least pending)
            and (self.has_member(userprofile=userprofile)
                 or self.has_pending_member(userprofile=userprofile)))

    def curator_can_leave(self, userprofile):
        """Check if a curator can leave a group."""
        curators = self.curators.all()

        return curators.count() > 1 or userprofile not in curators

    def user_can_join(self, userprofile):
        """Checks if a user can join a group.

        Each one of the requirements must assert to True.
        """
        can_join_group = all([
            # Must be vouched and group must be Open
            userprofile.is_vouched,
            getattr(self, 'accepting_new_members', Group.OPEN) != Group.CLOSED,
            # only makes sense to join if not already a member
            not self.has_member(userprofile=userprofile),
            # or the membership is in a pending state
            not self.has_pending_member(userprofile=userprofile),
            # If this is an access group the user should be able to join
            (userprofile.can_join_access_groups()
             if type(self) is Group and self.is_access_group else True)
        ])
        return can_join_group

    # Read-only properties so clients don't care which subclasses have some fields
    @property
    def is_visible(self):
        """Checks if a group is visible."""
        return getattr(self, 'visible', True)

    def add_member(self, userprofile):
        """Adds a method to a group."""
        self.members.add(userprofile)

    def remove_member(self, userprofile):
        """Removes a member from a group."""
        self.members.remove(userprofile)

    def has_member(self, userprofile):
        """Checks membership status."""
        return self.members.filter(user=userprofile.user).exists()

    def has_pending_member(self, userprofile):
        """Checks if a membership is pending.

        Skills have no pending members, just members
        """
        return False
示例#40
0
class Group(GroupBase):
    """Group class."""
    ALIAS_MODEL = GroupAlias

    # Possible group types
    OPEN = u'yes'
    REVIEWED = u'by_request'
    CLOSED = u'no'

    GROUP_TYPES = (
        (OPEN, _lazy(u'Open')),
        (REVIEWED, _lazy(u'Reviewed')),
        (CLOSED, _lazy(u'Closed')),
    )

    ACCESS_GROUP = _lazy(u'Access Group')
    TAG = _lazy(u'Tag')
    ACCESS_GROUP_TYPES = (
        (True, ACCESS_GROUP),
        (False, TAG),
    )

    # Has a steward taken ownership of this group?
    description = models.TextField(max_length=1024,
                                   verbose_name=_lazy(u'Description'),
                                   default='',
                                   blank=True)
    curators = models.ManyToManyField('users.UserProfile',
                                      related_name='groups_curated')
    irc_channel = models.CharField(
        max_length=63,
        verbose_name=_lazy(u'IRC Channel'),
        help_text=_lazy(
            u'An IRC channel where this group is discussed (optional).'),
        default='',
        blank=True)
    website = models.URLField(
        max_length=200,
        verbose_name=_lazy(u'Website'),
        help_text=_lazy(
            u'A URL of a web site with more information about this group (optional).'
        ),
        default='',
        blank=True)
    wiki = models.URLField(
        max_length=200,
        verbose_name=_lazy(u'Wiki'),
        help_text=_lazy(
            u'A URL of a wiki with more information about this group (optional).'
        ),
        default='',
        blank=True)
    members_can_leave = models.BooleanField(default=True)
    accepting_new_members = models.CharField(
        verbose_name=_lazy(u'Accepting new members'),
        choices=GROUP_TYPES,
        default=OPEN,
        max_length=10)
    new_member_criteria = models.TextField(
        max_length=1024,
        default='',
        blank=True,
        verbose_name=_lazy(u'New Member Criteria'),
        help_text=_lazy(
            u'Specify the criteria you will use to decide whether or not '
            u'you will accept a membership request.'))
    functional_area = models.BooleanField(default=False)
    visible = models.BooleanField(
        default=True,
        help_text=_lazy(
            u'Whether group is shown on the UI (in group lists, search, etc). Mainly '
            u'intended to keep system groups like "staff" from cluttering up the '
            u'interface.'))
    max_reminder = models.IntegerField(
        default=0,
        help_text=
        (u'The max PK of pending membership requests the last time we sent the '
         u'curator a reminder'))

    terms = models.TextField(default='', verbose_name=_('Terms'), blank=True)
    invalidation_days = models.PositiveIntegerField(
        null=True,
        default=None,
        blank=True,
        verbose_name=_('Invalidation days'))
    invites = models.ManyToManyField('users.UserProfile',
                                     related_name='invites_received',
                                     through='Invite',
                                     through_fields=('group', 'redeemer'))
    invite_email_text = models.TextField(
        max_length=2048,
        default='',
        blank=True,
        help_text=_('Please enter any additional text for the '
                    'invitation email'))
    is_access_group = models.BooleanField(
        default=False,
        choices=ACCESS_GROUP_TYPES,
        verbose_name='Is this an access group?')

    objects = GroupManager.from_queryset(GroupQuerySet)()

    @classmethod
    def get_functional_areas(cls):
        """Return all visible groups that are functional areas."""
        return cls.objects.visible().filter(functional_area=True)

    @classmethod
    def get_non_functional_areas(cls, **kwargs):
        """
        Return all visible groups that are not functional areas.

        Use kwargs to apply additional filtering to the groups.
        """
        return cls.objects.visible().filter(functional_area=False, **kwargs)

    @classmethod
    def get_curated(cls):
        """Return all non-functional areas that are curated."""
        return cls.get_non_functional_areas(curators__isnull=False)

    @classmethod
    def search(cls, query):
        return super(Group, cls).search(query).visible()

    def merge_groups(self, group_list):
        for membership in GroupMembership.objects.filter(group__in=group_list):
            # add_member will never demote someone, so just add them with the current membership
            # level from the merging group and they'll end up with the highest level from
            # either group.
            self.add_member(membership.userprofile, membership.status)

        for group in group_list:
            group.aliases.update(alias=self)
            group.delete()

    def add_member(self,
                   userprofile,
                   status=GroupMembership.MEMBER,
                   inviter=None):
        """
        Add a user to this group. Optionally specify status other than member.

        If user is already in the group with the given status, this is a no-op.

        If user is already in the group with a different status, their status will
        be updated if the change is a promotion. Otherwise, their status will not change.

        If the group in question is the NDA group, also add the user to the NDA newsletter.
        """
        defaults = dict(status=status, date_joined=now())
        membership, _ = GroupMembership.objects.get_or_create(
            userprofile=userprofile, group=self, defaults=defaults)

        # Remove the need_removal flag in any case
        # We have a renewal, let's save the object.
        if membership.needs_renewal:
            membership.needs_renewal = False
            membership.save()

        if membership.status != status:
            # Status changed
            # The only valid status change states are:
            # PENDING to MEMBER
            # PENDING to PENDING_TERMS
            # PENDING_TERMS to MEMBER

            old_status = membership.status
            membership.status = status
            statuses = [
                (GroupMembership.PENDING, GroupMembership.MEMBER),
                (GroupMembership.PENDING, GroupMembership.PENDING_TERMS),
                (GroupMembership.PENDING_TERMS, GroupMembership.MEMBER)
            ]

            if (old_status, status) in statuses:
                # Status changed
                membership.save()

        if inviter:
            # Set the invite to the last person who renewed the membership
            invite = get_object_or_none(Invite,
                                        group=membership.group,
                                        redeemer=userprofile)
            if invite:
                invite.inviter = inviter
                invite.save()

    def remove_member(self, userprofile, status=None, send_email=False):
        """Change membership status for a group.

        If user is a member of an open group, then the user is removed.

        If a user is a member of a reviewed or closed group,
        then the membership is in a pending state.
        """
        # Avoid circular dependencies
        from mozillians.users.models import UserProfile

        try:
            membership = GroupMembership.objects.get(group=self,
                                                     userprofile=userprofile)
        except GroupMembership.DoesNotExist:
            return
        old_status = membership.status

        # If the group is of type Group.OPEN, delete membership
        # If no status is given, delete membership,
        # If the current membership is PENDING*, delete membership
        if (not status or self.accepting_new_members == Group.OPEN
                or old_status != GroupMembership.MEMBER):
            # We have either an open group or the request to join a reviewed group is denied
            # or the curator manually declined a user in a pending state.
            membership.delete()
            # delete the invitation to the group if exists
            Invite.objects.filter(group=self, redeemer=userprofile).delete()
            send_email = True
            # Remove all the access groups the user is a member of
            # if the group to remove is the NDA
            if not GroupMembership.objects.filter(
                    userprofile=userprofile,
                    group__name__in=settings.NDA_ACCESS_GROUPS):
                group_memberships = GroupMembership.objects.none()
                # If the user is not staff, we need to delete the memberships to any access group
                if not userprofile.can_create_access_groups:
                    group_memberships = GroupMembership.objects.filter(
                        userprofile=userprofile, group__is_access_group=True)

                for access_membership in group_memberships:
                    group = access_membership.group
                    if not group.curator_can_leave(userprofile):
                        # If the user is the only curator, let's add the superusers as curators
                        # as a fallback option
                        for super_user in UserProfile.objects.filter(
                                user__is_superuser=True):
                            group.curators.add(super_user)
                            if not group.has_member(super_user):
                                group.add_member(super_user)
                    group.curators.remove(userprofile)
                    access_membership.delete()

        # Group is either of Group.REVIEWED or Group.CLOSED, change membership to `status`
        else:
            # if we are here, there is a new status for our user
            membership.status = status
            membership.needs_renewal = False
            membership.save()

    def has_member(self, userprofile):
        """
        Return True if this user is in this group with status MEMBER.
        """
        return self.groupmembership_set.filter(
            userprofile=userprofile, status=GroupMembership.MEMBER).exists()

    def has_pending_member(self, userprofile):
        """
        Return True if this user is in this group with status PENDING or
        there is a flag marking the profile ready for a renewal
        """
        return (self.groupmembership_set.filter(
            userprofile=userprofile).filter(
                Q(status=GroupMembership.PENDING)
                | Q(needs_renewal=True))).exists()
示例#41
0
class UserProfile(UserProfilePrivacyModel):
    objects = ProfileManager()

    user = models.OneToOneField(User)
    full_name = models.CharField(max_length=255,
                                 default='',
                                 blank=False,
                                 verbose_name=_lazy(u'Full Name'))
    is_vouched = models.BooleanField(
        default=False,
        help_text='You can edit vouched status by editing invidual vouches')
    can_vouch = models.BooleanField(
        default=False,
        help_text='You can edit can_vouch status by editing invidual vouches')
    last_updated = models.DateTimeField(auto_now=True)
    bio = models.TextField(verbose_name=_lazy(u'Bio'), default='', blank=True)
    photo = ImageField(default='',
                       blank=True,
                       upload_to=_calculate_photo_filename)

    # validated geo data (validated that it's valid geo data, not that the
    # mozillian is there :-) )
    geo_country = models.ForeignKey('geo.Country',
                                    blank=True,
                                    null=True,
                                    on_delete=models.SET_NULL)
    geo_region = models.ForeignKey('geo.Region',
                                   blank=True,
                                   null=True,
                                   on_delete=models.SET_NULL)
    geo_city = models.ForeignKey('geo.City',
                                 blank=True,
                                 null=True,
                                 on_delete=models.SET_NULL)
    lat = models.FloatField(_lazy(u'Latitude'), blank=True, null=True)
    lng = models.FloatField(_lazy(u'Longitude'), blank=True, null=True)

    # django-cities-light fields
    city = models.ForeignKey('cities_light.City',
                             blank=True,
                             null=True,
                             on_delete=models.SET_NULL)
    region = models.ForeignKey('cities_light.Region',
                               blank=True,
                               null=True,
                               on_delete=models.SET_NULL)
    country = models.ForeignKey('cities_light.Country',
                                blank=True,
                                null=True,
                                on_delete=models.SET_NULL)

    date_mozillian = models.DateField('When was involved with Mozilla',
                                      null=True,
                                      blank=True,
                                      default=None)
    timezone = models.CharField(max_length=100,
                                blank=True,
                                default='',
                                choices=zip(common_timezones,
                                            common_timezones))
    title = models.CharField(_lazy(u'What do you do for Mozilla?'),
                             max_length=70,
                             blank=True,
                             default='')

    story_link = models.URLField(
        _lazy(u'Link to your contribution story'),
        help_text=_lazy(u'If you have created something public that '
                        u'tells the story of how you came to be a '
                        u'Mozillian, specify that link here.'),
        max_length=1024,
        blank=True,
        default='')
    # This is the Auth0 user ID. We are saving only the primary here.
    auth0_user_id = models.CharField(max_length=1024, default='', blank=True)
    is_staff = models.BooleanField(default=False)

    def __unicode__(self):
        """Return this user's name when their profile is called."""
        return self.display_name

    def get_absolute_url(self):
        return reverse('phonebook:profile_view', args=[self.user.username])

    class Meta:
        db_table = 'profile'
        ordering = ['full_name']

    def __getattribute__(self, attrname):
        """Special privacy aware __getattribute__ method.

        This method returns the real value of the attribute of object,
        if the privacy_level of the attribute is at least as large as
        the _privacy_level attribute.

        Otherwise it returns a default privacy respecting value for
        the attribute, as defined in the privacy_fields dictionary.

        special_functions provides methods that privacy safe their
        respective properties, where the privacy modifications are
        more complex.
        """
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))
        privacy_fields = UserProfile.privacy_fields()
        privacy_level = _getattr('_privacy_level')
        special_functions = {
            'accounts': '_accounts',
            'alternate_emails': '_alternate_emails',
            'email': '_primary_email',
            'is_public_indexable': '_is_public_indexable',
            'languages': '_languages',
            'vouches_made': '_vouches_made',
            'vouches_received': '_vouches_received',
            'vouched_by': '_vouched_by',
            'websites': '_websites',
            'identity_profiles': '_identity_profiles'
        }

        if attrname in special_functions:
            return _getattr(special_functions[attrname])

        if not privacy_level or attrname not in privacy_fields:
            return _getattr(attrname)

        field_privacy = _getattr('privacy_%s' % attrname)
        if field_privacy < privacy_level:
            return privacy_fields.get(attrname)

        return _getattr(attrname)

    def _filter_accounts_privacy(self, accounts):
        if self._privacy_level:
            return accounts.filter(privacy__gte=self._privacy_level)
        return accounts

    @property
    def _accounts(self):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))
        excluded_types = [
            ExternalAccount.TYPE_WEBSITE, ExternalAccount.TYPE_EMAIL
        ]
        accounts = _getattr('externalaccount_set').exclude(
            type__in=excluded_types)
        return self._filter_accounts_privacy(accounts)

    @property
    def _alternate_emails(self):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))
        accounts = _getattr('externalaccount_set').filter(
            type=ExternalAccount.TYPE_EMAIL)
        return self._filter_accounts_privacy(accounts)

    @property
    def _identity_profiles(self):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))
        accounts = _getattr('idp_profiles').all()
        return self._filter_accounts_privacy(accounts)

    @property
    def _is_public_indexable(self):
        for field in PUBLIC_INDEXABLE_FIELDS:
            if getattr(self, field, None) and getattr(
                    self, 'privacy_%s' % field, None) == PUBLIC:
                return True
        return False

    @property
    def _languages(self):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))
        if self._privacy_level > _getattr('privacy_languages'):
            return _getattr('language_set').none()
        return _getattr('language_set').all()

    @property
    def _primary_email(self):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))

        privacy_fields = UserProfile.privacy_fields()

        if self._privacy_level:
            # Try IDP contact first
            if self.idp_profiles.exists():
                contact_ids = self.identity_profiles.filter(
                    primary_contact_identity=True)
                if contact_ids.exists():
                    return contact_ids[0].email
                return ''

            # Fallback to user.email
            if _getattr('privacy_email') < self._privacy_level:
                return privacy_fields['email']

        # In case we don't have a privacy aware attribute access
        if self.idp_profiles.filter(primary_contact_identity=True).exists():
            return self.idp_profiles.filter(
                primary_contact_identity=True)[0].email
        return _getattr('user').email

    @property
    def _vouched_by(self):
        privacy_level = self._privacy_level
        voucher = (UserProfile.objects.filter(
            vouches_made__vouchee=self).order_by('vouches_made__date'))

        if voucher.exists():
            voucher = voucher[0]
            if privacy_level:
                voucher.set_instance_privacy_level(privacy_level)
                for field in UserProfile.privacy_fields():
                    if getattr(voucher, 'privacy_%s' % field) >= privacy_level:
                        return voucher
                return None
            return voucher

        return None

    def _vouches(self, type):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))

        vouch_ids = []
        for vouch in _getattr(type).all():
            vouch.vouchee.set_instance_privacy_level(self._privacy_level)
            for field in UserProfile.privacy_fields():
                if getattr(vouch.vouchee, 'privacy_%s' % field,
                           0) >= self._privacy_level:
                    vouch_ids.append(vouch.id)
        vouches = _getattr(type).filter(pk__in=vouch_ids)

        return vouches

    @property
    def _vouches_made(self):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))
        if self._privacy_level:
            return self._vouches('vouches_made')
        return _getattr('vouches_made')

    @property
    def _vouches_received(self):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))
        if self._privacy_level:
            return self._vouches('vouches_received')
        return _getattr('vouches_received')

    @property
    def _websites(self):
        _getattr = (lambda x: super(UserProfile, self).__getattribute__(x))
        accounts = _getattr('externalaccount_set').filter(
            type=ExternalAccount.TYPE_WEBSITE)
        return self._filter_accounts_privacy(accounts)

    @property
    def display_name(self):
        return self.full_name

    @property
    def privacy_level(self):
        """Return user privacy clearance."""
        if self.user.is_superuser:
            return PRIVATE
        if self.groups.filter(name='staff').exists():
            return EMPLOYEES
        if self.is_vouched:
            return MOZILLIANS
        return PUBLIC

    @property
    def is_complete(self):
        """Tests if a user has all the information needed to move on
        past the original registration view.

        """
        return self.display_name.strip() != ''

    @property
    def is_public(self):
        """Return True is any of the privacy protected fields is PUBLIC."""
        # TODO needs update

        for field in type(self).privacy_fields():
            if getattr(self, 'privacy_%s' % field, None) == PUBLIC:
                return True
        return False

    @property
    def is_manager(self):
        return self.user.is_superuser

    @property
    def date_vouched(self):
        """ Return the date of the first vouch, if available."""
        vouches = self.vouches_received.all().order_by('date')[:1]
        if vouches:
            return vouches[0].date
        return None

    def set_instance_privacy_level(self, level):
        """Sets privacy level of instance."""
        self._privacy_level = level

    def set_privacy_level(self, level, save=True):
        """Sets all privacy enabled fields to 'level'."""
        for field in type(self).privacy_fields():
            setattr(self, 'privacy_%s' % field, level)
        if save:
            self.save()

    def get_photo_thumbnail(self, geometry='160x160', **kwargs):
        if 'crop' not in kwargs:
            kwargs['crop'] = 'center'

        if self.photo and default_storage.exists(self.photo.name):
            # Workaround for legacy images in RGBA model

            try:
                image_obj = Image.open(self.photo)
            except IOError:
                return get_thumbnail(settings.DEFAULT_AVATAR_PATH, geometry,
                                     **kwargs)

            if image_obj.mode == 'RGBA':
                new_fh = default_storage.open(self.photo.name, 'w')
                converted_image_obj = image_obj.convert('RGB')
                converted_image_obj.save(new_fh, 'JPEG')
                new_fh.close()

            return get_thumbnail(self.photo, geometry, **kwargs)
        return get_thumbnail(settings.DEFAULT_AVATAR_PATH.format(), geometry,
                             **kwargs)

    def get_photo_url(self, geometry='160x160', **kwargs):
        """Return photo url.

        If privacy allows and no photo set, return gravatar link.
        If privacy allows and photo set return local photo link.
        If privacy doesn't allow return default local link.
        """
        privacy_level = getattr(self, '_privacy_level', MOZILLIANS)
        if (not self.photo and self.privacy_photo >= privacy_level):
            return gravatar(self.email, size=geometry)

        photo_url = self.get_photo_thumbnail(geometry, **kwargs).url
        if photo_url.startswith('https://') or photo_url.startswith('http://'):
            return photo_url
        return absolutify(photo_url)

    def is_vouchable(self, voucher):
        """Check whether self can receive a vouch from voucher."""
        # If there's a voucher, they must be able to vouch.
        if voucher and not voucher.can_vouch:
            return False

        # Maximum VOUCH_COUNT_LIMIT vouches per account, no matter what.
        if self.vouches_received.all().count() >= settings.VOUCH_COUNT_LIMIT:
            return False

        # If you've already vouched this account, you cannot do it again
        vouch_query = self.vouches_received.filter(voucher=voucher)
        if voucher and vouch_query.exists():
            return False

        return True

    def timezone_offset(self):
        """
        Return minutes the user's timezone is offset from UTC.  E.g. if user is
        4 hours behind UTC, returns -240.
        If user has not set a timezone, returns None (not 0).
        """
        if self.timezone:
            return offset_of_timezone(self.timezone)

    def save(self, *args, **kwargs):
        self._privacy_level = None
        autovouch = kwargs.pop('autovouch', False)

        super(UserProfile, self).save(*args, **kwargs)
        # Auto_vouch follows the first save, because you can't
        # create foreign keys without a database id.

        if autovouch:
            self.auto_vouch()
示例#42
0
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse as django_reverse
from django.http import HttpResponsePermanentRedirect, HttpResponseRedirect
from django.utils.encoding import iri_to_uri, smart_str

from django.utils.translation import ugettext_lazy as _lazy, activate

from mozillians.common import urlresolvers
from mozillians.common.templatetags.helpers import redirect, urlparams
from mozillians.common.urlresolvers import reverse


LOGIN_MESSAGE = _lazy(u'You must be logged in to continue.')
GET_VOUCHED_MESSAGE = _lazy(u'You must be vouched to continue.')


class StrongholdMiddleware(object):
    """Keep unvouched users out, unless explicitly allowed in.

    Inspired by https://github.com/mgrouchy/django-stronghold/

    """

    def __init__(self, get_response):
        self.get_response = get_response
        self.exceptions = getattr(settings, 'STRONGHOLD_EXCEPTIONS', [])

    def __call__(self, request):
示例#43
0
class LICENSE_CC_BY_SA(_LicenseBase):
    id = 7
    icons = 'cc-attrib cc-share'
    name = _lazy(u'Creative Commons Attribution-ShareAlike 3.0')
    url = 'http://creativecommons.org/licenses/by-sa/3.0/'
示例#44
0
class LICENSE_CC_BY(_LicenseBase):
    id = 2
    name = _lazy(u'Creative Commons Attribution 3.0')
    url = 'http://creativecommons.org/licenses/by/3.0/'
    icons = 'cc-attrib'
示例#45
0
class LICENSE_CC_BY_ND(_LicenseBase):
    id = 6
    icons = 'cc-attrib cc-noderiv'
    name = _lazy(u'Creative Commons Attribution-NoDerivs 3.0')
    url = 'http://creativecommons.org/licenses/by-nd/3.0/'
示例#46
0
class LICENSE_COPYRIGHT(_LicenseBase):
    id = 1
    name = _lazy(u'All Rights Reserved')
    icons = 'copyr'
    some_rights = False
    url = None
示例#47
0
class LICENSE_CC_BY_NC(_LicenseBase):
    id = 3
    icons = 'cc-attrib cc-noncom'
    name = _lazy(u'Creative Commons Attribution-NonCommercial 3.0')
    url = 'http://creativecommons.org/licenses/by-nc/3.0/'
示例#48
0
class PublishForm(happyforms.Form):
    # Publish choice wording is slightly different here than with the
    # submission flow because the app may have already been published.
    mark_safe_lazy = lazy(mark_safe, six.text_type)
    PUBLISH_CHOICES = (
        (mkt.PUBLISH_IMMEDIATE,
         mark_safe_lazy(
             _lazy(
                 u'<b>Published</b>: Visible to everyone in the Marketplace and '
                 u'included in search results and listing pages.'))),
        (mkt.PUBLISH_HIDDEN,
         mark_safe_lazy(
             _lazy(u'<b>Unlisted</b>: Visible to only people with the URL and '
                   u'does not appear in search results and listing pages.'))),
    )

    # Used for setting initial form values.
    PUBLISH_MAPPING = {
        mkt.STATUS_PUBLIC: mkt.PUBLISH_IMMEDIATE,
        mkt.STATUS_UNLISTED: mkt.PUBLISH_HIDDEN,
        mkt.STATUS_APPROVED: mkt.PUBLISH_PRIVATE,
    }
    # Use in form processing to set status.
    STATUS_MAPPING = dict((v, k) for k, v in PUBLISH_MAPPING.items())

    publish_type = forms.TypedChoiceField(required=False,
                                          choices=PUBLISH_CHOICES,
                                          widget=forms.RadioSelect(),
                                          initial=0,
                                          coerce=int,
                                          label=_lazy('App Visibility:'))
    limited = forms.BooleanField(
        required=False,
        label=_lazy(u'<b>Limit to my team</b>: Visible to only Team Members.'))

    def __init__(self, *args, **kwargs):
        self.addon = kwargs.pop('addon')
        super(PublishForm, self).__init__(*args, **kwargs)

        limited = False
        publish = self.PUBLISH_MAPPING.get(self.addon.status,
                                           mkt.PUBLISH_IMMEDIATE)
        if self.addon.status == mkt.STATUS_APPROVED:
            # Special case if app is currently private.
            limited = True
            publish = mkt.PUBLISH_HIDDEN

        # Determine the current selection via STATUS to publish choice mapping.
        self.fields['publish_type'].initial = publish
        self.fields['limited'].initial = limited

        # Make the limited label safe so we can display the HTML.
        self.fields['limited'].label = mark_safe(self.fields['limited'].label)

    def save(self):
        publish = self.cleaned_data['publish_type']
        limited = self.cleaned_data['limited']

        if publish == mkt.PUBLISH_HIDDEN and limited:
            publish = mkt.PUBLISH_PRIVATE

        status = self.STATUS_MAPPING[publish]
        self.addon.update(status=status)

        mkt.log(mkt.LOG.CHANGE_STATUS, self.addon.get_status_display(),
                self.addon)
        # Call update_version, so various other bits of data update.
        self.addon.update_version()
        # Call to update names and locales if changed.
        self.addon.update_name_from_package_manifest()
        self.addon.update_supported_locales()

        if waffle.switch_is_active('iarc-upgrade-v2'):
            iarc_publish.delay(self.addon.pk)
        else:
            set_storefront_data.delay(self.addon.pk)
示例#49
0
 class Meta:
     verbose_name = _lazy('grupo')
     verbose_name_plural = _lazy('grupos')
示例#50
0
 class Meta:
     verbose_name = _lazy('Historial')
     verbose_name_plural = _lazy('Historiales')
     ordering = ['-fecha']
示例#51
0
文件: forms.py 项目: zu83/kitsune
        super(GroupAvatarForm, self).__init__(*args, **kwargs)
        self.fields["avatar"].help_text = _("Your avatar will be resized to {size}x{size}").format(
            size=settings.AVATAR_SIZE
        )

    class Meta(object):
        model = GroupProfile
        fields = ["avatar"]

    def clean_avatar(self):
        if not ("avatar" in self.cleaned_data and self.cleaned_data["avatar"]):
            return self.cleaned_data["avatar"]
        try:
            check_file_size(self.cleaned_data["avatar"], settings.MAX_AVATAR_FILE_SIZE)
        except FileTooLargeError as e:
            raise forms.ValidationError(e.args[0])
        return self.cleaned_data["avatar"]


USERS_PLACEHOLDER = _lazy("username")


class AddUserForm(forms.Form):
    """Form to add members or leaders to group."""

    users = MultiUsernameField(
        widget=forms.TextInput(
            attrs={"placeholder": USERS_PLACEHOLDER, "class": "user-autocomplete"}
        )
    )
示例#52
0
import logging

from collections import OrderedDict

from django.conf import settings
from django.utils.translation import ugettext_lazy as _lazy

from django_statsd.clients import statsd
from zendesk import Zendesk, ZendeskError

log = logging.getLogger('k.questions.marketplace')

MARKETPLACE_CATEGORIES = OrderedDict([
    ('payments', _lazy('Payments')),
    ('applications', _lazy('Applications')),
    ('account', _lazy('Account')),
])


class ZendeskSettingsError(ZendeskError):
    """Exception for missing settings."""


def get_zendesk():
    """Instantiate and return a Zendesk client"""
    # Verify required Zendesk settings
    zendesk_url = settings.ZENDESK_URL
    zendesk_email = settings.ZENDESK_USER_EMAIL
    zendesk_password = settings.ZENDESK_USER_PASSWORD
    if not zendesk_url or not zendesk_email or not zendesk_password:
        log.error('Zendesk settings error: please set ZENDESK_URL, '
示例#53
0
def validate_email(value):
    if not re.search(EMAIL_REGEX, value):
        raise ValidationError(_lazy("Invalid Email"))
    return value
示例#54
0
class HistorialEstado(models.Model):
    """
    Modelo para guardar historial de cambio de estado de los grupos.

        **Significado de cada estado**

           * ``ACTIVO`` Este estado es aplicado a grupos que realicen todas las funciones
             que un grupo hace, es decir: reunion de G.A.R, reunion de discipulado, etc.

           * ``INACTIVO`` Este estado es aplicado a grupos que en la actualidad no se
             encuentran realizando reuniones de G.A.R, pero realizan encuentros, reuniones
             de dicipulado, etc.

           * ``SUSPENDIDO`` Este estado es aplicado a grupos que en la actualidad no realizan
             ninguna acción de grupos, pero no quiere ser archivado.

           * ``ARCHIVADO`` Este estado es aplicado a grupos que serán eliminados.


        **Usabilidad**

            Solo se podrá tener un ``estado`` diferente de seguido para cada grupo, es decir,
            no se podrán tener dos estados de ``ACTIVO`` de seguido para el mismo grupo.

            Ante el caso de una posible repetición de ``estado``, la creación de el historial del
            estado será abortada.
    """

    ACTIVO = 'AC'
    INACTIVO = 'IN'
    SUSPENDIDO = 'SU'
    ARCHIVADO = 'AR'

    OPCIONES_ESTADO = (
        (ACTIVO, 'ACTIVO'),
        (INACTIVO, 'INACTIVO'),
        (SUSPENDIDO, 'SUSPENDIDO'),
        (ARCHIVADO, 'ARCHIVADO'),
    )

    grupo = models.ForeignKey(Grupo, related_name='historiales', verbose_name=_lazy('grupo'))
    fecha = models.DateTimeField(verbose_name=_lazy('fecha'), auto_now_add=True)
    estado = models.CharField(max_length=2, choices=OPCIONES_ESTADO, verbose_name=_lazy('estado'))

    objects = HistorialManager()

    def __str__(self):
        return 'Historial {estado} para grupo: {self.grupo}'.format(self=self, estado=self.get_estado_display())

    class Meta:
        verbose_name = _lazy('Historial')
        verbose_name_plural = _lazy('Historiales')
        ordering = ['-fecha']

    def save(self, *args, **kwargs):
        """
        Guarda una instancia de EstadoHistorial

        .. warning::
            Si el grupo tiene en su último historial, el mismo estado del historial actual
            a guardar, entonces, este será omitido.
        """

        with transaction.atomic():
            last = self.grupo.historiales.first()
            if last is None or last.estado != self.estado:
                if self.estado == self.ARCHIVADO:
                    self.grupo.nombre = '{} (ARCHIVADO)'.format(self.grupo.__str__())
                    self.grupo.save()
                return super().save(*args, **kwargs)
示例#55
0
class ExternalAccount(models.Model):
    # Constants for type field values.
    TYPE_AMO = 'AMO'
    TYPE_BMO = 'BMO'
    TYPE_EMAIL = 'EMAIL'
    TYPE_MDN = 'MDN'
    TYPE_SUMO = 'SUMO'
    TYPE_FACEBOOK = 'FACEBOOK'
    TYPE_TWITTER = 'TWITTER'
    TYPE_AIM = 'AIM'
    TYPE_SKYPE = 'SKYPE'
    TYPE_YAHOO = 'YAHOO'
    TYPE_WEBSITE = 'WEBSITE'
    TYPE_BITBUCKET = 'BITBUCKET'
    TYPE_SLIDESHARE = 'SLIDESHARE'
    TYPE_WEBMAKER = 'WEBMAKER'
    TYPE_MOWIKI = 'MOZILLAWIKI'
    TYPE_REMO = 'REMO'
    TYPE_LINKEDIN = 'LINKEDIN'
    TYPE_JABBER = 'JABBER'
    TYPE_DISCOURSE = 'DISCOURSE'
    TYPE_LANYRD = 'LANYRD'
    TYPE_LANDLINE = 'Phone (Landline)'
    TYPE_MOBILE = 'Phone (Mobile)'
    TYPE_MOPONTOON = 'MOZILLAPONTOON'
    TYPE_TRANSIFEX = 'TRANSIFEX'
    TYPE_TELEGRAM = 'TELEGRAM'
    TYPE_MASTODON = 'MASTODON'
    TYPE_DISCORD = 'DISCORD'
    TYPE_MOZPHAB = 'MOZPHAB'

    # Account type field documentation:
    # name: The name of the service that this account belongs to. What
    #       users see
    # url: If the service features profile pages for its users, then
    #      this field should be a link to that profile page. User's
    #      identifier should be replaced by the special string
    #      {identifier}.
    # validator: Points to a function which will clean and validate
    #            user's entry. Function should return the cleaned
    #            data.
    ACCOUNT_TYPES = {
        TYPE_AMO: {
            'name': 'Mozilla Add-ons',
            'url': 'https://addons.mozilla.org/user/{identifier}/',
            'validator': validate_username_not_url
        },
        TYPE_BMO: {
            'name': 'Bugzilla (BMO)',
            'url':
            'https://bugzilla.mozilla.org/user_profile?login={identifier}',
            'validator': validate_username_not_url
        },
        TYPE_EMAIL: {
            'name': 'Alternate email address',
            'url': '',
            'validator': validate_email
        },
        TYPE_BITBUCKET: {
            'name': 'Bitbucket',
            'url': 'https://bitbucket.org/{identifier}',
            'validator': validate_username_not_url
        },
        TYPE_MDN: {
            'name': 'MDN',
            'url': 'https://developer.mozilla.org/profiles/{identifier}',
            'validator': validate_username_not_url
        },
        TYPE_SUMO: {
            'name': 'Mozilla Support',
            'url': 'https://support.mozilla.org/user/{identifier}',
            'validator': validate_username_not_url
        },
        TYPE_FACEBOOK: {
            'name': 'Facebook',
            'url': 'https://www.facebook.com/{identifier}',
            'validator': validate_username_not_url
        },
        TYPE_TWITTER: {
            'name': 'Twitter',
            'url': 'https://twitter.com/{identifier}',
            'validator': validate_twitter
        },
        TYPE_AIM: {
            'name': 'AIM',
            'url': ''
        },
        TYPE_SKYPE: {
            'name': 'Skype',
            'url': ''
        },
        TYPE_SLIDESHARE: {
            'name': 'SlideShare',
            'url': 'http://www.slideshare.net/{identifier}',
            'validator': validate_username_not_url
        },
        TYPE_YAHOO: {
            'name': 'Yahoo! Messenger',
            'url': ''
        },
        TYPE_WEBSITE: {
            'name': 'Website URL',
            'url': '',
            'validator': validate_website
        },
        TYPE_WEBMAKER: {
            'name': 'Mozilla Webmaker',
            'url': 'https://{identifier}.makes.org',
            'validator': validate_username_not_url
        },
        TYPE_MOWIKI: {
            'name': 'Mozilla Wiki',
            'url': 'https://wiki.mozilla.org/User:{identifier}',
            'validator': validate_username_not_url
        },
        TYPE_REMO: {
            'name': 'Mozilla Reps',
            'url': 'https://reps.mozilla.org/u/{identifier}/',
            'validator': validate_username_not_url
        },
        TYPE_LINKEDIN: {
            'name': 'LinkedIn',
            'url': 'https://www.linkedin.com/in/{identifier}/',
            'validator': validate_linkedin
        },
        TYPE_JABBER: {
            'name': 'XMPP/Jabber',
            'url': '',
            'validator': validate_email
        },
        TYPE_MASTODON: {
            'name': 'Mastodon',
            'url': '',
            'validator': validate_email
        },
        TYPE_DISCOURSE: {
            'name': 'Mozilla Discourse',
            'url': 'https://discourse.mozilla.org/users/{identifier}',
            'validator': validate_username_not_url
        },
        TYPE_LANYRD: {
            'name': 'Lanyrd',
            'url': 'http://lanyrd.com/profile/{identifier}/',
            'validator': validate_username_not_url
        },
        TYPE_LANDLINE: {
            'name': 'Phone (Landline)',
            'url': '',
            'validator': validate_phone_number
        },
        TYPE_MOBILE: {
            'name': 'Phone (Mobile)',
            'url': '',
            'validator': validate_phone_number
        },
        TYPE_MOPONTOON: {
            'name': 'Mozilla Pontoon',
            'url': 'https://pontoon.mozilla.org/contributor/{identifier}/',
            'validator': validate_email
        },
        TYPE_TRANSIFEX: {
            'name': 'Transifex',
            'url': 'https://www.transifex.com/accounts/profile/{identifier}/',
            'validator': validate_username_not_url
        },
        TYPE_TELEGRAM: {
            'name': 'Telegram',
            'url': 'https://telegram.me/{identifier}',
            'validator': validate_username_not_url
        },
        TYPE_DISCORD: {
            'name': 'Discord',
            'url': '',
            'validator': validate_discord
        },
        TYPE_MOZPHAB: {
            'name': 'Mozilla Phabricator',
            'url': 'https://phabricator.services.mozilla.com/p/{identifier}/',
            'validator': validate_username_not_url
        },
    }

    user = models.ForeignKey(UserProfile)
    identifier = models.CharField(max_length=255,
                                  verbose_name=_lazy(u'Account Username'))
    type = models.CharField(
        max_length=30,
        choices=sorted(
            [(k, v['name'])
             for (k, v) in ACCOUNT_TYPES.iteritems() if k != TYPE_EMAIL],
            key=lambda x: x[1]),
        verbose_name=_lazy(u'Account Type'))
    privacy = models.PositiveIntegerField(default=MOZILLIANS,
                                          choices=PRIVACY_CHOICES_WITH_PRIVATE)

    class Meta:
        ordering = ['type']
        unique_together = ('identifier', 'type', 'user')

    def get_identifier_url(self):
        url = self.ACCOUNT_TYPES[self.type]['url'].format(
            identifier=urlquote(self.identifier))
        if self.type == 'LINKEDIN' and '://' in self.identifier:
            return self.identifier

        return iri_to_uri(url)

    def unique_error_message(self, model_class, unique_check):
        if model_class == type(self) and unique_check == ('identifier', 'type',
                                                          'user'):
            return _('You already have an account with this name and type.')
        else:
            return super(ExternalAccount,
                         self).unique_error_message(model_class, unique_check)

    def __unicode__(self):
        return self.type
示例#56
0
class Grupo(DiasSemanaMixin, SixALNode, AL_Node):
    """
    Modelo para guardar la información de los grupos de una iglesia.
    """

    ACTIVO = 'A'
    INACTIVO = 'I'

    ESTADOS = (
        (ACTIVO, 'Activo'),
        (INACTIVO, 'Inactivo'),
    )

    parent = models.ForeignKey(
        'self', verbose_name=_lazy('grupo origen'), related_name='children_set', null=True, db_index=True
    )
    direccion = models.CharField(verbose_name=_lazy('dirección'), max_length=50)
    fechaApertura = models.DateField(verbose_name=_lazy('fecha de apertura'))
    diaGAR = models.CharField(verbose_name=_lazy('dia G.A.R'), max_length=1, choices=DiasSemanaMixin.DIAS_SEMANA)
    horaGAR = models.TimeField(verbose_name=_lazy('hora G.A.R'))
    diaDiscipulado = models.CharField(
        verbose_name=_lazy('dia discipulado'), max_length=1, choices=DiasSemanaMixin.DIAS_SEMANA, blank=True, null=True
    )
    horaDiscipulado = models.TimeField(verbose_name=_lazy('hora discipulado'), blank=True, null=True)
    nombre = models.CharField(verbose_name=_lazy('nombre'), max_length=255)
    red = models.ForeignKey(Red, verbose_name=('red'), null=True, blank=True)
    barrio = models.ForeignKey('miembros.Barrio', verbose_name=_lazy('barrio'))

    # campos para ubicaciones en mapas
    latitud = models.FloatField(verbose_name='Latitud', blank=True, null=True)
    longitud = models.FloatField(verbose_name='Longitud', blank=True, null=True)

    # managers
    _objects = GrupoManagerStandard()  # contiene los defaults de django
    objects = GrupoManager()

    node_order_by = ['id']

    class Meta:
        verbose_name = _lazy('grupo')
        verbose_name_plural = _lazy('grupos')

    def __str__(self):
        if self.estado == self.__class__.objects.get_queryset().get_historial_model().ARCHIVADO:
            return self.get_nombre()

        lideres = ["{0} {1}({2})".format(
            lider.nombre.upper(), lider.primer_apellido.upper(), lider.cedula
        ) for lider in self.lideres.all()]

        return " - ".join(lideres)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        if not self.historiales.exists():
            self.actualizar_estado(estado=self.__class__.objects.get_queryset().get_historial_model().ACTIVO)

    @classmethod
    def _obtener_arbol_recursivamente(cls, padre, resultado):
        """
        Construye el organigrama de forma recursiva.
        """

        lista_hijos = []
        for hijo in padre.get_children().prefetch_related('lideres'):
            cls._obtener_arbol_recursivamente(hijo, lista_hijos)

        resultado.append(padre)
        if lista_hijos:
            resultado.append(lista_hijos)

    @classmethod
    def obtener_arbol(cls, padre=None):
        """
        :returns:
            El organigrama en una lista de listas incluyendo el padre, que me indica como va el desarrollo de los
            grupos.

        :param padre:
            El grupo inicial desde donde sa va mostrar el organigrama. Si no se indica se muestra todo el organigrama
            de una iglesia.
        """

        # TODO Mejorar en la doc que retorna con un ejemplo usar como guia el metodo get get_annotated_list de
        # http://django-treebeard.readthedocs.io/en/latest/api.html#treebeard.models.Node.add_child

        arbol = []
        if padre is None:
            padre = cls.objects.raiz()

        if padre is not None:
            cls._obtener_arbol_recursivamente(padre, arbol)

        return arbol

    # Deprecado
    @classmethod
    def obtener_arbol_viejo(cls, raiz=None):  # pragma: no cover
        """
        :returns:
            El arbol en una lista de listas incluyendo el padre, que me indica como va el desarrollo de los
            grupos.
        """

        arbol = []
        if raiz is None:
            raiz = cls.objects.raiz()

        if raiz is not None:
            pila = [[raiz]]
            act = None
            bajada = True

            discipulos = list(raiz.get_children().select_related('parent').prefetch_related('lideres'))
            while len(discipulos) > 0:
                hijo = discipulos.pop()
                if hijo:
                    if act is not None:
                        pila.append(act)
                    sw = True
                    while len(pila) > 0 and sw:
                        act = pila.pop()
                        if act[len(act) - 1] == hijo.parent:
                            act.append([hijo])
                            bajada = True
                            sw = False
                        elif act[len(act) - 2] == hijo.parent:
                            act[len(act) - 1].append(hijo)
                            bajada = True
                            sw = False
                        elif isinstance(act[-1], (tuple, list)) and bajada:
                            pila.append(act)
                            pila.append(act[len(act) - 1])
                        elif not isinstance(act[-1], (tuple, list)):
                            bajada = False
                hijos = hijo.get_children().select_related('parent').prefetch_related('lideres')
                if len(hijos) > 0:
                    discipulos.extend(list(hijos))
            if pila:
                arbol = pila[0]
            else:
                arbol = act

        return arbol

    @classmethod
    def obtener_ruta(cls, inicial, final):
        """
        :returns:
            Una lista con los grupos que conforman la ruta que hay desde el grupo inicial al grupo final
            incluyendo estos grupos.

        :param inicial:
            Grupo en cual inicia la ruta.

        :param final:
            Grupo en el cual termina la ruta.
        """

        ruta = []
        grupo = final

        while grupo != inicial:
            ruta.insert(0, grupo)
            grupo = grupo.get_parent()

        ruta.insert(0, inicial)
        return ruta

    @property
    def discipulos(self):
        """
        :returns:
            Un queryset con los miembros del grupo actual que son lideres.

        .. note::

            Es considerado lider todo miembro que tenga permiso de líder.
        """

        return self.miembros.lideres()

    @property
    def reuniones_GAR_sin_ofrenda_confirmada(self):
        """
        :returns:
            Un queryset con las reuniones GAR del grupo actual que no tienen la ofrenda confirmada.
        """

        return self.reuniones_gar.filter(confirmacionEntregaOfrenda=False)

    @property
    def reuniones_discipulado_sin_ofrenda_confirmada(self):
        """
        :returns:
            Un queryset con las reuniones de discipulado del grupo actual que no tienen la ofrenda confirmada.
        """

        return self.reuniones_discipulado.filter(confirmacionEntregaOfrenda=False)

    @property
    def grupos_red(self):
        """
        :returns:
            Un queryset con la subred del grupo actual.

        .. note::

            Entiéndase por subred todos los descendientes del grupo actual incluyéndose asimismo.
        """

        from .utils import convertir_lista_grupos_a_queryset
        return convertir_lista_grupos_a_queryset(self.get_tree(self))

    @property
    @cache_value(key='cabeza_red', suffix='pk')
    def cabeza_red(self):
        """
        :returns:
            El grupo cabeza de red del grupo actual o ``None`` si el grupo actual se encuentra por encima de los
            cabezas de red.

        .. note::

            Entiéndase por cabeza de red un grupo que se encuentra dentro de los 72 del pastor presidente, es decir,
            un grupo que se encuentra dos niveles mas abajo que la raiz del arbol.
        """

        ancestros = self.get_ancestors()
        if self.get_depth() == 3:
            return self
        elif len(ancestros) > 2:
            return ancestros[2]
        else:
            return None

    @property
    @cache_value(key='numero_celulas', suffix='pk')
    def numero_celulas(self):
        """
        :returns:
            El numero de grupos a cargo que tiene el grupo actual, incluyendose el mismo.
        """

        return self.get_descendant_count() + 1

    @property
    def estado(self):
        """
        Retorna el estado del grupo de acuerdo a su historial.

        :rtype: str
        """

        return getattr(self.historiales.first(), 'estado', 'NONE')

    @property
    def is_activo(self):
        """
        :returns:
            ``True`` si el estado del grupo es activo.

        :rtype: bool
        """
        return self.estado == self.historiales.model.ACTIVO

    def get_estado_display(self):
        """
        :returns:
            Estado display del grupo de acuerdo a su historial.
        """

        return self.historiales.first().get_estado_display()

    def confirmar_ofrenda_reuniones_GAR(self, reuniones):
        """
        Confirma la ofrenda de las reuniones GAR ingresadas del grupo actual.

        :param list[int] reuniones:
            Contiene los ids de las reuniones a confirmar.
        """

        self.reuniones_gar.filter(id__in=reuniones).update(confirmacionEntregaOfrenda=True)

    def confirmar_ofrenda_reuniones_discipulado(self, reuniones):
        """
        Confirma la ofrenda de las reuniones de discipulado ingresadas del grupo actual.

        :param list[int] reuniones:
            Contiene los ids de las reuniones a confirmar.
        """

        self.reuniones_discipulado.filter(id__in=reuniones).update(confirmacionEntregaOfrenda=True)

    def trasladar(self, nuevo_padre):
        """
        Traslada el grupo actual y sus descendientes debajo de un nuevo grupo en el arbol. A los lideres del grupo
        actual, se les modifica el grupo al que pertenecen, al nuevo grupo.

        :param nuevo_padre:
            Grupo que va ser usado como nuevo padre del grupo actual que se esta trasladando.

        :raise InvalidMoveToDescendant:
            Cuando se intenta trasladar el grupo actual debajo de uno de sus descendientes.
        """

        with transaction.atomic():
            if nuevo_padre != self.parent:
                self.move(nuevo_padre, pos='sorted-child')
                self.lideres.all().update(grupo=nuevo_padre)

                if nuevo_padre.red != self.red:
                    grupos = [grupo.id for grupo in self._get_tree(self)]
                    Grupo._objects.filter(id__in=grupos).update(red=nuevo_padre.red)

    def _trasladar_miembros(self, nuevo_grupo):
        """
        Traslada todos los miembros que no lideran grupo del grupo actual al nuevo grupo.

        :param nuevo_grupo:
            Grupo al cual van a pertenecer los miembros que se van a trasladar.
        """

        self.miembros.filter(grupo_lidera=None).update(grupo=nuevo_grupo)

    def _trasladar_visitas(self, nuevo_grupo):
        """
        Traslada todas las visitas del grupo actual al nuevo grupo.

        :param nuevo_grupo:
            Grupo al cual van a pertenecer las visitas que se van a trasladar.
        """

        self.visitas.update(grupo=nuevo_grupo)

    def _trasladar_encontristas(self, nuevo_grupo):
        """
        Traslada todos los encontristas del grupo actual al nuevo grupo.

        :param nuevo_grupo:
            Grupo al cual van a pertenecer los encontristas que se van a trasladar.
        """

        self.encontristas.update(grupo=nuevo_grupo)

    def _trasladar_reuniones_gar(self, nuevo_grupo):
        """
        Traslada todas las reuniones GAR del grupo actual al nuevo grupo.

        :param nuevo_grupo:
            Grupo al cual van a pertenecer las reuniones GAR que se van a trasladar.
        """

        self.reuniones_gar.update(grupo=nuevo_grupo)

    def _trasladar_reuniones_discipulado(self, nuevo_grupo):
        """
        Traslada todas las reuniones de discipulado del grupo actual al nuevo grupo.

        :param nuevo_grupo:
            Grupo al cual van a pertenecer las reuniones de discipulado que se van a trasladar.
        """

        self.reuniones_discipulado.update(grupo=nuevo_grupo)

    def fusionar(self, nuevo_grupo):
        """
        Traslada la información asociada al grupo actual (visitas, encontristas, reuniones, miembros, etc) al
        nuevo grupo y elimina el grupo actual.

        :param nuevo_grupo:
            Grupo al cual va a pertenecer la información que se va a trasladar.
        """

        if self != nuevo_grupo:
            with transaction.atomic():
                for hijo in self.get_children():
                    hijo.trasladar(nuevo_grupo)

                self._trasladar_visitas(nuevo_grupo)
                self._trasladar_miembros(nuevo_grupo)
                self._trasladar_encontristas(nuevo_grupo)
                self._trasladar_reuniones_gar(nuevo_grupo)
                self._trasladar_reuniones_discipulado(nuevo_grupo)

                self.delete()

    def get_nombre(self):
        """
        Retorna el nombre del grupo.

        :rtype: str
        """
        return self.nombre

    def miembrosGrupo(self):
        """Devuelve los miembros de un grupo (queryset) sino tiene, devuelve el queryset vacio."""

        lideres = CambioTipo.objects.filter(nuevoTipo__nombre__iexact='lider').values('miembro')
        miembros = CambioTipo.objects.filter(nuevoTipo__nombre__iexact='miembro').values('miembro')
        return self.miembros.filter(id__in=miembros).exclude(id__in=lideres)

    def get_direccion(self):
        """
        :returns:
            La direccion de manera legible para los buscadores de mapas
        """
        if self.get_position() is None:
            return clean_direccion(self.direccion)
        else:
            return ','.join([str(x) for x in self.get_position()])

    def get_position(self):
        """
        :returns:
            Las coordenadas de un grupo o `None` en caso de no tener last coordenadas.
        """
        if self.latitud is not None and self.longitud is not None:
            return [self.latitud, self.longitud]
        return None

    def actualizar_estado(self, estado, **kwargs):
        """
        Actualiza el estado de el grupo en su historial.

        :param estado:
            El estado que se asignara.
        """

        actual = self.historiales.first()

        if actual is None:
            self.__class__.objects.get_queryset().get_historial_model().objects.create(
                grupo=self, estado=estado, **kwargs)
        elif estado != actual.estado:
            if estado not in list(map(lambda x: x[0], actual.OPCIONES_ESTADO)):
                raise OperationalError(_lazy('Estado "{}" no está permitido'.format(estado)))
            actual.__class__.objects.create(grupo=self, estado=estado, **kwargs)
示例#57
0
class UserForm(UserCreationForm):
    first_name = forms.CharField(
        label=_lazy("First Name"),
        validators=[
            RegexValidator(
                regex="^([A-ZŁŚĆĄŻŹÓĆŃĘ]){1}([a-zążźśęćńół]){2,30}$",
                message=_lazy(
                    "Name should have first letter upper case and the rest lower case"
                ),
            )
        ],
        widget=forms.Textarea(
            attrs={
                "cols": 5,
                "rows": 1,
                "class": "form-control",
                "style": "resize:none;",
            }),
    )
    last_name = forms.CharField(
        label=_lazy("Last Name"),
        validators=[
            RegexValidator(
                regex="^([A-ZŁŚĆĄŻŹÓĆŃĘ]){1}([a-zążźśęćńół]){2,30}$",
                message=_lazy(
                    "Surname should have first letter upper case and the rest lower case"
                ),
            )
        ],
        widget=forms.Textarea(
            attrs={
                "cols": 5,
                "rows": 1,
                "class": "form-control",
                "style": "resize:none;",
            }),
    )

    class Meta:
        model = User
        fields = [
            "username",
            "password1",
            "password2",
            "first_name",
            "last_name",
            "email",
        ]
        widgets = {
            "username":
            forms.Textarea(
                attrs={
                    "cols": 5,
                    "rows": 1,
                    "class": "form-control",
                    "style": "resize:none;",
                }),
            "email":
            forms.Textarea(
                attrs={
                    "cols": 5,
                    "rows": 1,
                    "class": "form-control",
                    "style": "resize:none;",
                }),
        }

    def __init__(self, *args, **kwargs):
        super(UserForm, self).__init__(*args, **kwargs)
        self.fields["username"].help_text = None
        self.fields["password1"].help_text = None
        self.fields["password2"].help_text = None
        self.fields["password1"].widget = forms.PasswordInput(
            attrs={"class": "form-control"})
        self.fields["password2"].widget = forms.PasswordInput(
            attrs={"class": "form-control"})
示例#58
0
    def get_actions(self, request, addon):
        actions = SortedDict()
        if request is None:
            # If request is not set, it means we are just (ab)using the
            # ReviewHelper for its `handler` attribute and we don't care about
            # the actions.
            return actions
        latest_version = addon.find_latest_version(
            channel=amo.RELEASE_CHANNEL_LISTED)
        reviewable_because_complete = addon.status not in (
            amo.STATUS_NULL, amo.STATUS_DELETED)
        reviewable_because_admin = (
            not addon.admin_review or
            acl.action_allowed(request, 'ReviewerAdminTools', 'View'))
        reviewable_because_submission_time = (
            not is_limited_reviewer(request) or
            (latest_version is not None and
                latest_version.nomination is not None and
                (datetime.datetime.now() - latest_version.nomination >=
                    datetime.timedelta(hours=REVIEW_LIMITED_DELAY_HOURS))))
        reviewable_because_pending = (
            latest_version is not None and
            len(latest_version.is_unreviewed) > 0)
        if (reviewable_because_complete and
                reviewable_because_admin and
                reviewable_because_submission_time and
                reviewable_because_pending):
            actions['public'] = {
                'method': self.handler.process_public,
                'minimal': False,
                'details': _lazy('This will approve, sign, and publish this '
                                 'version. The comments will be sent to the '
                                 'developer.'),
                'label': _lazy('Approve')}
            actions['reject'] = {
                'method': self.handler.process_sandbox,
                'label': _lazy('Reject'),
                'details': _lazy('This will reject this version and remove it '
                                 'from the queue. The comments will be sent '
                                 'to the developer.'),
                'minimal': False}
        actions['info'] = {
            'method': self.handler.request_information,
            'label': _lazy('Request more information'),
            'details': _lazy('This will request more information from the '
                             'developer. You will be notified when they '
                             'reply.'),
            'minimal': True}
        actions['super'] = {
            'method': self.handler.process_super_review,
            'label': _lazy('Request super-review'),
            'details': _lazy('If you have concerns about this add-on that an '
                             'admin reviewer should look into, enter your '
                             'comments in the area below. They will not be '
                             'sent to the developer.'),
            'minimal': True}
        actions['comment'] = {
            'method': self.handler.process_comment,
            'label': _lazy('Comment'),
            'details': _lazy('Make a comment on this version. The developer '
                             'won\'t be able to see this.'),
            'minimal': True}

        return actions
示例#59
0
class LicenseForm(AMOModelForm):
    builtin = forms.TypedChoiceField(
        choices=[], coerce=int,
        widget=LicenseRadioSelect(attrs={'class': 'license'}))
    name = forms.CharField(widget=TranslationTextInput(),
                           label=_lazy(u"What is your license's name?"),
                           required=False, initial=_lazy('Custom License'))
    text = forms.CharField(widget=TranslationTextarea(), required=False,
                           label=_lazy(u'Provide the text of your license.'))

    def __init__(self, *args, **kw):
        addon = kw.pop('addon', None)
        self.version = None
        if addon:
            self.version = addon.latest_version
            if self.version:
                kw['instance'], kw['initial'] = self.version.license, None
                # Clear out initial data if it's a builtin license.
                if getattr(kw['instance'], 'builtin', None):
                    kw['initial'] = {'builtin': kw['instance'].builtin}
                    kw['instance'] = None

        super(LicenseForm, self).__init__(*args, **kw)

        cs = [(x.builtin, x)
              for x in License.objects.builtins().filter(on_form=True)]
        cs.append((License.OTHER, _('Other')))
        self.fields['builtin'].choices = cs
        if addon and not addon.is_listed:
            self.fields['builtin'].required = False

    class Meta:
        model = License
        fields = ('builtin', 'name', 'text')

    def clean_name(self):
        name = self.cleaned_data['name']
        return name.strip() or _('Custom License')

    def clean(self):
        data = self.cleaned_data
        if self.errors:
            return data
        elif data['builtin'] == License.OTHER and not data['text']:
            raise forms.ValidationError(
                _('License text is required when choosing Other.'))
        return data

    def get_context(self):
        """Returns a view context dict having keys license_urls, license_form,
        and license_other_val.
        """
        license_urls = dict(License.objects.builtins()
                            .values_list('builtin', 'url'))
        return dict(license_urls=license_urls, version=self.version,
                    license_form=self.version and self,
                    license_other_val=License.OTHER)

    def save(self, *args, **kw):
        """Save all form data.

        This will only create a new license if it's not one of the builtin
        ones.

        Keyword arguments

        **log=True**
            Set to False if you do not want to log this action for display
            on the developer dashboard.
        """
        log = kw.pop('log', True)
        changed = self.changed_data

        builtin = self.cleaned_data['builtin']
        if builtin == '':  # No license chosen, it must be an unlisted add-on.
            return
        if builtin != License.OTHER:
            license = License.objects.get(builtin=builtin)
        else:
            # Save the custom license:
            license = super(LicenseForm, self).save(*args, **kw)

        if self.version:
            if changed or license != self.version.license:
                self.version.update(license=license)
                if log:
                    amo.log(amo.LOG.CHANGE_LICENSE, license,
                            self.version.addon)
        return license
示例#60
0
class UserEditForm(UserRegisterForm, PasswordMixin):
    oldpassword = forms.CharField(
        max_length=255,
        required=False,
        widget=forms.PasswordInput(render_value=False))
    password = forms.CharField(max_length=255,
                               required=False,
                               min_length=PasswordMixin.min_length,
                               error_messages=PasswordMixin.error_msg,
                               widget=PasswordMixin.widget(render_value=False))
    password2 = forms.CharField(max_length=255,
                                required=False,
                                widget=forms.PasswordInput(render_value=False))
    photo = forms.FileField(label=_lazy(u'Profile Photo'), required=False)

    notifications = forms.MultipleChoiceField(
        choices=[],
        widget=NotificationsSelectMultiple,
        initial=email.NOTIFICATIONS_DEFAULT,
        required=False)

    lang = forms.TypedChoiceField(label=_lazy(u'Default locale'),
                                  choices=LOCALES)

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(UserEditForm, self).__init__(*args, **kwargs)

        if not self.instance.lang and self.request:
            self.initial['lang'] = self.request.LANG

        if self.instance:
            default = dict((i, n.default_checked)
                           for i, n in email.NOTIFICATIONS_BY_ID.items())
            user = dict((n.notification_id, n.enabled)
                        for n in self.instance.notifications.all())
            default.update(user)

            # Add choices to Notification.
            choices = email.NOTIFICATIONS_CHOICES
            if not self.instance.is_developer:
                choices = email.NOTIFICATIONS_CHOICES_NOT_DEV

            if self.instance.fxa_migrated():
                self.fields['email'].required = False
                self.fields['email'].widget = forms.EmailInput(
                    attrs={'readonly': 'readonly'})
                self.fields['email'].help_text = fxa_error_message(
                    _(u'Firefox Accounts users cannot currently change their '
                      u'email address.'))

            # Append a "NEW" message to new notification options.
            saved = self.instance.notifications.values_list('notification_id',
                                                            flat=True)
            self.choices_status = {}
            for idx, label in choices:
                self.choices_status[idx] = idx not in saved

            self.fields['notifications'].choices = choices
            self.fields['notifications'].initial = [
                i for i, v in default.items() if v
            ]
            self.fields['notifications'].widget.form_instance = self

        # TODO: We should inherit from a base form not UserRegisterForm
        if self.fields.get('recaptcha'):
            del self.fields['recaptcha']

    class Meta:
        model = UserProfile
        exclude = ('password', 'picture_type', 'last_login', 'fxa_id',
                   'read_dev_agreement')

    def clean(self):
        data = self.cleaned_data
        amouser = self.request.user

        # Passwords
        p1 = data.get("password")
        p2 = data.get("password2")

        if p1 or p2:
            if not amouser.check_password(data["oldpassword"]):
                msg = _("Wrong password entered!")
                self._errors["oldpassword"] = ErrorList([msg])
                del data["oldpassword"]

        super(UserEditForm, self).clean()
        return data

    def clean_email(self):
        email = self.cleaned_data.get('email')
        if self.instance.fxa_migrated():
            if not email or email == self.instance.email:
                return self.instance.email
            else:
                raise forms.ValidationError(_(u'Email cannot be changed.'))
        else:
            return email

    def clean_photo(self):
        photo = self.cleaned_data['photo']

        if not photo:
            return

        if photo.content_type not in ('image/png', 'image/jpeg'):
            raise forms.ValidationError(_('Images must be either PNG or JPG.'))

        if photo.size > settings.MAX_PHOTO_UPLOAD_SIZE:
            raise forms.ValidationError(
                _('Please use images smaller than %dMB.' %
                  (settings.MAX_PHOTO_UPLOAD_SIZE / 1024 / 1024 - 1)))

        return photo

    def clean_bio(self):
        bio = self.cleaned_data['bio']
        normalized = clean_nl(unicode(bio))
        if has_links(normalized):
            # There's some links, we don't want them.
            raise forms.ValidationError(_('No links are allowed.'))
        return bio

    def save(self, log_for_developer=True):
        u = super(UserEditForm, self).save(commit=False)
        data = self.cleaned_data
        photo = data['photo']
        if photo:
            u.picture_type = 'image/png'
            tmp_destination = u.picture_path + '__unconverted'

            with storage.open(tmp_destination, 'wb') as fh:
                for chunk in photo.chunks():
                    fh.write(chunk)

            tasks.resize_photo.delay(tmp_destination,
                                     u.picture_path,
                                     set_modified_on=[u])

        if data['password']:
            u.set_password(data['password'])
            log_cef('Password Changed',
                    5,
                    self.request,
                    username=u.username,
                    signature='PASSWORDCHANGED',
                    msg='User changed password')
            if log_for_developer:
                amo.log(amo.LOG.CHANGE_PASSWORD)
                log.info(u'User (%s) changed their password' % u)

        for (i, n) in email.NOTIFICATIONS_BY_ID.items():
            enabled = n.mandatory or (str(i) in data['notifications'])
            UserNotification.update_or_create(user=u,
                                              notification_id=i,
                                              update={'enabled': enabled})

        log.debug(u'User (%s) updated their profile' % u)

        u.save()
        return u