Add preliminary L10n dashboard. p=fwenzel, p=clouserw, p=potch

This commit is contained in:
Fred Wenzel 2010-02-17 16:18:32 +01:00 коммит произвёл Wil Clouser
Родитель 5eb496fc3b
Коммит bd5d3abce0
18 изменённых файлов: 544 добавлений и 3 удалений

Просмотреть файл

Просмотреть файл

@ -0,0 +1,67 @@
from django.conf import settings
import jinja2
from jingo import register
from product_details import product_details
from access import acl
from .models import L10nSettings, L10nEventlog
def _permission_to_edit_locale(request, locale=''):
"""If locale is empty, it checks global permissions."""
if acl.action_allowed(request, 'Admin', 'EditAnyLocale'):
return True
if locale and acl.action_allowed(request, 'Localizers', locale):
return True
return False
@register.inclusion_tag('localizers/sidebar.html')
@jinja2.contextfunction
def localizers_sidebar(context, locale_code=""):
"""Sidebar on the per-locale localizer dashboard page."""
ctx = dict(context.items())
request = context['request']
ctx.update({
'show_edit': _permission_to_edit_locale(request, locale_code),
'locale_code': locale_code,
})
return ctx
@register.inclusion_tag('localizers/sidebar_motd.html')
@jinja2.contextfunction
def localizers_sidebar_motd(context, lang=''):
"""Message of the Day on localizer dashboards."""
request = context['request']
try:
l10n_set = L10nSettings.objects.get(locale=lang)
motd = l10n_set.motd
except L10nSettings.DoesNotExist:
motd = None
ctx = dict(context.items())
ctx.update({
'motd_lang': lang,
'motd': motd,
'show_edit': _permission_to_edit_locale(request, lang),
})
return ctx
@register.inclusion_tag('localizers/locale_switcher.html')
def locale_switcher(current_locale=None):
"""Locale dropdown to switch user locale on localizer pages."""
return {
'current_locale': current_locale,
'locales': settings.AMO_LANGUAGES + settings.HIDDEN_LANGUAGES,
'languages': product_details.languages,
}

37
apps/localizers/models.py Normal file
Просмотреть файл

@ -0,0 +1,37 @@
from django.db import models
import caching.base
import amo.models
from translations.fields import PurifiedField
class L10nEventlog(caching.base.CachingMixin, models.Model):
locale = models.CharField(max_length=30, default='')
type = models.CharField(max_length=20, default='')
action = models.CharField(max_length=40, default='')
field = models.CharField(max_length=20, default='', null=True)
user = models.ForeignKey('users.UserProfile')
changed_id = models.PositiveIntegerField(
default=0, help_text='id of the object being affected by the change')
added = models.CharField(max_length=255, default='', null=True)
removed = models.CharField(max_length=255, default='', null=True)
notes = models.TextField()
created = models.DateTimeField(auto_now_add=True)
objects = amo.models.ManagerBase()
class Meta:
db_table = 'l10n_eventlog'
get_latest_by = 'created'
class L10nSettings(amo.models.ModelBase):
"""Per-locale L10n Dashboard settings"""
locale = models.CharField(max_length=30, default='', unique=True)
motd = PurifiedField()
team_homepage = models.CharField(max_length=255, default='', null=True)
class Meta:
db_table = 'l10n_settings'

Просмотреть файл

@ -0,0 +1,7 @@
{% extends "impala/base.html" %}
{% block title %}{{ page_title(_('Localizer Dashboard')) }}{% endblock %}
{% block js %} {{ js('zamboni/localizers') }} {% endblock %}
{% block bodyclass %}gutter{% endblock %}

Просмотреть файл

