Ejemplo n.º 1
0
 def get_actions(self):
     actions = SortedDict()
     actions['public'] = {'method': self.handler.process_public,
                          'minimal': False,
                          'label': _lazy('Push to public'),
                          'details': _lazy(
                             'This will approve the sandboxed app so it '
                             'appears on the public side.')}
     actions['reject'] = {'method': self.handler.process_sandbox,
                          'label': _lazy('Reject'),
                          'minimal': False,
                          'details': _lazy(
                             'This will reject the app and remove it '
                             'from the review queue.')}
     actions['info'] = {'method': self.handler.request_information,
                        'label': _lazy('Request more information'),
                        'minimal': True,
                        'details': _lazy(
                            'This will send the author(s) an email '
                            'requesting more information.')}
     actions['super'] = {'method': self.handler.process_super_review,
                         'label': _lazy('Request super-review'),
                         'minimal': True,
                         'details': _lazy(
                             'Flag this app for an admin to review')}
     actions['comment'] = {'method': self.handler.process_comment,
                           'label': _lazy('Comment'),
                           'minimal': True,
                           'details': _lazy(
                                 'Make a comment on this app.  The '
                                 'author won\'t be able to see this.')}
     return actions
Ejemplo n.º 2
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
Ejemplo n.º 3
0
    def get_actions(self, request, addon):
        labels, details = self._review_actions()

        actions = SortedDict()
        if not addon.admin_review or acl.action_allowed(request, "ReviewerAdminTools", "View"):
            if self.review_type != "preliminary":
                actions["public"] = {
                    "method": self.handler.process_public,
                    "minimal": False,
                    "label": _lazy("Push to public"),
                }
            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
Ejemplo n.º 4
0
def index(request):
    """
    Display search results for Opinions on Firefox. Shows breakdown of
    Praise/Issues/Ideas, sites/themes, and search filters.

    If no search criteria are explicitly set, the page is considered the
    "Dashboard" (i.e. the home page of Firefox Input). Otherwise, the title
    of the page is "Search Results".
    """

    VERSIONS = VersionCount.objects.filter(active=1)

    VERSION_CHOICES = {
        FIREFOX: ([('--', _lazy(u'-- all --', 'version_choice'))] +
                  [(v.version, v.version) for v in VERSIONS
                  if v.product == FIREFOX.id]),
        MOBILE: ([('--', _lazy(u'-- all --', 'version_choice'))] +
                 [(v.version, v.version) for v in VERSIONS
                 if v.product == MOBILE.id]),
    }

    try:
        meta = ('type', 'locale', 'platform', 'day_sentiment', 'manufacturer',
                'device')
        (results, form, product, version, metas, type_filter) = _get_results(
                request, meta=meta)
    except SearchError, e:
        return render(request, 'search/unavailable.html', {'search_error': e},
                      status=500)
Ejemplo n.º 5
0
    def __init__(self, *args, **kwargs):
        """Override the __init__ method to change form labels"""
        super(PasswordChangeForm, self).__init__(*args, **kwargs)

        self.fields['old_password'].label = _lazy(u'Verify your old password')
        self.fields['new_password1'].label = _lazy(u'Enter a new password')
        self.fields['new_password2'].label = _lazy(u'Confirm new password')
Ejemplo n.º 6
0
    def get_actions(self):
        labels, details = self._review_actions()

        actions = SortedDict()
        if self.review_type != 'preliminary':
            actions['public'] = {'method': self.handler.process_public,
                                 'minimal': False,
                                 'label': _lazy('Push to public')}

        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
Ejemplo n.º 7
0
 def __init__(self, *args, **kw):
     super(PaypalSetupForm, self).__init__(*args, **kw)
     self.fields["business_account"].choices = (
         ("yes", _lazy("Yes")),
         ("no", _lazy("No")),
         ("later", _lazy(u"I'll link my PayPal account later.")),
     )
Ejemplo n.º 8
0
    def get_actions(self):
        labels, details = self._review_actions()

        actions = SortedDict()
        if self.review_type != "preliminary":
            actions["public"] = {
                "method": self.handler.process_public,
                "minimal": False,
                "label": _lazy("Push to public"),
            }

        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,
        }
        for k, v in actions.items():
            v["details"] = details.get(k)

        return actions
Ejemplo n.º 9
0
    def __init__(self, *args, **kwargs):
        """Override the __init__ method to change form labels"""
        super(PasswordChangeForm, self).__init__(*args, **kwargs)

        self.fields["old_password"].label = _lazy(u"Verify your old password")
        self.fields["new_password1"].label = _lazy(u"Enter a new password")
        self.fields["new_password2"].label = _lazy(u"Confirm new password")
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
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])
Ejemplo n.º 12
0
def promo_video_dance(request):
    """Dancing promo video."""
    d = dict(video_title=_lazy('Dance'),
             video_description=_lazy("He's got the moves, he's got ambition. "
                                     "How far can this fox's feet take him? "
                                     "Get inspired for your Firefox Flicks "
                                     "entry by checking out our video."),
             page_type='videos',
             video_embed=Markup(embedCode(promo_video_shortlink('dance'),
                                          width=600, height=337)))
    return render(request, 'videos/promo.html', d)
Ejemplo n.º 13
0
 def __init__(self, *args, **kwargs):
     super(UsernameField, self).__init__(
         label=_lazy(u'Username'), max_length=30, min_length=3,
         regex=r'^[\w.@+-]+$',
         help_text=_lazy(u'Required. 30 characters or fewer. '
                         'Letters, digits and @/./+/-/_ only.'),
         error_messages={'invalid': USERNAME_INVALID,
                         'required': USERNAME_REQUIRED,
                         'min_length': USERNAME_SHORT,
                         'max_length': USERNAME_LONG},
         *args, **kwargs)
Ejemplo n.º 14
0
def promo_video_twilight(request):
    """Twilight parody promo video."""
    desc = _lazy('A teenage girl learns the truth about the fox. Get inspired '
                 'for your Firefox Flicks entry by checking out our video.')
    d = dict(video_title=_lazy('Twilight'),
             video_description=desc,
             tweet_text=desc,
             page_type='videos',
             video_embed=Markup(embedCode(promo_video_shortlink('twilight'),
                                          width=600, height=337)))
    return render(request, 'videos/promo.html', d)
Ejemplo n.º 15
0
def promo_video_noir(request):
    """Film Noir promo video."""
    d = dict(video_title=_lazy('Noir'),
             video_description=_lazy('The fox meets a damsel in distress, but '
                                     'can he help her? Get inspired for your '
                                     'Firefox Flicks entry by checking out '
                                     'our video.'),
             page_type='videos',
             video_embed=Markup(embedCode(promo_video_shortlink('noir'),
                                          width=600, height=337)))
    return render(request, 'videos/promo.html', d)
Ejemplo n.º 16
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')),
        )

        return [(cls, title) for (prop, cls, title) in props
                if getattr(self, prop)]
Ejemplo n.º 17
0
Archivo: forms.py Proyecto: gerv/kuma
 def __init__(self, *args, **kwargs):
     super(UsernameField, self).__init__(
         label=_lazy(u'Username'), max_length=30, min_length=3,
         regex=r'^[\w.-]+$',
         help_text=_lazy(u'Required. 30 characters or fewer. '
                         'Letters, digits and ./-/_ only.'),
         widget=forms.TextInput(attrs={'placeholder': USERNAME_PLACEHOLDER}),
         error_messages={'invalid': USERNAME_INVALID,
                         'required': USERNAME_REQUIRED,
                         'min_length': USERNAME_SHORT,
                         'max_length': USERNAME_LONG},
         *args, **kwargs)
Ejemplo n.º 18
0
 def render_additional_info(self, row):
     info = []
     if row.is_site_specific:
         info.append(_lazy(u"Site Specific"))
     if len(row.file_platform_ids) == 1 and row.file_platform_ids != [amo.PLATFORM_ALL.id]:
         k = row.file_platform_ids[0]
         # L10n: first argument is the platform such as Linux, Mac OS X
         info.append(_lazy(u"{0} only").format(amo.PLATFORMS[k].name))
     if row.external_software:
         info.append(_lazy(u"Requires External Software"))
     if row.binary:
         info.append(_lazy(u"Binary Components"))
     return u", ".join([jinja2.escape(i) for i in info])
Ejemplo n.º 19
0
 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': '/',
     }
Ejemplo n.º 20
0
def validate_twitter(username):
    """Return a twitter username given '@' or http(s) strings."""

    if username:
        username = re.sub('https?://(www\.)?twitter\.com/|@', '', username)

        # Twitter accounts must be alphanumeric ASCII including underscore, and <= 15 chars.
        # https://support.twitter.com/articles/101299-why-can-t-i-register-certain-usernames
        if len(username) > 15:
            raise ValidationError(_lazy('Twitter usernames cannot be longer than 15 characters.'))

        if not re.match('^\w+$', username):
            raise ValidationError(_lazy('Twitter usernames must contain only alphanumeric characters'
                                        ' and the underscore.'))
    return username
Ejemplo n.º 21
0
def search(request):
    to_json = JSONRenderer().render
    context = {}
    form = WikiSearchForm()

    # Get options to fill the various select boxes of the search forms.
    languages = _options_tuple_to_dict(form.fields['language'].choices)
    categories = _options_tuple_to_dict(form.fields['category'].choices)
    products = _options_tuple_to_dict(form.fields['product'].choices)
    topics = _options_tuple_to_dict(form.fields['topics'].choices)

    filters = {
        'language': {
            'meta': {
                'name': 'language',
                'label': _lazy(u'Language'),
                'multi': False,
            },
            'options': languages,
        },
        'category': {
            'meta': {
                'name': 'category',
                'label': _lazy(u'Category'),
                'multi': True,
            },
            'options': categories,
        },
        'product': {
            'meta': {
                'name': 'product',
                'label': _lazy(u'Relevant to'),
                'multi': True,
            },
            'options': products,
        },
        'topics': {
            'meta': {
                'name': 'topics',
                'label': _lazy(u'Topics'),
                'multi': True,
            },
            'options': topics,
        },
    }
    context['filters_json'] = to_json(filters)

    return render(request, 'coolsearch/search.html', context)
