def get_language_from_request(self, request): supported = dict(settings.LANGUAGES) accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '') for accept_lang, unused in parse_accept_lang_header(accept): if accept_lang == '*': break # We have a very restricted form for our language files # (no encoding specifier, since they all must be UTF-8 and # only one possible language each time. So we avoid the # overhead of gettext.find() and work out the MO file # manually. # 'normalized' is the root name of the locale in POSIX # format (which is the format used for the directories # holding the MO files). normalized = locale.locale_alias.get(to_locale(accept_lang, True)) if not normalized: continue # Remove the default encoding from locale_alias. normalized = normalized.split('.')[0] for lang_code in (accept_lang, accept_lang.split('-')[0]): lang_code = lang_code.lower() if lang_code in supported and check_for_language(lang_code): return lang_code return None
def add_device_locale(device=None): global device_translations from django.conf import settings if not device or not settings.USE_I18N: return current_translation = trans._active[currentThread()] lang = current_translation.language() key = "%s#%s" % (lang, device.id) loc = trans.to_locale(lang) klass = trans.DjangoTranslation if sys.version_info < (2, 4): klass = trans.DjangoTranslation23 device_trans = device_translations.get(key, None) if device_trans is None: translations_paths = get_translations_paths(device) for translations_path in translations_paths: try: t = gettext_module.translation('django', translations_path, [loc], klass) t.set_language(lang) if device_trans is None: device_trans = t else: device_trans.merge(t) except IOError, e: pass device_translations[key] = device_trans fallback = getattr(device_trans, '_fallback', None) if not fallback and hasattr(device_trans, 'add_fallback'): device_trans.add_fallback(current_translation)
class AppFormDetails(AddonFormBase): LOCALES = [(translation.to_locale(k).replace('_', '-'), v) for k, v in do_dictsort(settings.LANGUAGES)] default_locale = forms.TypedChoiceField(required=False, choices=LOCALES) homepage = TransField.adapt(forms.URLField)(required=False) privacy_policy = TransField( widget=TransTextarea(), required=True, label=_lazy(u"Please specify your app's Privacy Policy")) class Meta: model = Webapp fields = ('default_locale', 'homepage', 'privacy_policy') def clean(self): # Make sure we have the required translations in the new locale. required = ['name', 'description'] data = self.cleaned_data if not self.errors and 'default_locale' in self.changed_data: fields = dict((k, getattr(self.instance, k + '_id')) for k in required) locale = data['default_locale'] ids = filter(None, fields.values()) qs = (Translation.objects.filter(locale=locale, id__in=ids, localized_string__isnull=False) .values_list('id', flat=True)) missing = [k for k, v in fields.items() if v not in qs] if missing: raise forms.ValidationError( _('Before changing your default locale you must have a ' 'name and description in that locale. ' 'You are missing %s.') % ', '.join(map(repr, missing))) return data
def is_edit_mode(self, request): from django.utils.translation.trans_real import get_language, to_locale locale = to_locale(get_language()) if 'en' in locale or request.path.find('/admin') > -1: return False #return request.GET.get('translate', 'False').lower() == 'true' and request.user.is_staff return request.user.is_staff and getattr(settings, 'ROSETTA_INPAGE', False)
def _fetch(lang, fallback=None): global _translations res = _translations.get(lang, None) if res is not None: return res loc = to_locale(lang) def _translation(path): try: if needs_compilation('django', path, lang): compile_messages('django', path, lang) t = gettext_module.translation('django', path, [loc], DjangoTranslation) t.set_language(lang) return t except IOError: return None res = _translation(globalpath) # We want to ensure that, for example, "en-gb" and "en-us" don't share # the same translation object (thus, merging en-us with a local update # doesn't affect en-gb), even though they will both use the core "en" # translation. So we have to subvert Python's internal gettext caching. base_lang = lambda x: x.split('-', 1)[0] if base_lang(lang) in [base_lang(trans) for trans in list(_translations)]: res._info = res._info.copy() res._catalog = res._catalog.copy() def _merge(path): t = _translation(path) if t is not None: if res is None: return t else: res.merge(t) return res for appname in reversed(settings.INSTALLED_APPS): app = import_module(appname) apppath = os.path.join(os.path.dirname(upath(app.__file__)), 'locale') if os.path.isdir(apppath): res = _merge(apppath) for localepath in reversed(settings.LOCALE_PATHS): if os.path.isdir(localepath): res = _merge(localepath) if res is None: if fallback is not None: res = fallback else: return gettext_module.NullTranslations() _translations[lang] = res return res
def post(self, request): from django.utils.translation.trans_real import to_locale, get_language from rosetta_inpage.utils import save_message source = request.POST.get('source', '') target_lang = request.POST.get('lang', '') target_msg = request.POST.get('msg', '') if target_lang: locale = to_locale(target_lang) else: locale = to_locale(get_language()) files = save_message(source, target_msg, locale) return { 'files': files, 'msg': target_msg, }
def extension_detail(request, addon): """Extensions details page.""" # if current version is incompatible with this app, redirect comp_apps = addon.compatible_apps if comp_apps and request.APP not in comp_apps: prefixer = urlresolvers.get_url_prefix() prefixer.app = comp_apps.keys()[0].short return http.HttpResponsePermanentRedirect(reverse( 'addons.detail', args=[addon.id])) # source tracking src = request.GET.get('src', 'addondetail') # get satisfaction only supports en-US lang = translation.to_locale(translation.get_language()) addon.has_satisfaction = (lang == 'en_US' and addon.get_satisfaction_company) # other add-ons from the same author(s) author_addons = order_by_translation(addon.authors_other_addons, 'name') # tags dev_tags, user_tags = addon.tags_partitioned_by_developer current_user_tags = [] if request.user.is_authenticated(): current_user_tags = user_tags.filter( addon_tags__user=request.amo_user) # addon recommendations recommended = Addon.objects.valid().only_translations().filter( recommended_for__addon=addon)[:5] # popular collections this addon is part of collections = Collection.objects.listed().filter( addons=addon, application__id=request.APP.id) data = { 'addon': addon, 'author_addons': author_addons, 'src': src, 'dev_tags': dev_tags, 'user_tags': user_tags, 'current_user_tags': current_user_tags, 'recommendations': recommended, 'review_form': ReviewForm(), 'reviews': Review.objects.latest().filter(addon=addon), 'get_replies': Review.get_replies, 'collections': collections.order_by('-subscribers')[:3], } return jingo.render(request, 'addons/details.html', data)
def post(self, request): from django.utils.translation.trans_real import to_locale, get_language from rosetta_inpage.utils import save_message source = request.POST.get('source', '') target_lang = request.POST.get('lang', '') target_msg = request.POST.get('msg', '') if target_lang: locale = to_locale(target_lang) else: locale = to_locale(get_language()) files = save_message(source, target_msg, locale) return { 'files': files, 'msg': target_msg, }
def extension_detail(request, addon): """Extensions details page.""" # If current version is incompatible with this app, redirect. comp_apps = addon.compatible_apps if comp_apps and request.APP not in comp_apps: prefixer = urlresolvers.get_url_prefix() prefixer.app = comp_apps.keys()[0].short return redirect('addons.detail', addon.slug, permanent=True) # get satisfaction only supports en-US. lang = translation.to_locale(translation.get_language()) addon.has_satisfaction = (lang == 'en_US' and addon.get_satisfaction_company) # Addon recommendations. recommended = Addon.objects.listed(request.APP).filter( recommended_for__addon=addon)[:6] # Popular collections this addon is part of. collections = Collection.objects.listed().filter( addons=addon, application__id=request.APP.id) ctx = { 'addon': addon, 'src': request.GET.get('src', 'dp-btn-primary'), 'version_src': request.GET.get('src', 'dp-btn-version'), 'tags': addon.tags.not_blacklisted(), 'grouped_ratings': GroupedRating.get(addon.id), 'recommendations': recommended, 'review_form': ReviewForm(), 'reviews': Review.objects.latest().filter(addon=addon), 'get_replies': Review.get_replies, 'collections': collections.order_by('-subscribers')[:3], 'abuse_form': AbuseForm(request=request), } # details.html just returns the top half of the page for speed. The bottom # does a lot more queries we don't want on the initial page load. if request.is_ajax(): # Other add-ons/apps from the same author(s). if addon.is_webapp(): others = Webapp.objects.listed().filter(type=amo.ADDON_WEBAPP) else: others = (Addon.objects.listed(request.APP) .exclude(type=amo.ADDON_WEBAPP)) others = (others.exclude(id=addon.id).distinct() .filter(addonuser__listed=True, authors__in=addon.listed_authors)) ctx['author_addons'] = others[:6] return jingo.render(request, 'addons/impala/details-more.html', ctx) else: if addon.is_webapp(): ctx['search_placeholder'] = 'apps' return jingo.render(request, 'addons/impala/details.html', ctx)
def extension_detail(request, addon): """Extensions details page.""" # If current version is incompatible with this app, redirect. comp_apps = addon.compatible_apps if comp_apps and request.APP not in comp_apps: prefixer = urlresolvers.get_url_prefix() prefixer.app = comp_apps.keys()[0].short return redirect('addons.detail', addon.slug, permanent=True) # get satisfaction only supports en-US. lang = translation.to_locale(translation.get_language()) addon.has_satisfaction = (lang == 'en_US' and addon.get_satisfaction_company) # Addon recommendations. recommended = Addon.objects.listed(request.APP).filter( recommended_for__addon=addon)[:6] # Popular collections this addon is part of. collections = Collection.objects.listed().filter( addons=addon, application__id=request.APP.id) ctx = { 'addon': addon, 'src': request.GET.get('src', 'dp-btn-primary'), 'version_src': request.GET.get('src', 'dp-btn-version'), 'tags': addon.tags.not_blacklisted(), 'grouped_ratings': GroupedRating.get(addon.id), 'recommendations': recommended, 'review_form': ReviewForm(), 'reviews': Review.objects.latest().filter(addon=addon), 'get_replies': Review.get_replies, 'collections': collections.order_by('-subscribers')[:3], 'abuse_form': AbuseForm(request=request), } # details.html just returns the top half of the page for speed. The bottom # does a lot more queries we don't want on the initial page load. if request.is_ajax(): # Other add-ons/apps from the same author(s). if addon.is_webapp(): others = Webapp.objects.listed().filter(type=amo.ADDON_WEBAPP) else: others = (Addon.objects.listed(request.APP) .exclude(type=amo.ADDON_WEBAPP)) others = (others.exclude(id=addon.id).distinct() .filter(addonuser__listed=True, authors__in=addon.listed_authors)) ctx['author_addons'] = others[:6] return jingo.render(request, 'addons/impala/details-more.html', ctx) else: if addon.is_webapp(): ctx['search_placeholder'] = 'apps' return jingo.render(request, 'addons/impala/details.html', ctx)
def impala_extension_detail(request, addon): """Extensions details page.""" # if current version is incompatible with this app, redirect comp_apps = addon.compatible_apps if comp_apps and request.APP not in comp_apps: prefixer = urlresolvers.get_url_prefix() prefixer.app = comp_apps.keys()[0].short return http.HttpResponsePermanentRedirect(reverse( 'addons.detail', args=[addon.slug])) # source tracking src = request.GET.get('src', 'addon-detail') # get satisfaction only supports en-US lang = translation.to_locale(translation.get_language()) addon.has_satisfaction = (lang == 'en_US' and addon.get_satisfaction_company) # other add-ons from the same author(s) author_addons = order_by_translation(addon.authors_other_addons, 'name')[:6] # tags tags = addon.tags.not_blacklisted() # addon recommendations recommended = MiniAddon.objects.valid().filter( recommended_for__addon=addon)[:5] # popular collections this addon is part of collections = Collection.objects.listed().filter( addons=addon, application__id=request.APP.id) data = { 'addon': addon, 'author_addons': author_addons, 'src': src, 'tags': tags, 'grouped_ratings': GroupedRating.get(addon.id), 'recommendations': recommended, 'review_form': ReviewForm(), 'reviews': Review.objects.latest().filter(addon=addon), 'get_replies': Review.get_replies, 'collections': collections.order_by('-subscribers')[:3], } if settings.REPORT_ABUSE: data['abuse_form'] = AbuseForm(request=request) return jingo.render(request, 'addons/impala/details.html', data)
def format_datetime_local(datetime, format="medium"): """Format the output of a datetime object to a localized string""" from babel.dates import format_datetime from django.conf import settings from django.utils.translation.trans_real import to_locale # See http://babel.pocoo.org/docs/api/dates/#date-and-time-formatting # for customizing the output format. try: result = format_datetime(datetime, format, locale=to_locale(settings.LANGUAGE_CODE)) except ValueError: result = format_datetime(datetime, format, locale="en_US") return result
def impala_extension_detail(request, addon): """Extensions details page.""" # if current version is incompatible with this app, redirect comp_apps = addon.compatible_apps if comp_apps and request.APP not in comp_apps: prefixer = urlresolvers.get_url_prefix() prefixer.app = comp_apps.keys()[0].short return http.HttpResponsePermanentRedirect( reverse('addons.detail', args=[addon.slug])) # source tracking src = request.GET.get('src', 'addon-detail') # get satisfaction only supports en-US lang = translation.to_locale(translation.get_language()) addon.has_satisfaction = (lang == 'en_US' and addon.get_satisfaction_company) # other add-ons from the same author(s) author_addons = order_by_translation(addon.authors_other_addons, 'name')[:6] # tags tags = addon.tags.not_blacklisted() # addon recommendations recommended = MiniAddon.objects.valid().filter( recommended_for__addon=addon)[:5] # popular collections this addon is part of collections = Collection.objects.listed().filter( addons=addon, application__id=request.APP.id) data = { 'addon': addon, 'author_addons': author_addons, 'src': src, 'tags': tags, 'grouped_ratings': GroupedRating.get(addon.id), 'recommendations': recommended, 'review_form': ReviewForm(), 'reviews': Review.objects.latest().filter(addon=addon), 'get_replies': Review.get_replies, 'collections': collections.order_by('-subscribers')[:3], } if settings.REPORT_ABUSE: data['abuse_form'] = AbuseForm(request=request) return jingo.render(request, 'addons/impala/details.html', data)
def _activate(locale): # XXX TODO: When it comes time to load .mo files on the fly and merge # them, this is the place to do it. We'll also need to implement our own # caching since the _translations stuff is built on a per locale basis, # not per locale + some key locale = django_trans.to_locale(locale) # Django caches the translation objects here t = django_trans._translations.get(locale, None) if t is not None: return t # Django's activate() simply calls translation() and adds it to a global. # We'll do the same here, first calling django's translation() so it can # do everything it needs to do, and then calling gettext directly to # load the rest. We make a deepcopy because Django will return the en-US # catalog if it doesn't have a locale (but we do). We don't want to merge # our foreign catalog into en-US. Since Django stuck the en-US catalog # into its cache for this locale, we have to update that too. t = copy.deepcopy(django_trans.translation(locale)) t.set_language(locale) try: # When trying to load css, js, and images through the Django server # gettext() throws an exception saying it can't find the .mo files. I # suspect this has something to do with Django trying not to load # extra stuff for requests that won't need it. I do know that I don't # want to try to debug it. This is what Django does in their function # also. # # We check for SETTINGS_MODULE here because if it's not here, then # it's possible we're in a test using override_settings and we don't # want to flip out. settings_module = getattr(settings, 'SETTINGS_MODULE', None) if settings_module: # If you've got extra .mo files to load, this is the place. path = import_module(settings_module).path domain = getattr(settings, 'TEXT_DOMAIN', 'messages') bonus = gettext.translation(domain, path('locale'), [locale], django_trans.DjangoTranslation) t.merge(bonus) # Overwrite t (defaults to en-US) with our real locale's plural form t.plural = bonus.plural except IOError: pass django_trans._translations[locale] = t return t
def _activate(locale): # XXX TODO: When it comes time to load .mo files on the fly and merge # them, this is the place to do it. We'll also need to implement our own # caching since the _translations stuff is built on a per locale basis, # not per locale + some key locale = django_trans.to_locale(locale) # Django caches the translation objects here t = django_trans._translations.get(locale, None) if t is not None: return t # Django's activate() simply calls translation() and adds it to a global. # We'll do the same here, first calling django's translation() so it can # do everything it needs to do, and then calling gettext directly to # load the rest. We make a deepcopy because Django will return the en-US # catalog if it doesn't have a locale (but we do). We don't want to merge # our foreign catalog into en-US. Since Django stuck the en-US catalog # into its cache for this locale, we have to update that too. t = copy.deepcopy(django_trans.translation(locale)) t.set_language(locale) try: # When trying to load css, js, and images through the Django server # gettext() throws an exception saying it can't find the .mo files. I # suspect this has something to do with Django trying not to load # extra stuff for requests that won't need it. I do know that I don't # want to try to debug it. This is what Django does in their function # also. # # We check for SETTINGS_MODULE here because if it's not here, then # it's possible we're in a test using override_settings and we don't # want to flip out. settings_module = getattr(settings, 'SETTINGS_MODULE', None) if settings_module: # If you've got extra .mo files to load, this is the place. path = import_module(settings_module).path domain = getattr(settings, 'TEXT_DOMAIN', 'messages') bonus = gettext.translation(domain, path('locale'), [locale], django_trans.DjangoTranslation) t.merge(bonus) # Overwrite t (defaults to en-US) with our real locale's plural form t.plural = bonus.plural except IOError: pass django_trans._translations[locale] = t return t
def format_datetime_local(datetime, format='medium'): """Format the output of a datetime object to a localized string""" from babel.dates import format_datetime from django.conf import settings from django.utils.translation.trans_real import to_locale # See http://babel.pocoo.org/docs/api/dates/#date-and-time-formatting # for customizing the output format. try: locale = to_locale(settings.LANGUAGE_CODE) except ValueError: locale = "en_US" result = format_datetime(datetime, format, locale=locale) return result
def extension_detail(request, addon): """Extensions details page.""" # If current version is incompatible with this app, redirect. comp_apps = addon.compatible_apps if comp_apps and request.APP not in comp_apps: prefixer = urlresolvers.get_url_prefix() prefixer.app = comp_apps.keys()[0].short return redirect("addons.detail", addon.slug, permanent=True) # get satisfaction only supports en-US. lang = translation.to_locale(translation.get_language()) addon.has_satisfaction = lang == "en_US" and addon.get_satisfaction_company # Addon recommendations. recommended = Addon.objects.listed(request.APP).filter(recommended_for__addon=addon)[:6] # Popular collections this addon is part of. collections = Collection.objects.listed().filter(addons=addon, application__id=request.APP.id) ctx = { "addon": addon, "src": request.GET.get("src", "dp-btn-primary"), "version_src": request.GET.get("src", "dp-btn-version"), "tags": addon.tags.not_blacklisted(), "grouped_ratings": GroupedRating.get(addon.id), "recommendations": recommended, "review_form": ReviewForm(), "reviews": Review.objects.latest().filter(addon=addon), "get_replies": Review.get_replies, "collections": collections.order_by("-subscribers")[:3], "abuse_form": AbuseForm(request=request), } # details.html just returns the top half of the page for speed. The bottom # does a lot more queries we don't want on the initial page load. if request.is_ajax(): # Other add-ons/apps from the same author(s). if addon.is_webapp(): others = Webapp.objects.listed().filter(type=amo.ADDON_WEBAPP) else: others = Addon.objects.listed(request.APP).exclude(type=amo.ADDON_WEBAPP) others = others.exclude(id=addon.id).distinct().filter(addonuser__listed=True, authors__in=addon.listed_authors) ctx["author_addons"] = others[:6] return jingo.render(request, "addons/impala/details-more.html", ctx) else: if addon.is_webapp(): ctx["search_cat"] = "apps" return jingo.render(request, "addons/impala/details.html", ctx)
def setup_mofile_with_entry(self, poentry, language='en'): locale_location = join(settings.LOCALE_DIR, 'locale', language, 'LC_MESSAGES') pofile = polib.pofile(join(locale_location, 'django.po')) pofile.append(poentry) pofile.save() pofile.save_as_mofile(join(locale_location, 'django.mo')) jit_locale = gettext_module.translation( 'django', join(settings.LOCALE_DIR, 'locale'), [trans_real.to_locale(language)], trans_real.DjangoTranslation) jit_locale.set_language(language) locale = trans_real.translation(language) locale.merge(jit_locale)
def process_request(self, request): """ :param request: :return: """ if self.is_edit_mode(request): logger.info('Editing mode enabled') from django.utils.translation.trans_real import get_language, to_locale locale = to_locale(get_language()) setattr(THREAD_LOCAL_STORAGE, EDIT_MODE, True) setattr(THREAD_LOCAL_STORAGE, MESSAGES, set()) setattr(THREAD_LOCAL_STORAGE, REQUEST, request) setattr(THREAD_LOCAL_STORAGE, REQUEST_LOCALE, locale) else: setattr(THREAD_LOCAL_STORAGE, EDIT_MODE, False) return None
class AddonBase(OnChangeMixin, ModelBase): STATUS_CHOICES = base.STATUS_CHOICES.items() LOCALES = [(translation.to_locale(k).replace('_', '-'), v) for k, v in do_dictsort(settings.LANGUAGES)] guid = models.CharField(max_length=255, unique=True, null=True) slug = models.CharField(max_length=30, unique=True, null=True) # This column is only used for webapps, so they can have a slug namespace # separate from addons and personas. app_slug = models.CharField(max_length=30, unique=True, null=True) name = TranslatedField() default_locale = models.CharField(max_length=10, default=settings.LANGUAGE_CODE, db_column='defaultlocale') type = models.PositiveIntegerField(db_column='addontype_id') status = models.PositiveIntegerField( choices=STATUS_CHOICES, db_index=True, default=0) highest_status = models.PositiveIntegerField( choices=STATUS_CHOICES, default=0, help_text="An upper limit for what an author can change.", db_column='higheststatus') icon_type = models.CharField(max_length=25, blank=True, db_column='icontype') homepage = TranslatedField() support_email = TranslatedField(db_column='supportemail') support_url = TranslatedField(db_column='supporturl') description = PurifiedField(short=False) summary = LinkifiedField() developer_comments = PurifiedField(db_column='developercomments') eula = PurifiedField() privacy_policy = PurifiedField(db_column='privacypolicy') the_reason = PurifiedField() the_future = PurifiedField() average_rating = models.FloatField(max_length=255, default=0, null=True, db_column='averagerating') bayesian_rating = models.FloatField(default=0, db_index=True, db_column='bayesianrating') total_reviews = models.PositiveIntegerField(default=0, db_column='totalreviews') weekly_downloads = models.PositiveIntegerField( default=0, db_column='weeklydownloads', db_index=True) total_downloads = models.PositiveIntegerField( default=0, db_column='totaldownloads') hotness = models.FloatField(default=0, db_index=True) average_daily_downloads = models.PositiveIntegerField(default=0) average_daily_users = models.PositiveIntegerField(default=0) share_count = models.PositiveIntegerField(default=0, db_index=True, db_column='sharecount') last_updated = models.DateTimeField(db_index=True, null=True, help_text='Last time this add-on had a file/version update') ts_slowness = models.FloatField(db_index=True, null=True, help_text='How much slower this add-on makes browser ts tests. ' 'Read as {addon.ts_slowness}% slower.') disabled_by_user = models.BooleanField(default=False, db_index=True, db_column='inactive') trusted = models.BooleanField(default=False) view_source = models.BooleanField(default=True, db_column='viewsource') public_stats = models.BooleanField(default=False, db_column='publicstats') prerelease = models.BooleanField(default=False) admin_review = models.BooleanField(default=False, db_column='adminreview') admin_review_type = models.PositiveIntegerField( choices=base.ADMIN_REVIEW_TYPES.items(), default=base.ADMIN_REVIEW_FULL) site_specific = models.BooleanField(default=False, db_column='sitespecific') external_software = models.BooleanField(default=False, db_column='externalsoftware') dev_agreement = models.BooleanField(default=False, help_text="Has the dev agreement been signed?") auto_repackage = models.BooleanField(default=True, help_text='Automatically upgrade jetpack add-on to a new sdk version?') outstanding = models.BooleanField(default=False) nomination_message = models.TextField(null=True, db_column='nominationmessage') target_locale = models.CharField( max_length=255, db_index=True, blank=True, null=True, help_text="For dictionaries and language packs") locale_disambiguation = models.CharField( max_length=255, blank=True, null=True, help_text="For dictionaries and language packs") wants_contributions = models.BooleanField(default=False) paypal_id = models.CharField(max_length=255, blank=True) charity = models.ForeignKey('Charity', null=True) # TODO(jbalogh): remove nullify_invalid once remora dies. suggested_amount = DecimalCharField( max_digits=8, decimal_places=2, nullify_invalid=True, blank=True, null=True, help_text=_(u'Users have the option of contributing more ' 'or less than this amount.')) total_contributions = DecimalCharField(max_digits=8, decimal_places=2, nullify_invalid=True, blank=True, null=True) annoying = models.PositiveIntegerField( choices=base.CONTRIB_CHOICES, default=0, help_text=_(u"Users will always be asked in the Add-ons" " Manager (Firefox 4 and above)")) enable_thankyou = models.BooleanField(default=False, help_text="Should the thankyou note be sent to contributors?") thankyou_note = TranslatedField() get_satisfaction_company = models.CharField(max_length=255, blank=True, null=True) get_satisfaction_product = models.CharField(max_length=255, blank=True, null=True) authors = models.ManyToManyField(UserProfileBase, through='AddonUser', related_name='addons') categories = models.ManyToManyField('Category', through='AddonCategoryBase') dependencies = models.ManyToManyField('self', symmetrical=False, through='AddonDependency', related_name='addons') premium_type = models.PositiveIntegerField( choices=base.ADDON_PREMIUM_TYPES.items(), default=base.ADDON_FREE) manifest_url = models.URLField(max_length=255, blank=True, null=True, verify_exists=False) app_domain = models.CharField(max_length=255, blank=True, null=True, db_index=True) _current_version = models.ForeignKey(VersionBase, related_name='___ignore', db_column='current_version', null=True, on_delete=models.SET_NULL) # This is for Firefox only. _backup_version = models.ForeignKey(VersionBase, related_name='___backup', db_column='backup_version', null=True, on_delete=models.SET_NULL) _latest_version = None make_public = models.DateTimeField(null=True) mozilla_contact = models.EmailField() # Whether the app is packaged or not (aka hosted). is_packaged = models.BooleanField(default=False, db_index=True) # This gets overwritten in the transformer. share_counts = collections.defaultdict(int) class Meta: db_table = 'addons' app_label = 'addons' @staticmethod def __new__(cls, *args, **kw): # # Return a Webapp instead of an Addon if the `type` column says this is # # really a webapp. # try: # type_idx = AddonBase._meta._type_idx # except AttributeError: # type_idx = (idx for idx, f in enumerate(AddonBase._meta.fields) # if f.attname == 'type').next() # AddonBase._meta._type_idx = type_idx # if ((len(args) == len(AddonBase._meta.fields) # and args[type_idx] == base.ADDON_WEBAPP) # or kw and kw.get('type') == base.ADDON_WEBAPP): # from gelato.models.webapp import Webapp # cls = Webapp return super(AddonBase, cls).__new__(cls, *args, **kw) def __unicode__(self): return u'%s: %s' % (self.id, self.name) def __init__(self, *args, **kw): super(AddonBase, self).__init__(*args, **kw) self._first_category = {} @property def premium(self): """ Returns the premium object which will be gotten by the transformer, if its not there, try and get it. Will return None if there's nothing there. """ if not hasattr(self, '_premium'): try: self._premium = self.addonpremium except AddonPremium.DoesNotExist: self._premium = None return self._premium
def extension_detail(request, addon): """Extensions details page.""" # if current version is incompatible with this app, redirect comp_apps = addon.compatible_apps if comp_apps and request.APP not in comp_apps: prefixer = urlresolvers.get_url_prefix() prefixer.app = comp_apps.keys()[0].short return http.HttpResponsePermanentRedirect(reverse( 'addons.detail', args=[addon.id])) # source tracking src = request.GET.get('src', 'addondetail') # get satisfaction only supports en-US lang = translation.to_locale(translation.get_language()) addon.has_satisfaction = (lang == 'en_US' and addon.get_satisfaction_company) # other add-ons from the same author(s) author_addons = (Addon.objects.valid().only_translations() .exclude(id=addon.id) .filter(addonuser__listed=True, authors__in=addon.listed_authors).distinct()) # tags (dev_tags, user_tags) = addon.tags_partitioned_by_developer current_user_tags = [] if request.user.is_authenticated(): current_user_tags = user_tags.filter( addon_tags__user=request.amo_user) # addon recommendations recommended = Addon.objects.valid().only_translations().filter( recommended_for__addon=addon)[:5] # popular collections this addon is part of coll_show_count = 3 collections = Collection.objects.listed().filter( addons=addon, application__id=request.APP.id) other_coll_count = max(0, collections.count() - coll_show_count) popular_coll = collections.order_by('-subscribers')[:coll_show_count] # this user's collections user_collections = _details_collections_dropdown(request, addon) data = { 'addon': addon, 'author_addons': author_addons, 'src': src, 'dev_tags': dev_tags, 'user_tags': user_tags, 'current_user_tags': current_user_tags, 'recommendations': recommended, 'collections': popular_coll, 'other_collection_count': other_coll_count, 'user_collections': user_collections, } return jingo.render(request, 'addons/details.html', data)
def messages_viewer(list_messages, view_locale=None): """ Use the original translate function instead of the patched one """ from django.utils.translation.trans_real import get_language, to_locale from rosetta_inpage.patches import original as _ # The original results = [] locale = to_locale(get_language()) catalog = utils.get_locale_catalog(locale) translated_count = 0 def create(msgid): entry = catalog.dict.get(msgid, None) # is_valid_translation = True if entry and entry.translated() else False # is_valid_translation = True if entry and entry.msgstr is not u"" or None \ # and not entry.obsolete else False is_valid_translation = utils.is_translated(entry) # if translated: # print "\n\n", str(is_valid_translation), ", " # print "Test= ", msg, translated, "==", encode(translated.msgstr), \ # ", file=", str(translated.pfile), "obs=", str(translated.obsolete), "\n" if is_valid_translation: msg_target = entry.msgstr else: msg_target = _(msgid) if not is_valid_translation: css_class = 'rosetta-inpage-todo' elif entry and 'fuzzy' in entry.flags: css_class = 'rosetta-inpage-fuzzy' else: css_class = 'rosetta-inpage-foo' return { 'show': show_message(msgid, view_locale), 'hash': rosetta_inpage.hash_text(msgid), 'source': mark_safe(msg), # the source message 'msg': mark_safe(msg_target), # the translated message 'translated': is_valid_translation, 'fuzzy': False if entry and 'fuzzy' not in entry.flags else True, 'css_class': css_class } def show_message(msgid, view_locale=None): if view_locale: poentry = utils.get_message(msgid, view_locale) if poentry and poentry.msgstr: return utils.encode(poentry.msgstr) return utils.encode(msgid) for msg in list_messages: item = create(msg) results.append(item) if item.get('translated', False): translated_count += 1 sorted_results = sorted(results, key=lambda entry: entry['show']) return sorted_results, translated_count
class InAppProductSerializer(serializers.ModelSerializer): _locales = [(translation.to_locale(k).replace('_', '-').lower(), v) for k, v in do_dictsort(settings.LANGUAGES)] app = serializers.SlugRelatedField(read_only=True, slug_field='app_slug', source='webapp') guid = serializers.CharField(read_only=True) include_inactive = serializers.BooleanField(read_only=True) logo_url = serializers.CharField( validators=[URLValidator(schemes=['http', 'https'])], required=False) name = NameField() default_locale = serializers.ChoiceField(choices=_locales) price_id = serializers.PrimaryKeyRelatedField(source='price', queryset=Price.objects) class Meta: model = InAppProduct fields = ['active', 'guid', 'app', 'price_id', 'name', 'default_locale', 'logo_url', 'include_inactive'] def validate(self, attrs): default_name = attrs['name'].get(attrs['default_locale'], None) if ((attrs['default_locale'] not in attrs['name']) or not default_name): raise ValidationError( 'no localization for default_locale {d} in "name"' .format(d=repr(attrs['default_locale']))) return attrs def validate_logo_url(self, logo_url): # This message is shown for all image errors even though it may # not be correct. This is to prevent leaking info that could # lead to port scanning, DOS'ing or other vulnerabilities. msg = _('Product logo must be a 64x64 image. ' 'Check that the URL is correct.') tmp_dest = StringIO() try: res = requests.get( logo_url, timeout=3, headers={'User-Agent': settings.MARKETPLACE_USER_AGENT}) res.raise_for_status() payload = 0 read_size = 100000 for chunk in res.iter_content(read_size): payload += len(chunk) if payload > settings.MAX_INAPP_IMAGE_SIZE: log.info('clean_logo_url: payload exceeded allowed ' 'size: {url}: '.format(url=logo_url)) raise ValidationError(msg) tmp_dest.write(chunk) except ValidationError: raise except Exception, exc: log.info('clean_logo_url: exception fetching {url}: ' '{exc.__class__.__name__}: {exc}' .format(url=logo_url, exc=exc)) raise ValidationError(msg) tmp_dest.seek(0) try: img = Image.open(tmp_dest) img.verify() except Exception, exc: log.info('clean_logo_url: Error loading/verifying {url}: ' '{exc.__class__.__name__}: {exc}' .format(url=logo_url, exc=exc)) raise ValidationError(msg)
def get_settings(conf_file_leafname, election_app=None, tests=False): conf = get_conf(conf_file_leafname) debug = bool(int(conf.get('STAGING'))) # Get the requested ELECTION_APP: if election_app is None: election_app = conf['ELECTION_APP'] election_app_fully_qualified = 'elections.' + election_app election_settings_module = election_app_fully_qualified + '.settings' elections_module = importlib.import_module(election_settings_module) language_code = conf.get('LANGUAGE_CODE', 'en-gb') # Internationalization # https://docs.djangoproject.com/en/1.6/topics/i18n/ locale_paths = [ join(BASE_DIR, 'locale') ] # The code below sets LANGUAGES to only those we have translations # for, so at the time of writing that will be: # [('en', 'English'), ('es-ar', 'Argentinian Spanish')] # whereas the default setting is a long list of languages which # includes: # ('es', 'Spanish'). # If someone's browser sends 'Accept-Language: es', that means that it # will be found in this list, but since there are no translations for 'es' # it'll fall back to LANGUAGE_CODE. However, if there is no 'es' in # LANGUAGES, then Django will attempt to do a best match, so if # Accept-Language is 'es' then it will use the 'es-ar' translation. We think # this is generally desirable (e.g. so someone can see YourNextMP in Spanish # if their browser asks for Spanish). languages = [ l for l in LANGUAGES if exists(join(locale_paths[0], to_locale(l[0]))) ] languages.append(('cy-gb', 'Welsh')) languages.append(('es-cr', 'Costa Rican Spanish')) # The language selection has been slightly complicated now that we # have two es- languages: es-ar and es-cr. Chrome doesn't offer # Costa Rican Spanish as one of its language choices, so the best # you can do is choose 'Spanish - español'. (This might well be # the case in other browsers too.) Since 'es-ar' comes first in # 'languages' after the preceding code, this means that someone # viewing the Costa Rica site with Chrome's preferred language set # to Spanish (i.e. with 'es' first in Accept-Language) will get # the Argentinian Spanish translations instead of Costa Rican # Spanish. To get around this, look for the default language code # for the site, and if that's present, move it to the front of # 'languages'. This should be generally helpful behaviour: the # default language code of the site should take precedence over # another language that happens to match based on the generic part # of the language code. language_code_index = next( (i for i, l in enumerate(languages) if l[0] == language_code), None ) if language_code_index is not None: languages.insert(0, languages.pop(language_code_index)) # Make sure the MEDIA_ROOT directory actually exists: media_root = conf.get('MEDIA_ROOT') or join(BASE_DIR, 'media') # Make sure that the MEDIA_ROOT and subdirectory for archived CSV # files exist: mkdir_p(join(media_root, 'csv-archives')) # Database # https://docs.djangoproject.com/en/1.6/ref/settings/#databases if conf.get('DATABASE_SYSTEM') == 'postgresql': databases = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': conf.get('YNMP_DB_NAME'), 'USER': conf.get('YNMP_DB_USER'), 'PASSWORD': conf.get('YNMP_DB_PASS'), 'HOST': conf.get('YNMP_DB_HOST'), 'PORT': conf.get('YNMP_DB_PORT'), } } else: databases = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': join(BASE_DIR, 'db.sqlite3'), } } # Setup caches depending on DEBUG: if debug: cache = {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'} cache_thumbnails = {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'} else: cache = { 'TIMEOUT': None, # cache keys never expire; we invalidate them 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', 'KEY_PREFIX': databases['default']['NAME'], } cache_thumbnails = { 'TIMEOUT': 60 * 60 * 24 * 2, # expire after two days 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', 'KEY_PREFIX': databases['default']['NAME'] + "-thumbnails", } # Create a dictionary with these settings and other simpler ones: result = { 'BASE_DIR': BASE_DIR, 'ALLOWED_HOSTS': conf.get('ALLOWED_HOSTS'), 'DEBUG': debug, 'RUNNING_TESTS': tests, # Google analytics settings: 'GOOGLE_ANALYTICS_ACCOUNT': conf.get('GOOGLE_ANALYTICS_ACCOUNT'), 'USE_UNIVERSAL_ANALYTICS': conf.get('USE_UNIVERSAL_ANALYTICS', True), # The Twitter account referenced in the Twitter card data: 'TWITTER_USERNAME': conf.get('TWITTER_USERNAME', ''), # The email address which is made public on the site for sending # support email to: 'SUPPORT_EMAIL': conf['SUPPORT_EMAIL'], # Email addresses that error emails are sent to when DEBUG = False 'ADMINS': conf['ADMINS'], # The From: address for all emails except error emails 'DEFAULT_FROM_EMAIL': conf['DEFAULT_FROM_EMAIL'], # The From: address for error emails 'SERVER_EMAIL': conf['SERVER_EMAIL'], # SECURITY WARNING: keep the secret key used in production secret! 'SECRET_KEY': conf['SECRET_KEY'], 'TEMPLATE_DEBUG': True, 'TEMPLATE_DIRS': ( join(BASE_DIR, 'mysite', 'templates'), ), 'TEMPLATE_CONTEXT_PROCESSORS': TEMPLATE_CONTEXT_PROCESSORS + ( # Required by allauth template tags "django.core.context_processors.request", # allauth specific context processors "allauth.account.context_processors.account", "allauth.socialaccount.context_processors.socialaccount", "django.contrib.messages.context_processors.messages", "mysite.context_processors.add_settings", "mysite.context_processors.election_date", "mysite.context_processors.add_group_permissions", "mysite.context_processors.add_notification_data", "mysite.context_processors.locale", "mysite.context_processors.add_site", ), 'ELECTION_APP': election_app, 'ELECTION_APP_FULLY_QUALIFIED': election_app_fully_qualified, # The Django applications in use: 'INSTALLED_APPS': ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.humanize', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', 'django_nose', 'django_extensions', 'pipeline', 'statici18n', 'sorl.thumbnail', 'rest_framework', 'rest_framework.authtoken', 'images', 'haystack', 'elections', 'popolo', election_app_fully_qualified, 'candidates', 'tasks', 'alerts', 'cached_counts', 'moderation_queue', 'auth_helpers', 'debug_toolbar', 'template_timings_panel', 'official_documents', 'results', 'notifications', 'allauth', 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.google', 'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.twitter', 'corsheaders', 'crispy_forms', ), 'SITE_ID': 1, 'MIDDLEWARE_CLASSES': ( 'debug_toolbar.middleware.DebugToolbarMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'candidates.middleware.CopyrightAssignmentMiddleware', 'candidates.middleware.DisallowedUpdateMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ), # django-allauth settings: 'AUTHENTICATION_BACKENDS': ( # Needed to login by username in Django admin, regardless of `allauth` "django.contrib.auth.backends.ModelBackend", # `allauth` specific authentication methods, such as login by e-mail "allauth.account.auth_backends.AuthenticationBackend", ), 'SOCIALACCOUNT_PROVIDERS': { 'google': {'SCOPE': ['https://www.googleapis.com/auth/userinfo.profile'], 'AUTH_PARAMS': {'access_type': 'online'}}, 'facebook': {'SCOPE': ['email',]}, }, 'LOGIN_REDIRECT_URL': '/', 'ACCOUNT_EMAIL_VERIFICATION': 'mandatory', 'ACCOUNT_EMAIL_REQUIRED': True, 'ACCOUNT_USERNAME_REQUIRED': True, 'SOCIALACCOUNT_AUTO_SIGNUP': True, 'ROOT_URLCONF': 'mysite.urls', 'WSGI_APPLICATION': 'mysite.wsgi.application', # Django Debug Toolbar settings: 'DEBUG_TOOLBAR_PATCH_SETTINGS': False, 'DEBUG_TOOLBAR_PANELS': [ 'debug_toolbar.panels.versions.VersionsPanel', 'debug_toolbar.panels.timer.TimerPanel', 'debug_toolbar.panels.settings.SettingsPanel', 'debug_toolbar.panels.headers.HeadersPanel', 'debug_toolbar.panels.request.RequestPanel', 'debug_toolbar.panels.sql.SQLPanel', 'debug_toolbar.panels.staticfiles.StaticFilesPanel', 'debug_toolbar.panels.templates.TemplatesPanel', 'debug_toolbar.panels.cache.CachePanel', 'debug_toolbar.panels.signals.SignalsPanel', 'debug_toolbar.panels.logging.LoggingPanel', 'debug_toolbar.panels.redirects.RedirectsPanel', 'template_timings_panel.panels.TemplateTimings.TemplateTimings', ], 'INTERNAL_IPS': ['127.0.0.1'], # Language settings (calculated above): 'LOCALE_PATHS': locale_paths, 'LANGUAGES': languages, 'LANGUAGE_CODE': language_code, 'TIME_ZONE': conf.get('TIME_ZONE', 'Europe/London'), 'USE_I18N': True, 'USE_L10N': True, 'USE_TZ': True, 'DD_MM_DATE_FORMAT_PREFERRED': conf.get('DD_MM_DATE_FORMAT_PREFERRED', True), # The media and static file settings: 'MEDIA_ROOT': media_root, 'MEDIA_URL': '/media/', # Settings for staticfiles and Django pipeline: 'STATIC_URL': '/static/', 'STATIC_ROOT': join(BASE_DIR, 'static'), 'STATICI18N_ROOT': join(BASE_DIR, 'mysite', 'static'), 'STATICFILES_DIRS': ( join(BASE_DIR, 'mysite', 'static'), ), 'STATICFILES_FINDERS': ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'pipeline.finders.PipelineFinder', ), 'PIPELINE_CSS': { 'image-review': { 'source_filenames': ( 'moderation_queue/css/jquery.Jcrop.css', 'moderation_queue/css/crop.scss', ), 'output_filename': 'css/image-review.css', }, 'official_documents': { 'source_filenames': ( 'official_documents/css/official_documents.scss', ), 'output_filename': 'css/official_documents.css', }, 'all': { 'source_filenames': ( 'candidates/style.scss', 'cached_counts/style.scss', 'select2/select2.css', 'jquery/jquery-ui.css', 'jquery/jquery-ui.structure.css', 'jquery/jquery-ui.theme.css', 'moderation_queue/css/photo-upload.scss', ), 'output_filename': 'css/all.css', } }, 'PIPELINE_JS': { 'image-review': { 'source_filenames': ( 'moderation_queue/js/jquery.color.js', 'moderation_queue/js/jquery.Jcrop.js', 'moderation_queue/js/crop.js', ), 'output_filename': 'js/image-review.js', }, 'all': { 'source_filenames': ( 'jquery/jquery-1.11.1.js', 'jquery/jquery-ui.js', 'foundation/js/foundation/foundation.js', 'foundation/js/foundation/foundation.equalizer.js', 'foundation/js/foundation/foundation.dropdown.js', 'foundation/js/foundation/foundation.tooltip.js', 'foundation/js/foundation/foundation.offcanvas.js', 'foundation/js/foundation/foundation.accordion.js', 'foundation/js/foundation/foundation.joyride.js', 'foundation/js/foundation/foundation.alert.js', 'foundation/js/foundation/foundation.topbar.js', 'foundation/js/foundation/foundation.reveal.js', 'foundation/js/foundation/foundation.slider.js', 'foundation/js/foundation/foundation.magellan.js', 'foundation/js/foundation/foundation.clearing.js', 'foundation/js/foundation/foundation.orbit.js', 'foundation/js/foundation/foundation.interchange.js', 'foundation/js/foundation/foundation.abide.js', 'foundation/js/foundation/foundation.tab.js', 'select2/select2.js', 'js/constituency.js', 'js/person_form.js', 'js/home_geolocation_form.js', 'js/versions.js', ), 'output_filename': 'js/all.js' } }, 'PIPELINE_COMPILERS': ( 'pipeline.compilers.sass.SASSCompiler', ), 'PIPELINE_SASS_ARGUMENTS': '--trace --quiet', 'PIPELINE_CSS_COMPRESSOR': 'pipeline.compressors.yui.YUICompressor', 'PIPELINE_JS_COMPRESSOR': 'pipeline.compressors.yui.YUICompressor', # On some platforms this might be called "yuicompressor", so it may be # necessary to symlink it into your PATH as "yui-compressor". 'PIPELINE_YUI_BINARY': '/usr/bin/env yui-compressor', 'TEST_RUNNER': 'django_nose.NoseTestSuiteRunner', 'SOURCE_HINTS': _( "Please don't quote third-party candidate sites \u2014 " "we prefer URLs of news stories or official candidate pages." ), # By default, cache successful results from MapIt for a day 'MAPIT_CACHE_SECONDS': 86400, 'DATABASES': databases, 'CACHES': { 'default': cache, 'thumbnails': cache_thumbnails, }, # sorl-thumbnail settings: 'THUMBNAIL_CACHE': 'thumbnails', 'THUMBNAIL_DEBUG': debug, # Settings for restricting user activity to reduce abuse: 'RESTRICT_RENAMES': conf.get('RESTRICT_RENAMES'), 'EDITS_ALLOWED': conf.get('EDITS_ALLOWED', True), # Django Rest Framework settings: 'REST_FRAMEWORK': { 'DEFAULT_PERMISSION_CLASSES': ('candidates.api_permissions.ReadOnly',), 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',), 'PAGE_SIZE': 10, }, # allow attaching extra data to notifications: 'NOTIFICATIONS_USE_JSONFIELD': True, 'HAYSTACK_SIGNAL_PROCESSOR': 'haystack.signals.RealtimeSignalProcessor', 'HAYSTACK_CONNECTIONS': { 'default': { 'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine', 'URL': 'http://127.0.0.1:9200/', 'INDEX_NAME': '{0}_{1}'.format(conf.get('YNMP_DB_NAME'), conf.get('YNMP_DB_HOST')), }, }, # CORS config 'CORS_ORIGIN_ALLOW_ALL': True, 'CORS_URLS_REGEX': r'^/api/.*$', 'CORS_ALLOW_METHODS': ( 'GET', 'OPTIONS', ), } if not conf.get('NEW_ACCOUNTS_ALLOWED', True): result['ACCOUNT_ADAPTER'] = \ 'mysite.account_adapter.NoNewUsersAccountAdapter' if tests: result['NOSE_ARGS'] = [ '--nocapture', '--with-yanc', # There are problems with OpenCV on Travis, so don't even try to # import moderation_queue/faces.py '--ignore-files=faces', ] if election_app == 'example': result['NOSE_ARGS'].append('--with-doctest') else: # If we're not testing, use PipelineCachedStorage result['STATICFILES_STORAGE'] = \ 'pipeline.storage.PipelineCachedStorage' if conf.get('NGINX_SSL'): result['SECURE_PROXY_SSL_HEADER'] = ('HTTP_X_FORWARDED_PROTO', 'https') result['ACCOUNT_DEFAULT_HTTP_PROTOCOL'] = 'https' for required_election_app_setting in ( 'SITE_OWNER', 'COPYRIGHT_HOLDER', ): result[required_election_app_setting] = \ getattr(elections_module, required_election_app_setting) for optional_election_app_setting, default in ( ('SITE_OWNER_URL', ''), ('AREAS_TO_ALWAYS_RETURN', []), ('IMAGE_PROXY_URL', ''), ): try: result[optional_election_app_setting] = \ getattr(elections_module, optional_election_app_setting) except AttributeError: result[optional_election_app_setting] = default # Make sure there's a trailing slash at the end of base MapIt URL: result['MAPIT_BASE_URL'] = \ re.sub(r'/*$', '/', elections_module.MAPIT_BASE_URL) result['INSTALLED_APPS'] = list(result['INSTALLED_APPS']) result['INSTALLED_APPS'].extend( getattr(elections_module, 'INSTALLED_APPS', []) ) return result
def get_settings(conf_file_leafname, election_app=None, tests=False): conf = get_conf(conf_file_leafname) debug = bool(int(conf.get('STAGING'))) # Get the requested ELECTION_APP: if election_app is None: election_app = conf['ELECTION_APP'] election_app_fully_qualified = 'elections.' + election_app language_code = conf.get('LANGUAGE_CODE', 'en-gb') # Internationalization # https://docs.djangoproject.com/en/1.6/topics/i18n/ locale_paths = [ join(BASE_DIR, 'locale') ] # The code below sets LANGUAGES to only those we have translations # for, so at the time of writing that will be: # [('en', 'English'), ('es-ar', 'Argentinian Spanish')] # whereas the default setting is a long list of languages which # includes: # ('es', 'Spanish'). # If someone's browser sends 'Accept-Language: es', that means that it # will be found in this list, but since there are no translations for 'es' # it'll fall back to LANGUAGE_CODE. However, if there is no 'es' in # LANGUAGES, then Django will attempt to do a best match, so if # Accept-Language is 'es' then it will use the 'es-ar' translation. We think # this is generally desirable (e.g. so someone can see YourNextMP in Spanish # if their browser asks for Spanish). languages = [ l for l in LANGUAGES if exists(join(locale_paths[0], to_locale(l[0]))) ] languages.append(('cy-gb', 'Welsh')) languages.append(('es-cr', 'Costa Rican Spanish')) # The language selection has been slightly complicated now that we # have two es- languages: es-ar and es-cr. Chrome doesn't offer # Costa Rican Spanish as one of its language choices, so the best # you can do is choose 'Spanish - español'. (This might well be # the case in other browsers too.) Since 'es-ar' comes first in # 'languages' after the preceding code, this means that someone # viewing the Costa Rica site with Chrome's preferred language set # to Spanish (i.e. with 'es' first in Accept-Language) will get # the Argentinian Spanish translations instead of Costa Rican # Spanish. To get around this, look for the default language code # for the site, and if that's present, move it to the front of # 'languages'. This should be generally helpful behaviour: the # default language code of the site should take precedence over # another language that happens to match based on the generic part # of the language code. language_code_index = next( (i for i, l in enumerate(languages) if l[0] == language_code), None ) if language_code_index is not None: languages.insert(0, languages.pop(language_code_index)) # Make sure the MEDIA_ROOT directory actually exists: media_root = conf.get('MEDIA_ROOT') or join(BASE_DIR, 'media') # Make sure that the MEDIA_ROOT and subdirectory for archived CSV # files exist: mkdir_p(join(media_root, 'csv-archives')) # Database # https://docs.djangoproject.com/en/1.6/ref/settings/#databases if conf.get('DATABASE_SYSTEM') == 'postgresql': databases = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': conf.get('YNMP_DB_NAME'), 'USER': conf.get('YNMP_DB_USER'), 'PASSWORD': conf.get('YNMP_DB_PASS'), 'HOST': conf.get('YNMP_DB_HOST'), 'PORT': conf.get('YNMP_DB_PORT'), } } else: databases = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': join(BASE_DIR, 'db.sqlite3'), } } # Setup caches depending on DEBUG: if debug: cache = {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'} cache_thumbnails = {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'} else: cache = { 'TIMEOUT': None, # cache keys never expire; we invalidate them 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', 'KEY_PREFIX': databases['default']['NAME'], } cache_thumbnails = { 'TIMEOUT': 60 * 60 * 24 * 2, # expire after two days 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', 'KEY_PREFIX': databases['default']['NAME'] + "-thumbnails", } # Create a dictionary with these settings and other simpler ones: result = { 'BASE_DIR': BASE_DIR, 'ALLOWED_HOSTS': conf.get('ALLOWED_HOSTS'), 'DEBUG': debug, 'RUNNING_TESTS': tests, # Google analytics settings: 'GOOGLE_ANALYTICS_ACCOUNT': conf.get('GOOGLE_ANALYTICS_ACCOUNT'), 'USE_UNIVERSAL_ANALYTICS': conf.get('USE_UNIVERSAL_ANALYTICS', True), # The Twitter account referenced in the Twitter card data: 'TWITTER_USERNAME': conf.get('TWITTER_USERNAME', ''), # The email address which is made public on the site for sending # support email to: 'SUPPORT_EMAIL': conf['SUPPORT_EMAIL'], # Email addresses that error emails are sent to when DEBUG = False 'ADMINS': conf['ADMINS'], # The From: address for all emails except error emails 'DEFAULT_FROM_EMAIL': conf['DEFAULT_FROM_EMAIL'], # The From: address for error emails 'SERVER_EMAIL': conf['SERVER_EMAIL'], # SECURITY WARNING: keep the secret key used in production secret! 'SECRET_KEY': conf['SECRET_KEY'], 'TEMPLATE_DEBUG': True, 'TEMPLATE_DIRS': ( join(BASE_DIR, 'mysite', 'templates'), ), 'TEMPLATE_CONTEXT_PROCESSORS': TEMPLATE_CONTEXT_PROCESSORS + ( # Required by allauth template tags "django.core.context_processors.request", # allauth specific context processors "allauth.account.context_processors.account", "allauth.socialaccount.context_processors.socialaccount", "django.contrib.messages.context_processors.messages", "mysite.context_processors.add_settings", "mysite.context_processors.election_date", "mysite.context_processors.add_group_permissions", "mysite.context_processors.add_notification_data", "mysite.context_processors.locale", "mysite.context_processors.add_site", ), 'ELECTION_APP': election_app, 'ELECTION_APP_FULLY_QUALIFIED': election_app_fully_qualified, # The Django applications in use: 'INSTALLED_APPS': [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.humanize', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', 'django_nose', 'django_extensions', 'pipeline', 'statici18n', 'sorl.thumbnail', 'rest_framework', 'rest_framework.authtoken', 'images', 'haystack', 'elections', 'popolo', election_app_fully_qualified, 'candidates', 'tasks', 'alerts', 'cached_counts', 'moderation_queue', 'auth_helpers', 'debug_toolbar', 'template_timings_panel', 'official_documents', 'results', 'notifications', 'allauth', 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.google', 'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.twitter', 'corsheaders', 'crispy_forms', ], 'SITE_ID': 1, 'MIDDLEWARE_CLASSES': ( 'debug_toolbar.middleware.DebugToolbarMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'candidates.middleware.CopyrightAssignmentMiddleware', 'candidates.middleware.DisallowedUpdateMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ), # django-allauth settings: 'AUTHENTICATION_BACKENDS': ( # Needed to login by username in Django admin, regardless of `allauth` "django.contrib.auth.backends.ModelBackend", # `allauth` specific authentication methods, such as login by e-mail "allauth.account.auth_backends.AuthenticationBackend", ), 'SOCIALACCOUNT_PROVIDERS': { 'google': {'SCOPE': ['https://www.googleapis.com/auth/userinfo.profile'], 'AUTH_PARAMS': {'access_type': 'online'}}, 'facebook': {'SCOPE': ['email',]}, }, 'LOGIN_REDIRECT_URL': '/', 'ACCOUNT_EMAIL_VERIFICATION': 'mandatory', 'ACCOUNT_EMAIL_REQUIRED': True, 'ACCOUNT_USERNAME_REQUIRED': True, 'SOCIALACCOUNT_AUTO_SIGNUP': True, 'ROOT_URLCONF': 'mysite.urls', 'WSGI_APPLICATION': 'mysite.wsgi.application', # Django Debug Toolbar settings: 'DEBUG_TOOLBAR_PATCH_SETTINGS': False, 'DEBUG_TOOLBAR_PANELS': [ 'debug_toolbar.panels.versions.VersionsPanel', 'debug_toolbar.panels.timer.TimerPanel', 'debug_toolbar.panels.settings.SettingsPanel', 'debug_toolbar.panels.headers.HeadersPanel', 'debug_toolbar.panels.request.RequestPanel', 'debug_toolbar.panels.sql.SQLPanel', 'debug_toolbar.panels.staticfiles.StaticFilesPanel', 'debug_toolbar.panels.templates.TemplatesPanel', 'debug_toolbar.panels.cache.CachePanel', 'debug_toolbar.panels.signals.SignalsPanel', 'debug_toolbar.panels.logging.LoggingPanel', 'debug_toolbar.panels.redirects.RedirectsPanel', 'template_timings_panel.panels.TemplateTimings.TemplateTimings', ], 'INTERNAL_IPS': ['127.0.0.1'], # Language settings (calculated above): 'LOCALE_PATHS': locale_paths, 'LANGUAGES': languages, 'LANGUAGE_CODE': language_code, 'TIME_ZONE': conf.get('TIME_ZONE', 'Europe/London'), 'USE_I18N': True, 'USE_L10N': True, 'USE_TZ': True, 'DD_MM_DATE_FORMAT_PREFERRED': conf.get('DD_MM_DATE_FORMAT_PREFERRED', True), # The media and static file settings: 'MEDIA_ROOT': media_root, 'MEDIA_URL': '/media/', # Settings for staticfiles and Django pipeline: 'STATIC_URL': '/static/', 'STATIC_ROOT': join(BASE_DIR, 'static'), 'STATICI18N_ROOT': join(BASE_DIR, 'mysite', 'static'), 'STATICFILES_DIRS': ( join(BASE_DIR, 'mysite', 'static'), ), 'STATICFILES_FINDERS': ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'pipeline.finders.PipelineFinder', ), 'PIPELINE': { 'STYLESHEETS': { 'image-review': { 'source_filenames': ( 'moderation_queue/css/jquery.Jcrop.css', 'moderation_queue/css/crop.scss', ), 'output_filename': 'css/image-review.css', }, 'official_documents': { 'source_filenames': ( 'official_documents/css/official_documents.scss', ), 'output_filename': 'css/official_documents.css', }, 'all': { 'source_filenames': ( 'candidates/style.scss', 'cached_counts/style.scss', 'select2/select2.css', 'jquery/jquery-ui.css', 'jquery/jquery-ui.structure.css', 'jquery/jquery-ui.theme.css', 'moderation_queue/css/photo-upload.scss', ), 'output_filename': 'css/all.css', } }, 'JAVASCRIPT': { 'image-review': { 'source_filenames': ( 'moderation_queue/js/jquery.color.js', 'moderation_queue/js/jquery.Jcrop.js', 'moderation_queue/js/crop.js', ), 'output_filename': 'js/image-review.js', }, 'all': { 'source_filenames': ( 'jquery/jquery-1.11.1.js', 'jquery/jquery-ui.js', 'foundation/js/foundation/foundation.js', 'foundation/js/foundation/foundation.equalizer.js', 'foundation/js/foundation/foundation.dropdown.js', 'foundation/js/foundation/foundation.tooltip.js', 'foundation/js/foundation/foundation.offcanvas.js', 'foundation/js/foundation/foundation.accordion.js', 'foundation/js/foundation/foundation.joyride.js', 'foundation/js/foundation/foundation.alert.js', 'foundation/js/foundation/foundation.topbar.js', 'foundation/js/foundation/foundation.reveal.js', 'foundation/js/foundation/foundation.slider.js', 'foundation/js/foundation/foundation.magellan.js', 'foundation/js/foundation/foundation.clearing.js', 'foundation/js/foundation/foundation.orbit.js', 'foundation/js/foundation/foundation.interchange.js', 'foundation/js/foundation/foundation.abide.js', 'foundation/js/foundation/foundation.tab.js', 'select2/select2.js', 'js/constituency.js', 'js/person_form.js', 'js/home_geolocation_form.js', 'js/versions.js', ), 'output_filename': 'js/all.js' } }, 'COMPILERS': ( 'pipeline.compilers.sass.SASSCompiler', ), 'SASS_BINARY': 'sassc', 'CSS_COMPRESSOR': 'pipeline.compressors.yui.YUICompressor', 'JS_COMPRESSOR': 'pipeline.compressors.yui.YUICompressor', # On some platforms this might be called "yuicompressor", so it may be # necessary to symlink it into your PATH as "yui-compressor". 'YUI_BINARY': '/usr/bin/env yui-compressor', }, 'TEST_RUNNER': 'django_nose.NoseTestSuiteRunner', 'SOURCE_HINTS': _( u"Please don't quote third-party candidate sites \u2014 " u"we prefer URLs of news stories or official candidate pages." ), # By default, cache successful results from MapIt for a day 'MAPIT_CACHE_SECONDS': 86400, 'DATABASES': databases, 'CACHES': { 'default': cache, 'thumbnails': cache_thumbnails, }, # sorl-thumbnail settings: 'THUMBNAIL_CACHE': 'thumbnails', 'THUMBNAIL_DEBUG': debug, # Settings for restricting user activity to reduce abuse: 'RESTRICT_RENAMES': conf.get('RESTRICT_RENAMES'), 'EDITS_ALLOWED': conf.get('EDITS_ALLOWED', True), # A bearer token for the Twitter API for mapping between # Twitter usernames and IDs. 'TWITTER_APP_ONLY_BEARER_TOKEN': conf.get('TWITTER_APP_ONLY_BEARER_TOKEN'), # Django Rest Framework settings: 'REST_FRAMEWORK': { 'DEFAULT_PERMISSION_CLASSES': ('candidates.api_permissions.ReadOnly',), 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',), 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework_jsonp.renderers.JSONPRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ), 'PAGE_SIZE': 10, }, # allow attaching extra data to notifications: 'NOTIFICATIONS_USE_JSONFIELD': True, 'HAYSTACK_SIGNAL_PROCESSOR': 'haystack.signals.RealtimeSignalProcessor', 'HAYSTACK_CONNECTIONS': { 'default': { 'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine', 'URL': 'http://127.0.0.1:9200/', 'INDEX_NAME': '{0}_{1}'.format(conf.get('YNMP_DB_NAME'), conf.get('YNMP_DB_HOST')), }, }, # CORS config 'CORS_ORIGIN_ALLOW_ALL': True, 'CORS_URLS_REGEX': r'^/(api|upcoming-elections)/.*$', 'CORS_ALLOW_METHODS': ( 'GET', 'OPTIONS', ), } if not conf.get('NEW_ACCOUNTS_ALLOWED', True): result['ACCOUNT_ADAPTER'] = \ 'mysite.account_adapter.NoNewUsersAccountAdapter' result['CANDIDATES_REQUIRED_FOR_WEIGHTED_PARTY_LIST'] = \ conf.get('CANDIDATES_REQUIRED_FOR_WEIGHTED_PARTY_LIST', 20) result['HOIST_ELECTED_CANDIDATES'] = \ conf.get('HOIST_ELECTED_CANDIDATES', True) if tests: result['NOSE_ARGS'] = [ '--nocapture', '--with-yanc', # There are problems with OpenCV on Travis, so don't even try to # import moderation_queue/faces.py '--ignore-files=faces', ] if election_app == 'example': result['NOSE_ARGS'].append('--with-doctest') else: # If we're not testing, use PipelineCachedStorage result['STATICFILES_STORAGE'] = \ 'pipeline.storage.PipelineCachedStorage' if conf.get('NGINX_SSL'): result['SECURE_PROXY_SSL_HEADER'] = ('HTTP_X_FORWARDED_PROTO', 'https') result['ACCOUNT_DEFAULT_HTTP_PROTOCOL'] = 'https' add_election_specific_settings(result, election_app_fully_qualified) result['RESULTS_FEATURE_ACTIVE'] = True result['DATE_FORMAT'] = conf.get('DATE_FORMAT', "jS E Y") return result
def _fetch(lang, fallback=None): global _translations res = _translations.get(lang, None) if res is not None: return res loc = to_locale(lang) def _translation(path): try: if needs_compilation('django', path, lang): compile_messages('django', path, lang) t = gettext_module.translation('django', path, [loc], DjangoTranslation) t.set_language(lang) return t except IOError: return None res = _translation(globalpath) # We want to ensure that, for example, "en-gb" and "en-us" don't share # the same translation object (thus, merging en-us with a local update # doesn't affect en-gb), even though they will both use the core "en" # translation. So we have to subvert Python's internal gettext caching. base_lang = lambda x: x.split('-', 1)[0] if base_lang(lang) in [ base_lang(trans) for trans in list(_translations) ]: res._info = res._info.copy() res._catalog = res._catalog.copy() def _merge(path): t = _translation(path) if t is not None: if res is None: return t else: res.merge(t) return res for appname in reversed(settings.INSTALLED_APPS): app = import_module(appname) apppath = os.path.join(os.path.dirname(upath(app.__file__)), 'locale') if os.path.isdir(apppath): res = _merge(apppath) for localepath in reversed(settings.LOCALE_PATHS): if os.path.isdir(localepath): res = _merge(localepath) if res is None: if fallback is not None: res = fallback else: return gettext_module.NullTranslations() _translations[lang] = res return res
def normalize_language(language): return locale.locale_alias.get(to_locale(language, True))
### Here be dragons. # Django decided to require that ForeignKeys be unique. That's generally # reasonable, but Translations break that in their quest for all things unholy. # Here we monkeypatch the error collector Django uses in validation to skip any # messages generated by Translations. (Django #13284) import re from django.conf import settings from django.core.management import validation from django.utils.translation import trans_real from jinja2.filters import do_dictsort Parent = validation.ModelErrorCollection class ModelErrorCollection(Parent): skip = ("Field 'id' under model '\w*Translation' must " "have a unique=True constraint.") def add(self, context, error): if not re.match(self.skip, error): Parent.add(self, context, error) validation.ModelErrorCollection = ModelErrorCollection LOCALES = [(trans_real.to_locale(k).replace('_', '-'), v) for k, v in do_dictsort(settings.LANGUAGES)]
def setup_mofile_with_entry(self, poentry, language='en'): locale_location = join(settings.LOCALE_DIR, 'locale', language, 'LC_MESSAGES') pofile = polib.pofile(join(locale_location, 'django.po')) pofile.append(poentry) pofile.save() pofile.save_as_mofile(join(locale_location, 'django.mo')) jit_locale = gettext_module.translation('django', join(settings.LOCALE_DIR, 'locale'), [trans_real.to_locale(language)], trans_real.DjangoTranslation) jit_locale.set_language(language) locale = trans_real.translation(language) locale.merge(jit_locale)
from django.conf import settings from django.utils.translation import trans_real from jinja2.filters import do_dictsort LOCALES = [(trans_real.to_locale(k).replace('_', '-'), v) for k, v in do_dictsort(settings.LANGUAGES)]