@ -0,0 +1,26 @@
{% extends "localizers/base.html" %}
{% block title %}
{# L10n: %s is a locale name or code ('German' or 'de') #}
{{ page_title(_('Localization Summary for {0}')|f(userlang['native'])) }}
{% endblock %}
{% block content %}
<header>
{{ impala_breadcrumbs([(url('localizers.dashboard'), _('Localization Dashboard'))]) }}
<h1>{{ _('Category Localization for {0}')|f(userlang.native) }}</h1>
{{ locale_switcher(current_locale=locale_code) }}
</header>
{{ localizers_sidebar(locale_code=locale_code) }}
<div class="primary c">
<section class="hero island c">
Future home of category L10n
</section>
</div>{# /#primary #}
{% endblock content %}

Просмотреть файл

@ -0,0 +1,44 @@
{% extends "localizers/base.html" %}
{% block title %}
{# L10n: %s is a locale name or code ('German' or 'de') #}
{{ page_title(_('Localization Summary for {0}')|f(userlang['native'])) }}
{% endblock %}
{% block content %}
<header>
{{ impala_breadcrumbs([(url('localizers.dashboard'), _('Localization Dashboard'))]) }}
<h1>{{ _('Localization Summary for {0}')|f(userlang.native) }}</h1>
{{ locale_switcher(current_locale=locale_code) }}
</header>
{{ localizers_sidebar(locale_code=locale_code) }}
<div class="primary c">
<section class="hero island c">
{% if members or team_homepage %}
{# L10n: {0} is a language name, e.g. 'Deutsch' #}
<h2>{{ _('Want to get in touch with the {0} L10n team?')|f(userlang['native']) }}</h2>
{% if team_homepage %}
<p><a href="{{ team_homepage|external_url }}" hreflang="{{ locale_code }}">
{# L10n: {0} is a language name, e.g. 'Deutsch' #}
{{ _('Visit the {0} L10n Homepage')|f(userlang['native']) }}
</a></p>
{% endif %}
{% if members %}
{# L10n: {0} is a language name, e.g. 'Deutsch' #}
<h2>{{ _('Current Localizers for {0}:')|f(userlang['native']) }}</h2>
<ul>
{% for member in members %}
<li>{{ member|user_link }}</li>
{% endfor %}
</ul>
{% endif %}
{% endif %}
</section>
</div>{# /#primary #}
{% endblock content %}

Просмотреть файл

@ -0,0 +1,11 @@
<div id="localeswitcher">
<form method="get" action="">
<label for="userlang">{{ _('Change Locale:') }}</label>
<select id="userlang" name="userlang" onChange="this.form.submit();">
{% for locale in locales %}
<option value="{{ locale }}"{{ ' selected' if locale == current_locale }}>{{ languages[locale]['native'] }}</option>
{% endfor %}
</select>
<noscript><button type="submit">{{ _('Go') }}</button></noscript>
</form>
</div>

Просмотреть файл

@ -0,0 +1,49 @@
<section class="secondary" role="complementary">
<aside id="l10n-motd" class="notice">
{# L10n: Header to an info box in the sidebar #}
<h2>{{ _('Good to know') }}</h2>
{{ localizers_sidebar_motd() }}
{% if locale_code %}
<hr />
{{ localizers_sidebar_motd(lang=locale_code) }}
{% endif %}
</aside>
<aside class="notice">
<h2>{{ _('Want the bigger picture?') }}</h2>
<a class="button" href="http://localize.mozilla.org">
{{ ('Visit localize.mozilla.org') }}
</a>
</aside>
{% if locale_code %}
<div>
<h2>{{ _('Options') }}</h2>
<ul>
<li>
<a href="{{ url('localizers.categories', locale_code) }}">{{ _('Categories') }}</a>
</li>
</ul>
</div>
{% if show_edit %}
<div>
<h2>{{ _('Need Help?') }}</h2>
<p>
<a class="button" target="_blank" href="http://www.mozilla.org/community/developer-forums.html#dev-l10n-web">
{{ _('Visit the Newsgroup') }}
</a>
</p>
{% if members %}
<h3>{{ _('E-Mail Your Team:') }}</h3>
<ul>
{% for member in members %}
<li><a href="mailto:{{ member.email }}">{{ member.display_name }}</a></li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endif %}
{% endif %}
</section>

Просмотреть файл

@ -0,0 +1,19 @@
<div class="motd">
{% if show_edit %}
<div class="edit_motd" style="display:none;">
<form action="{{ url('localizers.set_motd') }}" method="post">
{{ csrf() }}
<input type="hidden" name="lang" value="{{ motd_lang }}"/>
<textarea name="msg">{{ motd and motd.localized_string }}</textarea>
<button type="submit">{{ _('Submit') }}</button>
<a href="#" class="cancel">{{ _('Cancel') }}</a>
</form>
</div>
{% endif %}
<div class="motd_text">
<div class="msg">{{ motd }}</div>
{% if show_edit %}
<p class="edit"><a href="#">{{ _('(edit)') }}</a></p>
{% endif %}
</div>
</div>

Просмотреть файл

@ -0,0 +1,50 @@
{% extends "localizers/base.html" %}
{% block title %}{{ page_title(_('Localizer Dashboard')) }}{% endblock %}
{% block content %}
<header>
{{ impala_breadcrumbs([(url('localizers.dashboard'), _('Localization Dashboard'))]) }}
<h1>{{ _('Mozilla Add-ons Localization') }}</h1>
</header>
{{ localizers_sidebar() }}
<div class="primary c">
<section class="notice">
<h2>{{ _('Want to help?') }}</h2>
<p>{{ _('AMO is available in so many languages thanks to the help of people like you!') }}</p>
<p><a class="button" target="_blank" href="https://wiki.mozilla.org/AMO:Localizers">
{{ _('Get started with AMO L10n') }}
</a></p>
</section>
<section class="island hero c">
<h2>{{ _('Existing AMO locales') }}</h2>
<table style="width:100%">
<thead>
<tr>
<th>{{ ('Locale code') }}</th>
<th colspan="2">{{ ('Locale name') }}</th>
</tr>
</thead>
<tbody>
{% for lang in amo_languages %}
<tr>
<td>{{ lang }}</td>
<td>
<a href="{{ url('localizers.locale_dashboard', lang) }}">
{{ languages[lang]['native'] }}
</a>
</td>
<td>
{{ languages[lang]['English'] }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
</div>
{% endblock content %}

16
apps/localizers/urls.py Normal file
Просмотреть файл

@ -0,0 +1,16 @@
from django.conf.urls.defaults import patterns, url, include
from . import views
# These will all start with /localizers/<locale_code>/
detail_patterns = patterns('',
url('^$', views.locale_dashboard, name='localizers.locale_dashboard'),
url('^categories/$', views.categories, name='localizers.categories'),
)
urlpatterns = patterns('',
('^(?P<locale_code>[\w-]+)/', include(detail_patterns)),
url('^$', views.summary, name='localizers.dashboard'),
url('^set_motd$', views.set_motd, name='localizers.set_motd'),
)

127
apps/localizers/views.py Normal file
Просмотреть файл

@ -0,0 +1,127 @@
from django import http
from django.conf import settings
import commonware.log
import jingo
from product_details import product_details
from access.models import Group
from amo.decorators import json_view, login_required, post_required, write
from amo.urlresolvers import reverse
from .helpers import _permission_to_edit_locale
from .models import L10nSettings
log = commonware.log.getLogger('z.l10n')
@write
@login_required
@post_required
@json_view
def set_motd(request):
"""AJAX: Set announcements for either global or per-locale dashboards."""
lang = request.POST.get('lang')
msg = request.POST.get('msg')
if (lang != '' and lang not in settings.AMO_LANGUAGES and
lang not in settings.HIDDEN_LANGUAGES or msg is None):
return json_view.error(_('An error occurred saving this message.'))
if _permission_to_edit_locale(lang):
return json_view.error(_('Access Denied'))
l10n_set, created = L10nSettings.objects.get_or_create(locale=lang)
# MOTDs are monolingual, so always store them in the default fallback
# locale (probably en-US)
l10n_set.motd = {settings.LANGUAGE_CODE: msg}
l10n_set.save(force_update=True)
log.info("Changing MOTD for (%s) to (%s)", lang or 'global', msg)
data = {
'msg': l10n_set.motd.localized_string,
'msg_purified': unicode(l10n_set.motd)
}
return data
def summary(request):
"""global L10n dashboard"""
data = {
'languages': product_details.languages,
'amo_languages': sorted(settings.AMO_LANGUAGES +
settings.HIDDEN_LANGUAGES),
'hidden_languages': settings.HIDDEN_LANGUAGES,
}
return jingo.render(request, 'localizers/summary.html', data)
def locale_switcher(f):
"""Decorator redirecting clicks on the locale switcher dropdown."""
def decorated(request, *args, **kwargs):
new_userlang = request.GET.get('userlang')
if (new_userlang and new_userlang in settings.AMO_LANGUAGES or
new_userlang in settings.HIDDEN_LANGUAGES):
kwargs['locale_code'] = new_userlang
return http.HttpResponsePermanentRedirect(reverse(
decorated, args=args, kwargs=kwargs))
else:
return f(request, *args, **kwargs)
return decorated
def valid_locale(f):
"""Decorator validating locale code for per-language pages."""
def decorated(request, locale_code, *args, **kwargs):
if locale_code not in (settings.AMO_LANGUAGES +
settings.HIDDEN_LANGUAGES):
raise http.Http404
return f(request, locale_code, *args, **kwargs)
return decorated
@locale_switcher
@valid_locale
def locale_dashboard(request, locale_code):
"""per-locale dashboard"""
data = {
'locale_code': locale_code,
'userlang': product_details.languages[locale_code],
}
# group members
try:
group = Group.objects.get(
rules__startswith=('Localizers:%s' % locale_code))
members = group.users.all()
except Group.DoesNotExist:
members = None
data['members'] = members
# team homepage
try:
l10n_set = L10nSettings.objects.get(locale=locale_code)
team_homepage = l10n_set.team_homepage
except L10nSettings.DoesNotExist:
team_homepage = None
data['team_homepage'] = team_homepage
return jingo.render(request, 'localizers/dashboard.html', data)
@login_required
@locale_switcher
@valid_locale
def categories(request, locale_code):
if _permission_to_edit_locale(request, locale_code):
return http.HttpResponseForbidden()
data = {
'locale_code': locale_code,
'userlang': product_details.languages[locale_code],
}
return jingo.render(request, 'localizers/categories.html', data)

Просмотреть файл

@ -26,7 +26,10 @@ class Config(models.Model):
@property
def json(self):
return json.loads(self.value)
try:
return json.loads(self.value)
except TypeError, ValueError:
return {}
def get_config(conf):

Просмотреть файл

@ -29,7 +29,7 @@ $(document).ready(function() {
.siblings('.motd_text').show();
});
$('.sidebar .motd .motd_text p.edit a').click(function(e) {
$('#l10n-motd .motd .motd_text p.edit a').click(function(e) {
e.preventDefault();
$(this).closest('.motd_text')
.hide()

Просмотреть файл

@ -0,0 +1,37 @@
CREATE TABLE `l10n_settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`locale` varchar(30) NOT NULL,
`motd` int(11) DEFAULT NULL,
`team_homepage` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `locale` (`locale`),
UNIQUE KEY `motd` (`motd`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
INSERT INTO l10n_settings (locale, team_homepage) VALUES
('ca', 'http://www.mozilla.cat/'),
('cs', 'http://www.mozilla.cz/'),
('da', 'http://mozilladanmark.dk/'),
('es-ES', 'http://www.proyectonave.es/'),
('eu', 'http://www.librezale.org/'),
('fy-NL', 'http://www.mozilla-nl.org/projecten/frysk'),
('fr', 'http://www.frenchmozilla.fr/'),
('ga-IE', 'http://gaeilge.mozdev.org/'),
('he', 'http://mozilla.org.il/'),
('hu', 'http://mozilla.fsf.hu/'),
('it', 'http://www.mozillaitalia.it/'),
('lt', 'http://firefox.lt/'),
('nl', 'http://www.mozilla-nl.org'),
('pl', 'http://www.aviary.pl/'),
('pt-PT', 'http://mozilla.pt/'),
('si', 'http://www.mozilla.lk/'),
('sk', 'http://www.mozilla.sk/'),
('ta-LK', 'http://www.mozilla.lk/'),
('uk', 'http://mozilla.org.ua/'),
('vi', 'http://vi.mozdev.org/'),
('zh-CN', 'http://narro.mozest.com/'),
('zh-TW', 'http://moztw.org/')
;
UPDATE l10n_settings SET created=NOW(), modified=NOW();

Просмотреть файл

@ -0,0 +1,37 @@
CREATE TABLE `l10n_settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`locale` varchar(30) NOT NULL,
`motd` int(11) DEFAULT NULL,
`team_homepage` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `locale` (`locale`),
UNIQUE KEY `motd` (`motd`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
INSERT INTO l10n_settings (locale, team_homepage) VALUES
('ca', 'http://www.mozilla.cat/'),
('cs', 'http://www.mozilla.cz/'),
('da', 'http://mozilladanmark.dk/'),
('es-ES', 'http://www.proyectonave.es/'),
('eu', 'http://www.librezale.org/'),
('fy-NL', 'http://www.mozilla-nl.org/projecten/frysk'),
('fr', 'http://www.frenchmozilla.fr/'),
('ga-IE', 'http://gaeilge.mozdev.org/'),
('he', 'http://mozilla.org.il/'),
('hu', 'http://mozilla.fsf.hu/'),
('it', 'http://www.mozillaitalia.it/'),
('lt', 'http://firefox.lt/'),
('nl', 'http://www.mozilla-nl.org'),
('pl', 'http://www.aviary.pl/'),
('pt-PT', 'http://mozilla.pt/'),
('si', 'http://www.mozilla.lk/'),
('sk', 'http://www.mozilla.sk/'),
('ta-LK', 'http://www.mozilla.lk/'),
('uk', 'http://mozilla.org.ua/'),
('vi', 'http://vi.mozdev.org/'),
('zh-CN', 'http://narro.mozest.com/'),
('zh-TW', 'http://moztw.org/')
;
UPDATE l10n_settings SET created=NOW(), modified=NOW();

Просмотреть файл

@ -99,9 +99,13 @@ AMO_LANGUAGES = (
'af', 'ar', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en-US', 'es-ES',
'eu', 'fa', 'fi', 'fr', 'ga-IE', 'he', 'hu', 'id', 'it', 'ja', 'ko', 'mn',
'nl', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sl', 'sq', 'sv-SE',
'tr', 'uk', 'vi', 'zh-CN', 'zh-TW',
'uk', 'vi', 'zh-CN', 'zh-TW',
)
# Not shown on the site, but .po files exist and these are available on the L10n
# dashboard. Generally languages start here and move into AMO_LANGUAGES.
HIDDEN_LANGUAGES = ('cy', 'sr', 'sr-Latn', 'tr')
def lazy_langs():
from product_details import product_details
@ -337,6 +341,7 @@ INSTALLED_APPS = (
'files',
'jingo_minify',
'market',
'localizers',
'pages',
'perf',
'product_details',
@ -711,6 +716,9 @@ MINIFY_BUNDLES = {
'js/lib/syntaxhighlighter/shBrushXml.js',
'js/zamboni/files.js',
),
'zamboni/localizers': (
'js/zamboni/localizers.js',
),
'zamboni/mobile': (
'js/lib/jquery-1.5.js',
'js/lib/jqmobile.js',

Просмотреть файл

@ -47,6 +47,9 @@ urlpatterns = patterns('',
# Downloads.
('^downloads/', include(versions.urls.download_patterns)),
# Localizer Pages
('^localizers/', include('localizers.urls')),
# Users
('', include('users.urls')),