Ejemplo n.º 22
0
def locale_name(locale, native=False, default=_lazy('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
Ejemplo n.º 23
0
    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)
Ejemplo n.º 24
0
def datetimeformat(context, value, format='shortdatetime'):
    """
    Returns date/time formatted using babel's locale settings. Uses the
    timezone from settings.py
    """
    if not isinstance(value, datetime.datetime):
        # Expecting date value
        raise ValueError

    tzinfo = timezone(settings.TIME_ZONE)
    tzvalue = tzinfo.localize(value)
    locale = _babel_locale(_contextual_locale(context))

    # If within a day, 24 * 60 * 60 = 86400s
    if format == 'shortdatetime':
        # Check if the date is today
        if value.toordinal() == datetime.date.today().toordinal():
            formatted = _lazy(u'Today at %s') % format_time(
                                    tzvalue, format='short', locale=locale)
        else:
            formatted = format_datetime(tzvalue, format='short', locale=locale)
    elif format == 'longdatetime':
        formatted = format_datetime(tzvalue, format='long', locale=locale)
    elif format == 'date':
        formatted = format_date(tzvalue, locale=locale)
    elif format == 'time':
        formatted = format_time(tzvalue, locale=locale)
    elif format == 'datetime':
        formatted = format_datetime(tzvalue, locale=locale)
    else:
        # Unknown format
        raise DateTimeFormatError

    return jinja2.Markup('<time datetime="%s">%s</time>' % \
                         (tzvalue.isoformat(), formatted))
Ejemplo n.º 25
0
def reviewers_page_title(context, title=None, addon=None):
    if addon:
        title = u'%s | %s' % (title, addon.name)
    else:
        section = _lazy('Reviewer Tools')
        title = u'%s | %s' % (title, section) if title else section
    return mkt_page_title(context, title)
Ejemplo n.º 26
0
 def __init__(self, *args, **kwargs):
     super(ReviewAppLogForm, self).__init__(*args, **kwargs)
     self.fields["search"].widget.attrs = {
         # L10n: Descript of what can be searched for.
         "placeholder": _lazy(u"app, reviewer, or comment"),
         "size": 30,
     }
Ejemplo n.º 27
0
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
Ejemplo n.º 28
0
def locale_name(locale, native=False, default=_lazy('Unknown')):
    """Convert a locale code into a human readable locale name."""
    if locale in product_details.languages:
        return product_details.languages[locale][
            native and 'native' or 'English']
    else:
        return default
Ejemplo n.º 29
0
def datetimeformat(context, value, format='shortdatetime'):
    """
    Returns a formatted date/time using Babel's locale settings. Uses the
    timezone from settings.py, if the user has not been authenticated.
    """
    if not isinstance(value, datetime.datetime):
        # Expecting date value
        raise ValueError

    request = context.get('request')

    default_tzinfo = convert_tzinfo = timezone(settings.TIME_ZONE)
    if value.tzinfo is None:
        value = default_tzinfo.localize(value)
        new_value = value.astimezone(default_tzinfo)
    else:
        new_value = value

    if 'timezone' not in request.session:
        if request.user.is_authenticated():
            try:
                convert_tzinfo = request.user.get_profile().timezone or \
                                 default_tzinfo
            except (Profile.DoesNotExist, AttributeError):
                pass
        request.session['timezone'] = convert_tzinfo
    else:
        convert_tzinfo = request.session['timezone']

    convert_value = new_value.astimezone(convert_tzinfo)
    locale = _babel_locale(_contextual_locale(context))

    # If within a day, 24 * 60 * 60 = 86400s
    if format == 'shortdatetime':
        # Check if the date is today
        today = datetime.datetime.now(tz=convert_tzinfo).toordinal()
        if convert_value.toordinal() == today:
            formatted = _lazy(u'Today at %s') % format_time(
                convert_value, format='short', tzinfo=convert_tzinfo,
                locale=locale)
        else:
            formatted = format_datetime(convert_value, format='short',
                tzinfo=convert_tzinfo, locale=locale)
    elif format == 'longdatetime':
        formatted = format_datetime(convert_value, format='long',
            tzinfo=convert_tzinfo, locale=locale)
    elif format == 'date':
        formatted = format_date(convert_value, locale=locale)
    elif format == 'time':
        formatted = format_time(convert_value, tzinfo=convert_tzinfo,
            locale=locale)
    elif format == 'datetime':
        formatted = format_datetime(convert_value, tzinfo=convert_tzinfo,
            locale=locale)
    else:
        # Unknown format
        raise DateTimeFormatError

    return jinja2.Markup('<time datetime="%s">%s</time>' % \
                         (convert_value.isoformat(), formatted))
Ejemplo n.º 30
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 placeholder
        parsed_url = urlparse(title)
        netloc = parsed_url.netloc
        if netloc in ['youtu.be', 'youtube.com', 'www.youtube.com']:
            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)
Ejemplo n.º 31
0
class ExternalAccount(models.Model):
    # Constants for type field values.
    TYPE_AMO = 'AMO'
    TYPE_BMO = 'BMO'
    TYPE_EMAIL = 'EMAIL'
    TYPE_GITHUB = 'GITHUB'
    TYPE_MDN = 'MDN'
    TYPE_SUMO = 'SUMO'
    TYPE_FACEBOOK = 'FACEBOOK'
    TYPE_TWITTER = 'TWITTER'
    TYPE_AIM = 'AIM'
    TYPE_GTALK = 'GTALK'
    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_MOVERBATIM = 'MOZILLAVERBATIM'
    TYPE_MOLOCOMOTION = 'MOZILLALOCOMOTION'
    TYPE_MOLOCATION = 'MOZILLALOCATION'
    TYPE_TRANSIFEX = 'TRANSIFEX'

    # 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_GITHUB: {
            'name': 'GitHub',
            'url': 'https://github.com/{identifier}',
            'validator': validate_username_not_url
        },
        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_MOLOCATION: {
            'name': 'Mozilla Location Service',
            'url':
            'https://location.services.mozilla.com/leaders#{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_GTALK: {
            'name': 'Google+ Hangouts',
            'url': '',
            'validator': validate_email
        },
        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': '',
            'validator': validate_website
        },
        TYPE_JABBER: {
            'name': 'XMPP/Jabber',
            'url': '',
            'validator': validate_email
        },
        TYPE_DISCOURSE: {
            'name': 'Mozilla Discourse',
            'url':
            'https://discourse.mozilla-community.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_MOVERBATIM: {
            'name': 'Mozilla Verbatim',
            'url': 'https://localize.mozilla.org/accounts/{identifier}/',
            'validator': validate_username_not_url
        },
        TYPE_MOLOCOMOTION: {
            'name': 'Mozilla Locomotion',
            'url': 'http://mozilla.locamotion.org/accounts/{identifier}/',
            'validator': validate_username_not_url
        },
        TYPE_TRANSIFEX: {
            'name': 'Transifex',
            'url': 'https://www.transifex.com/accounts/profile/{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)

    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))
        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)
Ejemplo n.º 32
0
class LicenseForm(AMOModelForm):
    builtin = forms.TypedChoiceField(choices=[],
                                     coerce=int,
                                     widget=forms.RadioSelect(
                                         attrs={'class': 'license'},
                                         renderer=LicenseChoiceRadio))
    name = forms.CharField(widget=TranslationTextInput(),
                           label=_lazy(u"What is your license's name?"),
                           required=False,
                           initial=_('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:
            qs = addon.versions.order_by('-version')[:1]
            self.version = qs[0] if qs else None
            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

    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 != 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
Ejemplo n.º 33
0
 class Meta:
     verbose_name = _lazy(u'registration profile')
     verbose_name_plural = _lazy(u'registration profiles')
Ejemplo n.º 34
0
class DocumentForm(forms.ModelForm):
    """Form to create/edit a document."""

    title = StrippedCharField(
        min_length=1,
        max_length=255,
        widget=forms.TextInput(attrs={'placeholder': TITLE_PLACEHOLDER}),
        label=_lazy(u'Title:'),
        help_text=_lazy(u'Title of article'),
        error_messages={
            'required': TITLE_REQUIRED,
            'min_length': TITLE_SHORT,
            'max_length': TITLE_LONG
        })

    slug = StrippedCharField(min_length=1,
                             max_length=255,
                             widget=forms.TextInput(),
                             label=_lazy(u'Slug:'),
                             help_text=_lazy(u'Article URL'),
                             error_messages={
                                 'required': SLUG_REQUIRED,
                                 'min_length': SLUG_SHORT,
                                 'max_length': SLUG_LONG
                             })

    category = forms.ChoiceField(
        choices=Document.CATEGORIES,
        initial=10,
        # Required for non-translations, which is
        # enforced in Document.clean().
        required=False,
        label=_lazy(u'Category:'),
        help_text=_lazy(u'Type of article'),
        widget=forms.HiddenInput())

    parent_topic = forms.ModelChoiceField(queryset=Document.objects.all(),
                                          required=False,
                                          label=_lazy(u'Parent:'))

    locale = forms.CharField(widget=forms.HiddenInput())

    def clean_slug(self):
        slug = self.cleaned_data['slug']
        if slug == '':
            # Default to the title, if missing.
            slug = self.cleaned_data['title']
        # "?", " ", quote disallowed in slugs altogether
        if '?' in slug or ' ' in slug or '"' in slug or "'" in slug:
            raise forms.ValidationError(SLUG_INVALID)
        # Pattern copied from urls.py
        if not re.compile(r'^[^\$]+$').match(slug):
            raise forms.ValidationError(SLUG_INVALID)
        # Guard against slugs that match urlpatterns
        for pat in RESERVED_SLUGS:
            if re.compile(pat).match(slug):
                raise forms.ValidationError(SLUG_INVALID)
        return slug

    class Meta:
        model = Document
        fields = ('title', 'slug', 'category', 'locale')

    def save(self, parent_doc, **kwargs):
        """Persist the Document form, and return the saved Document."""
        doc = super(DocumentForm, self).save(commit=False, **kwargs)
        doc.parent = parent_doc
        if 'parent_topic' in self.cleaned_data:
            doc.parent_topic = self.cleaned_data['parent_topic']
        doc.save()
        # not strictly necessary since we didn't change
        # any m2m data since we instantiated the doc
        self.save_m2m()
        return doc
Ejemplo n.º 35
0
from django.conf import settings

from tower import ugettext_lazy as _lazy


# WARNING: When adding a new app feature here also include a migration.
#
# WARNING: Order matters here. Don't re-order these or alphabetize them. If you
# add new ones put them on the end.
#
# These are used to dynamically generate the field list for the AppFeatures
# django model in mkt.webapps.models.
APP_FEATURES = OrderedDict([
    ('APPS', {
        'name': _lazy(u'App Management API'),
        'description': _lazy(u'The app requires the `navigator.mozApps` API '
                             u'to install and manage other apps.'),
        'apis': ('navigator.mozApps',),
    }),
    ('PACKAGED_APPS', {
        'name': _lazy(u'Packaged Apps Install API'),
        'description': _lazy(
            u'The app requires the `navigator.mozApps.installPackage` API '
            u'to install other packaged apps.'),
        'apis': ('navigator.mozApps.installPackage',),
    }),
    ('PAY', {
        'name': _lazy(u'Web Payment'),
        'description': _lazy(u'The app requires the `navigator.mozApps` API.'),
        'apis': ('navigator.pay', 'navigator.mozPay',),
Ejemplo n.º 36
0
    @classmethod
    def unstrip_fors(cls, html, dehydrations):
        """Replace the tokens with <for> tags the ForParser understands."""
        def hydrate(match):
            return dehydrations.get(int(match.group(1) or match.group(2)), '')

        # Put <for ...> tags back in:
        html = cls._PARSED_STRIPPED_FOR.sub(hydrate, html)

        # Replace {/for} tags:
        return cls._PARSED_STRIPPED_FOR_CLOSER.sub(u'</for>', html)


# L10n: This error is displayed if a template is included into itself.
RECURSION_MESSAGE = _lazy(u'[Recursive inclusion of "%s"]')


class WikiParser(sumo_parser.WikiParser):
    """An extension of the parser from the forums adding more crazy features

    {for} tags, inclusions, and templates--oh my!

    """

    image_template = 'wikiparser/hook_image_lazy.html'

    def __init__(self, base_url=None, doc_id=None):
        """
        doc_id -- If you want to be nice, pass the ID of the Document you are
            rendering. This will make recursive inclusions fail immediately
Ejemplo n.º 37
0
class AddonFilter(BaseFilter):
    opts = (('name', _lazy(u'Name')), ('updated', _lazy(u'Updated')),
            ('created', _lazy(u'Created')), ('popular', _lazy(u'Downloads')),
            ('rating', _lazy(u'Rating')))
Ejemplo n.º 38
0
class AppFilter(BaseFilter):
    opts = (('name', _lazy(u'Name')), ('created', _lazy(u'Created')))
Ejemplo n.º 39
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, widget=RequiredTextInput)
    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 = captcha.fields.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):
        super(UserRegisterForm, self).__init__(*args, **kwargs)

        if not settings.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
Ejemplo n.º 40
0
# From page 10 of the Mozilla Exporter API docs v1.0.0
#
# BDT not in docs, but added in for bug 1043481.
BANGO_CURRENCIES = [
    'AUD', 'BDT', 'CAD', 'CHF', 'COP', 'DKK', 'EGP', 'EUR', 'GBP', 'IDR',
    'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'QAR', 'SEK', 'SGD', 'THB',
    'USD', 'ZAR'
]
BANGO_CURRENCIES = dict((k, ALL_CURRENCIES[k]) for k in BANGO_CURRENCIES)

BANGO_OUTPAYMENT_CURRENCIES = ['EUR', 'GBP', 'USD']
BANGO_OUTPAYMENT_CURRENCIES = [(k, ALL_CURRENCIES[k])
                               for k in BANGO_OUTPAYMENT_CURRENCIES]

BANGO_COUNTRIES = [
    ('AFG', _lazy(u'Afghanistan')),
    ('ALA', _lazy(u'Åland Islands')),
    ('ALB', _lazy(u'Albania')),
    ('DZA', _lazy(u'Algeria')),
    ('ASM', _lazy(u'American Samoa')),
    ('AND', _lazy(u'Andorra')),
    ('AGO', _lazy(u'Angola')),
    ('AIA', _lazy(u'Anguilla')),
    ('ATA', _lazy(u'Antarctica')),
    ('ATG', _lazy(u'Antigua and Barbuda')),
    ('ARG', _lazy(u'Argentina')),
    ('ARM', _lazy(u'Armenia')),
    ('ABW', _lazy(u'Aruba')),
    ('AUS', _lazy(u'Australia')),
    ('AUT', _lazy(u'Austria')),
    ('AZE', _lazy(u'Azerbaijan')),
Ejemplo n.º 41
0
def yesno(boolean_value):
    return jinja2.Markup(_lazy(u'Yes') if boolean_value else _lazy(u'No'))
Ejemplo n.º 42
0
class RegionForm(forms.Form):
    regions = forms.MultipleChoiceField(
        required=False,
        label=_lazy(u'Choose the regions your app will be listed in:'),
        choices=mkt.regions.REGIONS_CHOICES_NAME,
        widget=forms.CheckboxSelectMultiple,
        error_messages={
            'required': _lazy(u'You must select at least one region.')
        })
    special_regions = forms.MultipleChoiceField(
        required=False,
        choices=[(x.id, x.name) for x in mkt.regions.SPECIAL_REGIONS],
        widget=forms.CheckboxSelectMultiple)
    enable_new_regions = forms.BooleanField(required=False,
                                            label=_lazy(u'Enable new regions'))
    restricted = forms.TypedChoiceField(
        required=False,
        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'}),
        initial=0,
        coerce=int)

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

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

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

        # The checkboxes for special regions are
        #
        # - checked ... if an app has not been requested for approval in
        #   China or the app has been rejected in China.
        #
        # - unchecked ... if an app has been requested for approval in
        #   China or the app has been approved in China.
        unchecked_statuses = (amo.STATUS_NULL, amo.STATUS_REJECTED)

        for region in self.special_region_objs:
            if self.product.geodata.get_status(region) in unchecked_statuses:
                # If it's rejected in this region, uncheck its checkbox.
                if region.id in self.initial['regions']:
                    self.initial['regions'].remove(region.id)
            elif region.id not in self.initial['regions']:
                # If it's pending/public, check its checkbox.
                self.initial['regions'].append(region.id)

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

    @property
    def special_region_objs(self):
        return mkt.regions.SPECIAL_REGIONS

    @property
    def special_region_ids(self):
        return mkt.regions.SPECIAL_REGION_IDS

    @property
    def special_region_statuses(self):
        """Returns the null/pending/public status for each region."""
        statuses = {}
        for region in self.special_region_objs:
            statuses[region.id] = self.product.geodata.get_status_slug(region)
        return statuses

    @property
    def special_region_messages(self):
        """Returns the L10n messages for each region's status."""
        return self.product.geodata.get_status_messages()

    def is_toggling(self):
        if not self.request or not hasattr(self.request, 'POST'):
            return False
        value = self.request.POST.get('toggle-paid')
        return value if value in ('free', 'paid') else False

    def _product_is_paid(self):
        return (self.product.premium_type in amo.ADDON_PREMIUMS
                or self.product.premium_type == amo.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']]
        special_regions = [
            int(x) for x in self.cleaned_data['special_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 mark 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 exluded from region (%s).' %
                         (self.product, region))
        else:
            self.product.addonexcludedregion.all().delete()
            log.info(u'[Webapp:%s] App mark as unrestricted.' % self.product)

        self.product.geodata.update(restricted=restricted)

        # Toggle region exclusions/statuses for special regions (e.g., China).
        toggle_app_for_special_regions(self.request, self.product,
                                       special_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)
Ejemplo n.º 43
0
import jingo
from caching.base import CachingManager, CachingMixin
from funfactory.manage import path
from funfactory.urlresolvers import reverse
from jinja2 import Markup
from tower import ugettext_lazy as _lazy

from badges.models import Badge, BadgeInstance
from banners import COLOR_CHOICES
from shared.models import LocaleField, ModelBase
from shared.storage import OverwritingStorage
from shared.utils import (absolutify, product_languages_lower, ugettext_locale
                          as _locale)

# L10n: Width and height are the width and height of an image.
SIZE = _lazy('%(width)sx%(height)s pixels')

BANNER_TEMPLATE_FILE = 'apps/banners/templates/banners/banner_template.html'
with open(path(BANNER_TEMPLATE_FILE)) as f:
    BANNER_TEMPLATE = f.read()


def rename(instance, filename):
    props = '%d_%s_%s_%s_%s' % (instance.banner_id, instance.image.width,
                                instance.image.height, instance.color,
                                instance.locale)
    hash = hashlib.sha1(props).hexdigest()
    extension = os.path.splitext(filename)[1]
    name = '%s%s' % (hash, extension)
    return os.path.join(settings.BANNER_IMAGE_PATH, name)
Ejemplo n.º 44
0
class UserProfilePrivacyModel(models.Model):
    _privacy_level = None

    privacy_photo = PrivacyField()
    privacy_full_name = PrivacyField()
    privacy_ircname = PrivacyField()
    privacy_email = PrivacyField()
    privacy_bio = PrivacyField()
    privacy_geo_city = PrivacyField()
    privacy_geo_region = PrivacyField()
    privacy_geo_country = PrivacyField()
    privacy_groups = PrivacyField()
    privacy_skills = PrivacyField()
    privacy_languages = PrivacyField()
    privacy_date_mozillian = PrivacyField()
    privacy_timezone = PrivacyField()
    privacy_tshirt = PrivacyField(choices=((PRIVILEGED,
                                            _lazy(u'Privileged')), ),
                                  default=PRIVILEGED)
    privacy_title = PrivacyField()
    privacy_story_link = PrivacyField()

    CACHED_PRIVACY_FIELDS = None

    class Meta:
        abstract = True

    @classmethod
    def clear_privacy_fields_cache(cls):
        """
        Clear any caching of the privacy fields.
        (This is only used in testing.)
        """
        cls.CACHED_PRIVACY_FIELDS = None

    @classmethod
    def privacy_fields(cls):
        """
        Return a dictionary whose keys are the names of the fields in this
        model that are privacy-controlled, and whose values are the default
        values to use for those fields when the user is not privileged to
        view their actual value.
        """
        # Cache on the class object
        if cls.CACHED_PRIVACY_FIELDS is None:
            privacy_fields = {}
            field_names = cls._meta.get_all_field_names()
            for name in field_names:
                if name.startswith(
                        'privacy_') or not 'privacy_%s' % name in field_names:
                    # skip privacy fields and uncontrolled fields
                    continue
                field = cls._meta.get_field(name)
                # Okay, this is a field that is privacy-controlled
                # Figure out a good default value for it (to show to users
                # who aren't privileged to see the actual value)
                if isinstance(field, ManyToManyField):
                    default = field.related.parent_model.objects.none()
                else:
                    default = field.get_default()
                privacy_fields[name] = default
            # HACK: There's not really an email field on UserProfile,
            # but it's faked with a property
            privacy_fields['email'] = u''

            cls.CACHED_PRIVACY_FIELDS = privacy_fields
        return cls.CACHED_PRIVACY_FIELDS
Ejemplo n.º 45
0
class UserProfile(UserProfilePrivacyModel):
    REFERRAL_SOURCE_CHOICES = (
        ('direct', 'Mozillians'),
        ('contribute', 'Get Involved'),
    )

    objects = UserProfileManager()

    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, default=datetime.now)
    groups = models.ManyToManyField(Group,
                                    blank=True,
                                    related_name='members',
                                    through=GroupMembership)
    skills = models.ManyToManyField(Skill, blank=True, related_name='members')
    bio = models.TextField(verbose_name=_lazy(u'Bio'), default='', blank=True)
    photo = ImageField(default='',
                       blank=True,
                       upload_to=_calculate_photo_filename)
    ircname = models.CharField(max_length=63,
                               verbose_name=_lazy(u'IRC Nickname'),
                               default='',
                               blank=True)

    # 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)

    allows_community_sites = models.BooleanField(
        default=True,
        verbose_name=_lazy(u'Sites that can determine my vouched status'),
        choices=((True, _lazy(u'All Community Sites')),
                 (False, _lazy(u'Only Mozilla Properties'))))
    allows_mozilla_sites = models.BooleanField(
        default=True,
        verbose_name=_lazy(u'Allow Mozilla sites to access my profile data?'),
        choices=((True, _lazy(u'Yes')), (False, _lazy(u'No'))))
    basket_token = models.CharField(max_length=1024, default='', blank=True)
    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))
    tshirt = models.IntegerField(
        _lazy(u'T-Shirt'),
        blank=True,
        null=True,
        default=None,
        choices=((1, _lazy(u'Fitted Small')), (2, _lazy(u'Fitted Medium')),
                 (3, _lazy(u'Fitted Large')), (4, _lazy(u'Fitted X-Large')),
                 (5, _lazy(u'Fitted XX-Large')), (6,
                                                  _lazy(u'Fitted XXX-Large')),
                 (7, _lazy(u'Straight-cut Small')),
                 (8, _lazy(u'Straight-cut Medium')),
                 (9, _lazy(u'Straight-cut Large')),
                 (10, _lazy(u'Straight-cut X-Large')),
                 (11, _lazy(u'Straight-cut XX-Large')),
                 (12, _lazy(u'Straight-cut XXX-Large'))))
    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='')
    referral_source = models.CharField(max_length=32,
                                       choices=REFERRAL_SOURCE_CHOICES,
                                       default='direct')

    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'
        }

        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 _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 and _getattr(
                'privacy_email') < self._privacy_level:
            email = privacy_fields['email']
            return 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.groups.filter(name='Managers').exists()
                or self.user.is_superuser):
            return PRIVILEGED
        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."""
        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 or self.user.groups.filter(
            name='Managers').exists()

    @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 set_membership(self, model, membership_list):
        """Alters membership to Groups and Skills."""
        if model is Group:
            m2mfield = self.groups
            alias_model = GroupAlias
        elif model is Skill:
            m2mfield = self.skills
            alias_model = SkillAlias

        # Remove any visible groups that weren't supplied in this list.
        if model is Group:
            GroupMembership.objects.filter(userprofile=self, group__visible=True)\
                .exclude(group__name__in=membership_list).delete()
        else:
            m2mfield.remove(*[
                g for g in m2mfield.all()
                if g.name not in membership_list and g.is_visible
            ])

        # Add/create the rest of the groups
        groups_to_add = []
        for g in membership_list:
            if alias_model.objects.filter(name=g).exists():
                group = alias_model.objects.get(name=g).alias
            else:
                group = model.objects.create(name=g)

            if group.is_visible:
                groups_to_add.append(group)

        if model is Group:
            for group in groups_to_add:
                group.add_member(self)
        else:
            m2mfield.add(*groups_to_add)

    def get_photo_thumbnail(self, geometry='160x160', **kwargs):
        if 'crop' not in kwargs:
            kwargs['crop'] = 'center'
        if self.photo:
            return get_thumbnail(self.photo, geometry, **kwargs)
        return get_thumbnail(settings.DEFAULT_AVATAR_PATH, 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.user.email, size=geometry)
        return self.get_photo_thumbnail(geometry, **kwargs).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 vouch(self, vouched_by, description='', autovouch=False):
        if not self.is_vouchable(vouched_by):
            return

        vouch = self.vouches_received.create(voucher=vouched_by,
                                             date=datetime.now(),
                                             description=description,
                                             autovouch=autovouch)

        self._email_now_vouched(vouched_by, description)
        return vouch

    def auto_vouch(self):
        """Auto vouch mozilla.com users."""
        email = self.user.email

        if any(email.endswith('@' + x) for x in settings.AUTO_VOUCH_DOMAINS):
            if not self.vouches_received.filter(
                    description=settings.AUTO_VOUCH_REASON,
                    autovouch=True).exists():
                self.vouch(None, settings.AUTO_VOUCH_REASON, autovouch=True)

    def _email_now_vouched(self, vouched_by, description=''):
        """Email this user, letting them know they are now vouched."""
        name = None
        voucher_profile_link = None
        vouchee_profile_link = utils.absolutify(self.get_absolute_url())
        if vouched_by:
            name = vouched_by.full_name
            voucher_profile_link = utils.absolutify(
                vouched_by.get_absolute_url())

        number_of_vouches = self.vouches_received.all().count()
        template = get_template(
            'phonebook/emails/vouch_confirmation_email.txt')
        message = template.render({
            'voucher_name':
            name,
            'voucher_profile_url':
            voucher_profile_link,
            'vouchee_profile_url':
            vouchee_profile_link,
            'vouch_description':
            description,
            'functional_areas_url':
            utils.absolutify(reverse('groups:index_functional_areas')),
            'groups_url':
            utils.absolutify(reverse('groups:index_groups')),
            'first_vouch':
            number_of_vouches == 1,
            'can_vouch_threshold':
            number_of_vouches == settings.CAN_VOUCH_THRESHOLD,
        })
        subject = _(u'You have been vouched on Mozillians.org')
        filtered_message = message.replace('&#34;', '"').replace('&#39;', "'")
        send_mail(subject, filtered_message, settings.FROM_NOREPLY,
                  [self.user.email])

    def lookup_basket_token(self):
        """
        Query Basket for this user's token.  If Basket doesn't find the user,
        returns None. If Basket does find the token, returns it. Otherwise,
        there must have been some error from the network or basket, and this
        method just lets that exception propagate so the caller can decide how
        best to handle it.

        (Does not update the token field on the UserProfile.)
        """
        try:
            result = basket.lookup_user(email=self.user.email)
        except basket.BasketException as exception:
            if exception.code == basket.errors.BASKET_UNKNOWN_EMAIL:
                return None
            raise
        return result['token']

    def get_annotated_groups(self):
        """
        Return a list of all the visible groups the user is a member of or pending
        membership. The groups pending membership will have a .pending attribute
        set to True, others will have it set False.
        """
        groups = []
        # Query this way so we only get the groups that the privacy controls allow the
        # current user to see. We have to force evaluation of this query first, otherwise
        # Django combines the whole thing into one query and loses the privacy control.
        groups_manager = self.groups
        # checks to avoid AttributeError exception b/c self.groups may returns
        # EmptyQuerySet instead of the default manager due to privacy controls
        if hasattr(groups_manager, 'visible'):
            user_group_ids = list(groups_manager.visible().values_list(
                'id', flat=True))
        else:
            user_group_ids = []
        for membership in self.groupmembership_set.filter(
                group_id__in=user_group_ids):
            group = membership.group
            group.pending = (membership.status == GroupMembership.PENDING)
            groups.append(group)
        return groups

    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
        super(UserProfile, self).save(*args, **kwargs)
        # Auto_vouch follows the first save, because you can't
        # create foreign keys without a database id.
        self.auto_vouch()

    def reverse_geocode(self):
        """
        Use the user's lat and lng to set their city, region, and country.
        Does not save the profile.
        """
        if self.lat is None or self.lng is None:
            return

        from mozillians.geo.models import Country
        from mozillians.geo.lookup import reverse_geocode, GeoLookupException
        try:
            result = reverse_geocode(self.lat, self.lng)
        except GeoLookupException:
            if self.geo_country:
                # If self.geo_country is already set, just give up.
                pass
            else:
                # No country set, we need to at least set the placeholder one.
                self.geo_country = Country.objects.get(mapbox_id='geo_error')
                self.geo_region = None
                self.geo_city = None
        else:
            if result:
                country, region, city = result
                self.geo_country = country
                self.geo_region = region
                self.geo_city = city
            else:
                logger.error('Got back NONE from reverse_geocode on %s, %s' %
                             (self.lng, self.lat))
Ejemplo n.º 46
0
from tower import ugettext as _, ugettext_lazy as _lazy

from kitsune import search as constants
from kitsune.forums.models import Forum, ThreadMappingType
from kitsune.products.models import Product
from kitsune.questions.models import QuestionMappingType
from kitsune.search.models import get_mapping_types
from kitsune.search.utils import locale_or_default, clean_excerpt, ComposedList
from kitsune.search import es_utils
from kitsune.search.forms import SearchForm
from kitsune.search.es_utils import ES_EXCEPTIONS, F, AnalyzerS
from kitsune.sumo.utils import paginate, smart_int
from kitsune.wiki.facets import documents_for
from kitsune.wiki.models import DocumentMappingType

EXCERPT_JOINER = _lazy(u'...', 'between search excerpts')


def jsonp_is_valid(func):
    func_regex = re.compile(
        r"""
        ^[a-zA-Z_\$]
        [a-zA-Z0-9_\$]*
        (\[[a-zA-Z0-9_\$]*\])*
        (\.[a-zA-Z0-9_\$]+
            (\[[a-zA-Z0-9_\$]*\])*
        )*$
    """, re.VERBOSE)
    return func_regex.match(func)

Ejemplo n.º 47
0
from django.db.models import Q, Manager, get_model
from django.db.models.query import QuerySet, ValuesQuerySet

from tower import ugettext_lazy as _lazy


PRIVILEGED = 1
EMPLOYEES = 2
MOZILLIANS = 3
PUBLIC = 4
PRIVACY_CHOICES = ((MOZILLIANS, _lazy(u'Mozillians')),
                   (PUBLIC, _lazy(u'Public')))
PUBLIC_INDEXABLE_FIELDS = ['full_name', 'ircname', 'email']


class UserProfileValuesQuerySet(ValuesQuerySet):
    """Custom ValuesQuerySet to support privacy.

    Note that when you specify fields in values() you need to include
    the related privacy field in your query.

    E.g. .values('first_name', 'privacy_first_name')

    """

    def _clone(self, *args, **kwargs):
        c = super(UserProfileValuesQuerySet, self)._clone(*args, **kwargs)
        c._privacy_level = getattr(self, '_privacy_level', None)
        return c

    def iterator(self):
Ejemplo n.º 48
0
def translate(request, document_slug, revision_id=None):
    """Create a new translation of a wiki document.

    * document_slug is for the default locale
    * translation is to the request locale

    """
    # TODO: Refactor this view into two views? (new, edit)
    # That might help reduce the headache-inducing branchiness.
    parent_doc = get_object_or_404(Document,
                                   locale=settings.WIKI_DEFAULT_LANGUAGE,
                                   slug=document_slug)
    user = request.user

    if settings.WIKI_DEFAULT_LANGUAGE == request.locale:
        # Don't translate to the default language.
        return HttpResponseRedirect(
            reverse('wiki.edit_document',
                    locale=settings.WIKI_DEFAULT_LANGUAGE,
                    args=[parent_doc.slug]))

    if not parent_doc.is_localizable:
        message = _lazy(u'You cannot translate this document.')
        return jingo.render(request,
                            'handlers/400.html', {'message': message},
                            status=400)

    based_on_rev = get_current_or_latest_revision(parent_doc,
                                                  reviewed_only=False)

    disclose_description = bool(request.GET.get('opendescription'))

    try:
        doc = parent_doc.translations.get(locale=request.locale)
    except Document.DoesNotExist:
        doc = None
        disclose_description = True

    user_has_doc_perm = ((not doc) or (doc and doc.allows_editing_by(user)))
    user_has_rev_perm = ((not doc) or (doc and doc.allows_revision_by(user)))
    if not user_has_doc_perm and not user_has_rev_perm:
        # User has no perms, bye.
        raise PermissionDenied

    doc_form = rev_form = None
    base_rev = None

    if user_has_doc_perm:
        doc_initial = _document_form_initial(doc) if doc else None
        doc_form = DocumentForm(
            initial=doc_initial,
            can_create_tags=user.has_perm('taggit.add_tag'))
    if user_has_rev_perm:
        initial = {'based_on': based_on_rev.id, 'comment': ''}
        if revision_id:
            base_rev = Revision.objects.get(pk=revision_id)
            initial.update(content=base_rev.content,
                           summary=base_rev.summary,
                           keywords=base_rev.keywords)
        elif not doc:
            initial.update(content=based_on_rev.content,
                           summary=based_on_rev.summary,
                           keywords=based_on_rev.keywords)
        instance = doc and get_current_or_latest_revision(doc)
        rev_form = RevisionForm(instance=instance, initial=initial)
        base_rev = base_rev or instance

    if request.method == 'POST':
        which_form = request.POST.get('form', 'both')
        doc_form_invalid = False

        if user_has_doc_perm and which_form in ['doc', 'both']:
            disclose_description = True
            post_data = request.POST.copy()
            post_data.update({'locale': request.locale})
            doc_form = DocumentForm(
                post_data,
                instance=doc,
                can_create_tags=user.has_perm('taggit.add_tag'))
            doc_form.instance.locale = request.locale
            doc_form.instance.parent = parent_doc
            if which_form == 'both':
                rev_form = RevisionForm(request.POST)

            # If we are submitting the whole form, we need to check that
            # the Revision is valid before saving the Document.
            if doc_form.is_valid() and (which_form == 'doc'
                                        or rev_form.is_valid()):
                doc = doc_form.save(parent_doc)

                # Possibly schedule a rebuild.
                _maybe_schedule_rebuild(doc_form)

                if which_form == 'doc':
                    url = urlparams(reverse('wiki.edit_document',
                                            args=[doc.slug]),
                                    opendescription=1)
                    return HttpResponseRedirect(url)

                doc_slug = doc_form.cleaned_data['slug']
            else:
                doc_form_invalid = True
        else:
            doc_slug = doc.slug

        if doc and user_has_rev_perm and which_form in ['rev', 'both']:
            rev_form = RevisionForm(request.POST)
            rev_form.instance.document = doc  # for rev_form.clean()
            if rev_form.is_valid() and not doc_form_invalid:
                _save_rev_and_notify(rev_form, request.user, doc)
                url = reverse('wiki.document_revisions', args=[doc_slug])
                return HttpResponseRedirect(url)

    show_revision_warning = _show_revision_warning(doc, base_rev)

    return jingo.render(
        request, 'wiki/translate.html', {
            'parent': parent_doc,
            'document': doc,
            'document_form': doc_form,
            'revision_form': rev_form,
            'locale': request.locale,
            'based_on': based_on_rev,
            'disclose_description': disclose_description,
            'show_revision_warning': show_revision_warning
        })
Ejemplo n.º 49
0
class UserProfile(SearchMixin, models.Model):
    # This field is required.
    user = models.OneToOneField(User)

    # Other fields here
    confirmation_code = models.CharField(max_length=32,
                                         editable=False,
                                         unique=True)
    is_confirmed = models.BooleanField(default=False)
    is_vouched = models.BooleanField(default=False)
    website = models.URLField(max_length=200,
                              verbose_name=_lazy(u'Website'),
                              blank=True,
                              null=True)

    # Foreign Keys and Relationships
    vouched_by = models.ForeignKey('UserProfile', null=True)
    groups = models.ManyToManyField('groups.Group')
    bio = models.CharField(max_length=255,
                           verbose_name=_lazy(u'Bio'),
                           blank=True)
    photo = models.BooleanField(default=False)
    display_name = models.CharField(max_length=255)
    ircname = models.CharField(max_length=63,
                               verbose_name=_lazy(u'IRC Nickname'),
                               blank=True)
    objects = UserProfileManager()

    class Meta:
        db_table = 'profile'

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

    def vouch(self, vouched_by, system=True, commit=True):
        changed = system  # do we need to do a vouch?
        if system:
            self.is_vouched = True

        if vouched_by and vouched_by.is_vouched:
            changed = True
            self.is_vouched = True
            self.vouched_by = vouched_by

        if commit and changed:
            self.save()
            # Email the user and tell them they were vouched.
            self._email_now_vouched()

    def get_confirmation_url(self):
        url = (absolutify(reverse('confirm')) + '?code=' +
               self.confirmation_code)
        return url

    def get_send_confirmation_url(self):
        url = (reverse('send_confirmation') + '?' +
               urllib.urlencode({'user': self.user.username}))
        return url

    def get_photo_url(self, cachebust=False):
        """Gets a user's userpic URL.  Appends cachebusting if requested."""
        # Import this here to avoid circular imports because other apps are loading this file in init.py
        from phonebook.helpers import gravatar
        if self.photo:
            url = '%s/%d.jpg' % (settings.USERPICS_URL, self.user_id)

            if cachebust:
                url += '?%d' % int(time.time())
        else:
            url = gravatar(self.user.email)

        return url

    def get_photo_file(self):
        return '%s/%d.jpg' % (settings.USERPICS_PATH, self.user_id)

    # TODO: get rid of this when larper is gone ETA Apr-2012
    def get_unique_id(self):
        r = self.get_ldap_person()
        return r[1]['uniqueIdentifier'][0]

    def get_ldap_person(self):
        email = self.user.email or self.user.username
        return larper.get_user_by_email(email)

    def _email_now_vouched(self):
        """Email this user, letting them know they are now vouched."""
        subject = _(u'You are now vouched on Mozillians!')
        message = _(u"You've now been vouched on Mozillians.org. "
                    "You'll now be able to search, vouch "
                    "and invite other Mozillians onto the site.")
        send_mail(subject, message, '*****@*****.**',
                  [self.user.email])

    @property
    def full_name(self):
        "%s %s" % (self.user.first_name, self.user.last_name)

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

    def fields(self):
        attrs = ('id', 'is_confirmed', 'is_vouched', 'website', 'bio', 'photo',
                 'display_name', 'ircname')
        d = dict((a, getattr(self, a)) for a in attrs)
        # user data
        attrs = ('username', 'first_name', 'last_name', 'email', 'last_login',
                 'date_joined')
        d.update(dict((a, getattr(self.user, a)) for a in attrs))
        # Index group ids... for fun.
        groups = list(self.groups.values_list('name', flat=True))
        d.update(dict(groups=groups))
        return d

    @classmethod
    def search(cls, query, vouched=None):
        """Sensible default search for UserProfiles."""
        query = query.lower().strip()
        fields = ('first_name__text', 'last_name__text', 'display_name__text',
                  'username__text', 'bio__text', 'website__text',
                  'email__text', 'groups__text', 'first_name__startswith',
                  'last_name__startswith')
        q = dict((field, query) for field in fields)
        s = S(cls).query(or_=q)
        if vouched is not None:
            s = s.filter(is_vouched=vouched)
        return s
Ejemplo n.º 50
0
from tower import ugettext_lazy as _lazy
from tower import ugettext as _

from django import forms
from django.conf import settings
from django.forms.widgets import CheckboxSelectMultiple

from contentflagging.forms import ContentFlagForm
import kuma.wiki.content
from sumo.form_fields import StrippedCharField
from .constants import (SLUG_CLEANSING_REGEX, REVIEW_FLAG_TAGS,
                        LOCALIZATION_FLAG_TAGS, RESERVED_SLUGS)
from .models import (Document, Revision, valid_slug_parent)

TITLE_REQUIRED = _lazy(u'Please provide a title.')
TITLE_SHORT = _lazy(u'The title is too short (%(show_value)s characters). '
                    u'It must be at least %(limit_value)s characters.')
TITLE_LONG = _lazy(u'Please keep the length of the title to %(limit_value)s '
                   u'characters or less. It is currently %(show_value)s '
                   u'characters.')
TITLE_PLACEHOLDER = _lazy(u'Name Your Article')
SLUG_REQUIRED = _lazy(u'Please provide a slug.')
SLUG_INVALID = _lazy(u'The slug provided is not valid.')
SLUG_SHORT = _lazy(u'The slug is too short (%(show_value)s characters). '
                   u'It must be at least %(limit_value)s characters.')
SLUG_LONG = _lazy(u'Please keep the length of the slug to %(limit_value)s '
                  u'characters or less. It is currently %(show_value)s '
                  u'characters.')
SUMMARY_REQUIRED = _lazy(u'Please provide a summary.')
SUMMARY_SHORT = _lazy(u'The summary is too short (%(show_value)s characters). '
Ejemplo n.º 51
0
    def _review_actions(self):
        labels = {'prelim': _lazy('Grant preliminary review')}
        details = {
            'prelim':
            _lazy('This will mark the files as '
                  'preliminarily reviewed.'),
            'info':
            _lazy('Use this form to request more information '
                  'from the author. They will receive an email '
                  'and be able to answer here. You will be '
                  'notified by email when they reply.'),
            'super':
            _lazy('If you have concerns about this add-on\'s '
                  'security, copyright issues, or other '
                  'concerns that an administrator should look '
                  'into, enter your comments in the area '
                  'below. They will be sent to '
                  'administrators, not the author.'),
            'reject':
            _lazy('This will reject the add-on and remove '
                  'it from the review queue.'),
            'comment':
            _lazy('Make a comment on this version.  The '
                  'author won\'t be able to see this.')
        }

        if self.addon.status == amo.STATUS_LITE:
            details['reject'] = _lazy('This will reject the files and remove '
                                      'them from the review queue.')

        if self.addon.status in (amo.STATUS_UNREVIEWED, amo.STATUS_NOMINATED):
            details['prelim'] = _lazy('This will mark the add-on as '
                                      'preliminarily reviewed. Future '
                                      'versions will undergo '
                                      'preliminary review.')
        elif self.addon.status == amo.STATUS_LITE:
            details['prelim'] = _lazy('This will mark the files as '
                                      'preliminarily reviewed. Future '
                                      'versions will undergo '
                                      'preliminary review.')
        elif self.addon.status == amo.STATUS_LITE_AND_NOMINATED:
            labels['prelim'] = _lazy('Retain preliminary review')
            details['prelim'] = _lazy('This will retain the add-on as '
                                      'preliminarily reviewed. Future '
                                      'versions will undergo preliminary '
                                      'review.')
        if self.review_type == 'pending':
            details['public'] = _lazy('This will approve a sandboxed version '
                                      'of a public add-on to appear on the '
                                      'public side.')
            details['reject'] = _lazy('This will reject a version of a public '
                                      'add-on and remove it from the queue.')
        else:
            details['public'] = _lazy('This will mark the add-on and its most '
                                      'recent version and files as public. '
                                      'Future versions will go into the '
                                      'sandbox until they are reviewed by an '
                                      'editor.')

        return labels, details
Ejemplo n.º 52
0
class RevisionForm(forms.ModelForm):
    """Form to create new revisions."""

    title = StrippedCharField(
        min_length=1,
        max_length=255,
        required=False,
        widget=forms.TextInput(attrs={'placeholder': TITLE_PLACEHOLDER}),
        label=_lazy(u'Title:'),
        help_text=_lazy(u'Title of article'),
        error_messages={
            'required': TITLE_REQUIRED,
            'min_length': TITLE_SHORT,
            'max_length': TITLE_LONG
        })
    slug = StrippedCharField(min_length=1,
                             max_length=255,
                             required=False,
                             widget=forms.TextInput(),
                             label=_lazy(u'Slug:'),
                             help_text=_lazy(u'Article URL'),
                             error_messages={
                                 'required': SLUG_REQUIRED,
                                 'min_length': SLUG_SHORT,
                                 'max_length': SLUG_LONG
                             })

    tags = StrippedCharField(required=False, label=_lazy(u'Tags:'))

    keywords = StrippedCharField(required=False,
                                 label=_lazy(u'Keywords:'),
                                 help_text=_lazy(u'Affects search results'))

    summary = StrippedCharField(
        required=False,
        min_length=5,
        max_length=1000,
        widget=forms.Textarea(),
        label=_lazy(u'Search result summary:'),
        help_text=_lazy(u'Only displayed on search results page'),
        error_messages={
            'required': SUMMARY_REQUIRED,
            'min_length': SUMMARY_SHORT,
            'max_length': SUMMARY_LONG
        })

    content = StrippedCharField(min_length=5,
                                max_length=300000,
                                label=_lazy(u'Content:'),
                                widget=forms.Textarea(),
                                error_messages={
                                    'required': CONTENT_REQUIRED,
                                    'min_length': CONTENT_SHORT,
                                    'max_length': CONTENT_LONG
                                })

    comment = StrippedCharField(required=False, label=_lazy(u'Comment:'))

    review_tags = forms.MultipleChoiceField(
        label=_("Tag this revision for review?"),
        widget=CheckboxSelectMultiple,
        required=False,
        choices=REVIEW_FLAG_TAGS)

    localization_tags = forms.MultipleChoiceField(
        label=_("Tag this revision for localization?"),
        widget=CheckboxSelectMultiple,
        required=False,
        choices=LOCALIZATION_FLAG_TAGS)

    current_rev = forms.CharField(required=False, widget=forms.HiddenInput())

    class Meta(object):
        model = Revision
        fields = ('title', 'slug', 'tags', 'keywords', 'summary', 'content',
                  'comment', 'based_on', 'toc_depth', 'render_max_age')

    def __init__(self, *args, **kwargs):

        # Snag some optional kwargs and delete them before calling
        # super-constructor.
        for n in ('section_id', 'is_iframe_target'):
            if n not in kwargs:
                setattr(self, n, None)
            else:
                setattr(self, n, kwargs[n])
                del kwargs[n]

        super(RevisionForm, self).__init__(*args, **kwargs)
        self.fields['based_on'].widget = forms.HiddenInput()

        if self.instance and self.instance.pk:

            # Ensure both title and slug are populated from parent document, if
            # last revision didn't have them
            if not self.instance.title:
                self.initial['title'] = self.instance.document.title
            if not self.instance.slug:
                self.initial['slug'] = self.instance.document.slug

            content = self.instance.content
            if not self.instance.document.is_template:
                tool = kuma.wiki.content.parse(content)
                tool.injectSectionIDs()
                if self.section_id:
                    tool.extractSection(self.section_id)
                tool.filterEditorSafety()
                content = tool.serialize()
            self.initial['content'] = content

            self.initial['review_tags'] = [
                x.name for x in self.instance.review_tags.all()
            ]
            self.initial['localization_tags'] = [
                x.name for x in self.instance.localization_tags.all()
            ]

        if self.section_id:
            self.fields['toc_depth'].required = False

    def _clean_collidable(self, name):
        value = self.cleaned_data[name]

        if self.is_iframe_target:
            # Since these collidables can change the URL of the page, changes
            # to them are ignored for an iframe submission
            return getattr(self.instance.document, name)

        error_message = {'slug': SLUG_COLLIDES}.get(name, OTHER_COLLIDES)
        try:
            existing_doc = Document.objects.get(
                locale=self.instance.document.locale, **{name: value})
            if self.instance and self.instance.document:
                if (not existing_doc.redirect_url()
                        and existing_doc.pk != self.instance.document.pk):
                    # There's another document with this value,
                    # and we're not a revision of it.
                    raise forms.ValidationError(error_message)
            else:
                # This document-and-revision doesn't exist yet, so there
                # shouldn't be any collisions at all.
                raise forms.ValidationError(error_message)

        except Document.DoesNotExist:
            # No existing document for this value, so we're good here.
            pass

        return value

    def clean_slug(self):
        # TODO: move this check somewhere else?
        # edits can come in without a slug, so default to the current doc slug
        if not self.cleaned_data['slug']:
            existing_slug = self.instance.document.slug
            self.cleaned_data['slug'] = self.instance.slug = existing_slug
        cleaned_slug = self._clean_collidable('slug')
        return cleaned_slug

    def clean_content(self):
        """Validate the content, performing any section editing if necessary"""
        content = self.cleaned_data['content']

        # If we're editing a section, we need to replace the section content
        # from the current revision.
        if self.section_id and self.instance and self.instance.document:
            # Make sure we start with content form the latest revision.
            full_content = self.instance.document.current_revision.content
            # Replace the section content with the form content.
            tool = kuma.wiki.content.parse(full_content)
            tool.replaceSection(self.section_id, content)
            content = tool.serialize()

        return content

    def clean_current_rev(self):
        """If a current revision is supplied in the form, compare it against
        what the document claims is the current revision. If there's a
        difference, then an edit has occurred since the form was constructed
        and we treat it as a mid-air collision."""
        current_rev = self.cleaned_data.get('current_rev', None)

        if not current_rev:
            # If there's no current_rev, just bail.
            return current_rev

        try:
            doc_current_rev = self.instance.document.current_revision.id
            if unicode(current_rev) != unicode(doc_current_rev):

                if (self.section_id and self.instance
                        and self.instance.document):
                    # This is a section edit. So, even though the revision has
                    # changed, it still might not be a collision if the section
                    # in particular hasn't changed.
                    orig_ct = (Revision.objects.get(
                        pk=current_rev).get_section_content(self.section_id))
                    curr_ct = (self.instance.document.current_revision.
                               get_section_content(self.section_id))
                    if orig_ct != curr_ct:
                        # Oops. Looks like the section did actually get
                        # changed, so yeah this is a collision.
                        raise forms.ValidationError(MIDAIR_COLLISION)

                    return current_rev

                else:
                    # No section edit, so this is a flat-out collision.
                    raise forms.ValidationError(MIDAIR_COLLISION)

        except Document.DoesNotExist:
            # If there's no document yet, just bail.
            return current_rev

    def save_section(self, creator, document, **kwargs):
        """Save a section edit."""
        # This is separate because the logic is slightly different and
        # may need to evolve over time; a section edit doesn't submit
        # all the fields, and we need to account for that when we
        # construct the new Revision.

        old_rev = Document.objects.get(
            pk=self.instance.document.id).current_revision
        new_rev = super(RevisionForm, self).save(commit=False, **kwargs)
        new_rev.document = document
        new_rev.creator = creator
        new_rev.toc_depth = old_rev.toc_depth
        new_rev.save()
        new_rev.review_tags.set(*[t.name for t in old_rev.review_tags.all()])
        return new_rev

    def save(self, creator, document, **kwargs):
        """Persist me, and return the saved Revision.

        Take several other necessary pieces of data that aren't from the
        form.

        """
        if self.section_id and self.instance and \
           self.instance.document:
            return self.save_section(creator, document, **kwargs)
        # Throws a TypeError if somebody passes in a commit kwarg:
        new_rev = super(RevisionForm, self).save(commit=False, **kwargs)

        new_rev.document = document
        new_rev.creator = creator
        new_rev.toc_depth = self.cleaned_data['toc_depth']
        new_rev.save()
        new_rev.review_tags.set(*self.cleaned_data['review_tags'])
        new_rev.localization_tags.set(*self.cleaned_data['localization_tags'])
        return new_rev
Ejemplo n.º 53
0
class PackagerFeaturesForm(forms.Form):
    about_dialog = forms.BooleanField(
        required=False,
        label=_lazy(u'About dialog'),
        help_text=_lazy(u'Creates a standard About dialog for your '
                        'extension'))
    preferences_dialog = forms.BooleanField(
        required=False,
        label=_lazy(u'Preferences dialog'),
        help_text=_lazy(u'Creates an example Preferences window'))
    toolbar = forms.BooleanField(
        required=False,
        label=_lazy(u'Toolbar'),
        help_text=_lazy(u'Creates an example toolbar for your extension'))
    toolbar_button = forms.BooleanField(
        required=False,
        label=_lazy(u'Toolbar button'),
        help_text=_lazy(u'Creates an example button on the browser '
                        'toolbar'))
    main_menu_command = forms.BooleanField(
        required=False,
        label=_lazy(u'Main menu command'),
        help_text=_lazy(u'Creates an item on the Tools menu'))
    context_menu_command = forms.BooleanField(
        required=False,
        label=_lazy(u'Context menu command'),
        help_text=_lazy(u'Creates a context menu item for images'))
    sidebar_support = forms.BooleanField(
        required=False,
        label=_lazy(u'Sidebar support'),
        help_text=_lazy(u'Creates an example sidebar panel'))
Ejemplo n.º 54
0
 def __unicode__(self):
     message = _lazy(u'%s banned by %s') % (self.user, self.by)
     if not self.is_active:
         message = _lazy(u"%s (no longer active)") % message
     return message
Ejemplo n.º 55
0
class ProfileForm(happyforms.ModelForm):
    photo = forms.ImageField(label=_lazy(u'Profile Photo'), required=False)
    photo_delete = forms.BooleanField(label=_lazy(u'Remove Profile Photo'),
                                      required=False)
    date_mozillian = forms.DateField(required=False,
                                     widget=MonthYearWidget(years=range(
                                         1998,
                                         datetime.today().year + 1),
                                                            required=False))
    groups = forms.CharField(label=_lazy(
        u'Start typing to add a group (example: Marketing, '
        'Support, WebDev, Thunderbird)'),
                             required=False)
    languages = forms.CharField(label=_lazy(
        u'Start typing to add a language you speak (example: '
        'English, French, German)'),
                                required=False)
    skills = forms.CharField(label=_lazy(
        u'Start typing to add a skill (example: Python, '
        'javascript, Graphic Design, User Research)'),
                             required=False)
    timezone = forms.ChoiceField(required=False,
                                 choices=zip(common_timezones,
                                             common_timezones))

    class Meta:
        model = UserProfile
        fields = ('full_name', 'ircname', 'website', 'bio', 'photo', 'country',
                  'region', 'city', 'allows_community_sites',
                  'allows_mozilla_sites', 'date_mozillian', 'timezone',
                  'privacy_photo', 'privacy_full_name', 'privacy_ircname',
                  'privacy_email', 'privacy_timezone', 'privacy_website',
                  'privacy_bio', 'privacy_city', 'privacy_region',
                  'privacy_country', 'privacy_groups', 'privacy_skills',
                  'privacy_languages', 'privacy_date_mozillian')
        widgets = {'bio': forms.Textarea()}

    def __init__(self, *args, **kwargs):
        locale = kwargs.pop('locale', 'en-US')

        super(ProfileForm, self).__init__(*args, **kwargs)
        country_list = product_details.get_regions(locale).items()
        country_list = sorted(country_list, key=lambda country: country[1])
        country_list.insert(0, ('', '----'))
        self.fields['country'].choices = country_list

    def clean_groups(self):
        """Groups are saved in lowercase because it's easy and
        consistent.

        """
        if not re.match(r'^[a-zA-Z0-9 .:,-]*$', self.cleaned_data['groups']):
            raise forms.ValidationError(
                _(u'Groups can only contain '
                  'alphanumeric characters, dashes, '
                  'spaces.'))
        system_groups = [
            g.name for g in self.instance.groups.all() if g.system
        ]
        groups = self.cleaned_data['groups']
        new_groups = filter(
            lambda x: x,
            map(lambda x: x.strip() or False,
                groups.lower().split(',')))

        return system_groups + new_groups

    def clean_languages(self):
        if not re.match(r'^[a-zA-Z0-9 .:,-]*$',
                        self.cleaned_data['languages']):
            raise forms.ValidationError(
                _(u'Languages can only contain '
                  'alphanumeric characters, dashes, '
                  'spaces.'))
        languages = self.cleaned_data['languages']

        return filter(
            lambda x: x,
            map(lambda x: x.strip() or False,
                languages.lower().split(',')))

    def clean_skills(self):
        if not re.match(r'^[a-zA-Z0-9 .:,-]*$', self.cleaned_data['skills']):
            raise forms.ValidationError(
                _(u'Skills can only contain '
                  'alphanumeric characters, dashes, '
                  'spaces.'))
        skills = self.cleaned_data['skills']
        return filter(
            lambda x: x,
            map(lambda x: x.strip() or False,
                skills.lower().split(',')))

    def save(self):
        """Save the data to profile."""
        self.instance.set_membership(Group, self.cleaned_data['groups'])
        self.instance.set_membership(Skill, self.cleaned_data['skills'])
        self.instance.set_membership(Language, self.cleaned_data['languages'])
        super(ProfileForm, self).save()
Ejemplo n.º 56
0
class PackagerCompatForm(forms.Form):
    enabled = forms.BooleanField(required=False)
    min_ver = forms.ModelChoiceField(AppVersion.objects.none(),
                                     empty_label=None,
                                     required=False,
                                     label=_lazy(u'Minimum'))
    max_ver = forms.ModelChoiceField(AppVersion.objects.none(),
                                     empty_label=None,
                                     required=False,
                                     label=_lazy(u'Maximum'))

    def __init__(self, *args, **kwargs):
        super(PackagerCompatForm, self).__init__(*args, **kwargs)
        if not self.initial:
            return

        self.app = self.initial['application']
        qs = (AppVersion.objects.filter(
            application=self.app.id).order_by('-version_int'))

        self.fields['enabled'].label = self.app.pretty
        if self.app == amo.FIREFOX:
            self.fields['enabled'].widget.attrs['checked'] = True

        # Don't allow version ranges as the minimum version.
        self.fields['min_ver'].queryset = qs.filter(~Q(version__contains='*'))
        self.fields['max_ver'].queryset = qs.all()

        # Unreasonably hardcode a reasonable default minVersion.
        if self.app in (amo.FIREFOX, amo.MOBILE, amo.THUNDERBIRD):
            try:
                self.fields['min_ver'].initial = qs.filter(
                    version=amo.DEFAULT_MINVER)[0]
            except (IndexError, AttributeError):
                pass

    def clean_min_ver(self):
        if self.cleaned_data['enabled'] and not self.cleaned_data['min_ver']:
            raise_required()
        return self.cleaned_data['min_ver']

    def clean_max_ver(self):
        if self.cleaned_data['enabled'] and not self.cleaned_data['max_ver']:
            raise_required()
        return self.cleaned_data['max_ver']

    def clean(self):
        if self.errors:
            return

        data = self.cleaned_data

        if data['enabled']:
            min_ver = data['min_ver']
            max_ver = data['max_ver']
            if not (min_ver and max_ver):
                raise forms.ValidationError(_('Invalid version range.'))

            if min_ver.version_int > max_ver.version_int:
                raise forms.ValidationError(
                    _('Min version must be less than Max version.'))

            # Pass back the app name and GUID.
            data['min_ver'] = str(min_ver)
            data['max_ver'] = str(max_ver)
            data['name'] = self.app.pretty
            data['guid'] = self.app.guid

        return data
Ejemplo n.º 57
0
class EditorQueueTable(SQLTable, ItemStateTable):
    addon_name = tables.Column(verbose_name=_lazy(u'Addon'))
    addon_type_id = tables.Column(verbose_name=_lazy(u'Type'))
    waiting_time_min = tables.Column(verbose_name=_lazy(u'Waiting Time'))
    flags = tables.Column(verbose_name=_lazy(u'Flags'), sortable=False)
    applications = tables.Column(verbose_name=_lazy(u'Applications'),
                                 sortable=False)
    platforms = tables.Column(verbose_name=_lazy(u'Platforms'), sortable=False)
    additional_info = tables.Column(verbose_name=_lazy(u'Additional'),
                                    sortable=False)

    def render_addon_name(self, row):
        url = reverse('editors.review', args=[row.addon_slug])
        self.increment_item()
        return u'<a href="%s">%s <em>%s</em></a>' % (
            url, jinja2.escape(
                row.addon_name), jinja2.escape(row.latest_version))

    def render_addon_type_id(self, row):
        return amo.ADDON_TYPE[row.addon_type_id]

    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])

    def render_applications(self, row):
        # TODO(Kumar) show supported version ranges on hover (if still needed)
        icon = u'<div class="app-icon ed-sprite-%s" title="%s"></div>'
        return u''.join([
            icon % (amo.APPS_ALL[i].short, amo.APPS_ALL[i].pretty)
            for i in row.application_ids
        ])

    def render_platforms(self, row):
        icons = []
        html = u'<div class="platform-icon plat-sprite-%s" title="%s"></div>'
        for platform in row.file_platform_ids:
            icons.append(html % (amo.PLATFORMS[int(platform)].shortname,
                                 amo.PLATFORMS[int(platform)].name))
        return u''.join(icons)

    def render_flags(self, row):
        return ''.join(u'<div class="app-icon ed-sprite-%s" '
                       u'title="%s"></div>' % flag for flag in row.flags)

    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)

    @classmethod
    def translate_sort_cols(cls, colname):
        legacy_sorts = {
            'name': 'addon_name',
            'age': 'waiting_time_min',
            'type': 'addon_type_id',
        }
        return legacy_sorts.get(colname, colname)

    @classmethod
    def default_order_by(cls):
        return '-waiting_time_min'

    @classmethod
    def review_url(cls, row):
        return reverse('editors.review', args=[row.addon_slug])

    class Meta:
        sortable = True
        columns = [
            'addon_name', 'addon_type_id', 'waiting_time_min', 'flags',
            'applications', 'additional_info'
        ]
Ejemplo n.º 58
0
class PackagerBasicForm(forms.Form):
    name = forms.CharField(
        min_length=5,
        max_length=50,
        help_text=_lazy(u'Give your add-on a name. The most successful '
                        'add-ons give some indication of their function in '
                        'their name.'))
    description = forms.CharField(
        required=False,
        widget=forms.Textarea,
        help_text=_lazy(u'Briefly describe your add-on in one sentence. '
                        'This appears in the Add-ons Manager.'))
    version = forms.CharField(
        max_length=32,
        help_text=_lazy(u'Enter your initial version number. Depending on the '
                        'number of releases and your preferences, this is '
                        'usually 0.1 or 1.0'))
    id = forms.CharField(
        help_text=_lazy(u'Each add-on requires a unique ID in the form of a '
                        'UUID or an email address, such as '
                        '[email protected]. The email address does not '
                        'have to be valid.'))
    package_name = forms.CharField(
        min_length=5,
        max_length=50,
        help_text=_lazy(u'The package name of your add-on used within the '
                        'browser. This should be a short form of its name '
                        '(for example, Test Extension might be '
                        'test_extension).'))
    author_name = forms.CharField(
        help_text=_lazy(u'Enter the name of the person or entity to be '
                        'listed as the author of this add-on.'))
    contributors = forms.CharField(
        required=False,
        widget=forms.Textarea,
        help_text=_lazy(u'Enter the names of any other contributors to this '
                        'extension, one per line.'))

    def clean_name(self):
        name = self.cleaned_data['name']
        addons.forms.clean_name(name)
        name_regex = re.compile('(mozilla|firefox|thunderbird)', re.I)
        if name_regex.match(name):
            raise forms.ValidationError(
                _('Add-on names should not contain Mozilla trademarks.'))
        return name

    def clean_package_name(self):
        slug = self.cleaned_data['package_name']
        if slugify(slug, ok='_', lower=False, delimiter='_') != slug:
            raise forms.ValidationError(
                _('Enter a valid package name consisting of letters, numbers, '
                  'or underscores.'))
        if Addon.objects.filter(slug=slug).exists():
            raise forms.ValidationError(
                _('This package name is already in use.'))
        if BlacklistedSlug.blocked(slug):
            raise forms.ValidationError(
                _(u'The package name cannot be: %s.' % slug))
        return slug

    def clean_id(self):
        id_regex = re.compile(
            """(\{[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\} |  # GUID
                   [a-z0-9-\.\+_]*\@[a-z0-9-\._]+)  # Email format""",
            re.I | re.X)

        if not id_regex.match(self.cleaned_data['id']):
            raise forms.ValidationError(
                _('The add-on ID must be a UUID string or an email '
                  'address.'))
        return self.cleaned_data['id']

    def clean_version(self):
        if not VERSION_RE.match(self.cleaned_data['version']):
            raise forms.ValidationError(_('The version string is invalid.'))
        return self.cleaned_data['version']
Ejemplo n.º 59
0
class AppFormBasic(addons.forms.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)

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

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

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

        if self.instance.is_packaged:
            # Manifest URL field for packaged apps is empty.
            self.fields['manifest_url'].required = False

    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'
            super(AppFormBasic, self)._post_clean()
        finally:
            self._meta.fields[slug_idx] = '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 BlacklistedSlug.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:
            # Only Admins can edit the manifest_url.
            if not acl.action_allowed(self.request, 'Admin', '%'):
                return self.instance.manifest_url
            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()

        return addonform
Ejemplo n.º 60
0
class Profile(ModelBase):
    """Profile model for django users, get it with user.get_profile()."""

    user = models.OneToOneField(User,
                                primary_key=True,
                                verbose_name=_lazy(u'User'))
    name = models.CharField(max_length=255,
                            null=True,
                            blank=True,
                            verbose_name=_lazy(u'Display name'))
    public_email = models.BooleanField(  # show/hide email
        default=False,
        verbose_name=_lazy(u'Make my email public'))
    avatar = models.ImageField(upload_to=settings.USER_AVATAR_PATH,
                               null=True,
                               blank=True,
                               verbose_name=_lazy(u'Avatar'),
                               max_length=settings.MAX_FILEPATH_LENGTH)
    bio = models.TextField(null=True,
                           blank=True,
                           verbose_name=_lazy(u'Biography'))
    website = models.URLField(max_length=255,
                              null=True,
                              blank=True,
                              verbose_name=_lazy(u'Website'))
    twitter = models.URLField(max_length=255,
                              null=True,
                              blank=True,
                              verbose_name=_lazy(u'Twitter URL'))
    facebook = models.URLField(max_length=255,
                               null=True,
                               blank=True,
                               verbose_name=_lazy(u'Facebook URL'))
    irc_handle = models.CharField(max_length=255,
                                  null=True,
                                  blank=True,
                                  verbose_name=_lazy(u'IRC nickname'))
    timezone = TimeZoneField(null=True,
                             blank=True,
                             verbose_name=_lazy(u'Timezone'))
    country = models.CharField(max_length=2,
                               choices=COUNTRIES,
                               null=True,
                               blank=True,
                               verbose_name=_lazy(u'Country'))
    # No city validation
    city = models.CharField(max_length=255,
                            null=True,
                            blank=True,
                            verbose_name=_lazy(u'City'))
    livechat_id = models.CharField(default=None,
                                   null=True,
                                   blank=True,
                                   max_length=255,
                                   verbose_name=_lazy(u'Livechat ID'))

    def __unicode__(self):
        return unicode(self.user)