Add locales managment to UI

This commit is contained in:
Dmitriy Simushev 2014-06-24 09:47:23 +00:00
parent 7309244ccf
commit 70c77762a2
10 changed files with 433 additions and 15 deletions

View File

@ -125,6 +125,16 @@ $dbtables = array(
"translation" => "text", "translation" => "text",
), ),
// Contains locales info
"${mysqlprefix}locale" => array(
// Artificial primary key
"localeid" => "int NOT NULL auto_increment PRIMARY KEY",
// Locale code
"code" => "varchar(5) NOT NULL",
// Indicates if a locale is enabled or not.
"enabled" => "tinyint NOT NULL DEFAULT 0",
),
// Contains localized mail templates // Contains localized mail templates
"${mysqlprefix}mailtemplate" => array( "${mysqlprefix}mailtemplate" => array(
// Artificial primary key // Artificial primary key

View File

@ -402,6 +402,27 @@ function add_mail_templates($link){
} }
} }
function add_locales($link)
{
global $mysqlprefix;
$localesresult = mysql_query("select code from ${mysqlprefix}locale", $link);
$existlocales = array();
for ($i = 0; $i < mysql_num_rows($localesresult); $i++) {
$existlocales[] = mysql_result($localesresult, $i, 'code');
}
$locales = discover_locales();
foreach ($locales as $locale) {
if (in_array($locale, $existlocales)) {
// Do not add locales twice.
continue;
}
$query = "insert into ${mysqlprefix}locale (code, enabled) values ('"
. mysql_real_escape_string($locale, $link) . "', 1)";
mysql_query($query, $link);
}
}
function get_yml_file_content($file) function get_yml_file_content($file)
{ {
$yaml = new \Symfony\Component\Yaml\Parser(); $yaml = new \Symfony\Component\Yaml\Parser();
@ -443,6 +464,7 @@ function check_status()
return; return;
} }
add_locales($link);
add_canned_messages($link); add_canned_messages($link);
add_mail_templates($link); add_mail_templates($link);

View File

@ -0,0 +1,50 @@
<?php
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Controller\Localization;
use Mibew\Controller\AbstractController as BaseAbstractController;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides a set of utility functions.
*/
abstract class AbstractController extends BaseAbstractController
{
/**
* Builds list of the localization tabs.
*
* @param Request $request Current request.
* @return array Tabs list. The keys of the array are tabs titles and the
* values are tabs URLs.
*/
protected function buildTabs(Request $request)
{
$tabs = array();
$route = $request->attributes->get('_route');
$tabs[getlocal('page_localization.tab.translation')] = ($route != 'translations')
? $this->generateUrl('translations')
: '';
$tabs[getlocal('page_localization.tab.locale')] = ($route != 'locales')
? $this->generateUrl('locales')
: '';
return $tabs;
}
}

View File

@ -0,0 +1,134 @@
<?php
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Controller\Localization;
use Mibew\Http\Exception\NotFoundException;
use Symfony\Component\HttpFoundation\Request;
/**
* Contains actions which are related with locales management.
*/
class LocaleController extends AbstractController
{
/**
* Generates list of all locales in the system.
*
* @param Request $request Incoming request.
* @return string Rendered page content.
*/
public function indexAction(Request $request)
{
set_csrf_token();
$operator = $this->getOperator();
$page = array(
// Use errors list stored in the request. We need to do so to have
// an ability to pass the request from other actions.
'errors' => $request->attributes->get('errors', array()),
);
$fs_locales = discover_locales();
$locale_names = get_locale_names();
$available_locales = get_available_locales();
$locales_list = array();
foreach($fs_locales as $locale) {
$locales_list[] = array(
'code' => $locale,
'name' => (isset($locale_names[$locale]) ? $locale_names[$locale] : $locale),
'isDisabled' => !in_array($locale, $available_locales),
);
}
$page['localesList'] = $locales_list;
$page['title'] = getlocal('page_locales.title');
$page['menuid'] = 'translation';
$page = array_merge($page, prepare_menu($operator));
$page['tabs'] = $this->buildTabs($request);
return $this->render('locales', $page);
}
/**
* Enables a locale.
*
* @param Request $request Incoming request.
* @return \Symfony\Component\HttpFoundation\Response A response object.
* @throws NotFoundException If the locale which should be enabled is not
* found.
*/
public function enableAction(Request $request)
{
csrf_check_token($request);
$locale = $request->attributes->get('locale');
// Check if locale exists.
if (!in_array($locale, discover_locales())) {
throw new NotFoundException();
}
// Enable locale if it is needed and redirect the operator to the
// locales page.
if (!in_array($locale, get_available_locales())) {
enable_locale($locale);
}
return $this->redirect($this->generateUrl('locales'));
}
/**
* Disables a locale.
*
* @param Request $request Incoming request.
* @return \Symfony\Component\HttpFoundation\Response A response object.
* @throws NotFoundException If the locale which should be disabled is not
* found.
*/
public function disableAction(Request $request)
{
csrf_check_token($request);
$locale = $request->attributes->get('locale');
$errors = array();
// Check if locale exists.
if (!in_array($locale, discover_locales())) {
throw new NotFoundException();
}
// Disable locale if we can do so.
$available_locales = get_available_locales();
if (in_array($locale, $available_locales)) {
if (count($available_locales) > 1) {
disable_locale($locale);
} else {
$errors[] = getlocal('page_locales.cannot_disable_all');
}
}
if (count($errors) != 0) {
// Something went wrong. Re-render locales list.
$request->attributes->set('errors', $errors);
return $this->indexAction($request);
}
return $this->redirect($this->generateUrl('locales'));
}
}

View File

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
namespace Mibew\Controller; namespace Mibew\Controller\Localization;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -115,6 +115,7 @@ class TranslationController extends AbstractController
$page['title'] = getlocal('page.translate.title'); $page['title'] = getlocal('page.translate.title');
$page['menuid'] = 'translation'; $page['menuid'] = 'translation';
$page = array_merge($page, prepare_menu($operator)); $page = array_merge($page, prepare_menu($operator));
$page['tabs'] = $this->buildTabs($request);
return $this->render('translations', $page); return $this->render('translations', $page);
} }

View File

@ -63,17 +63,51 @@ function locale_pattern_check($locale)
function get_available_locales() function get_available_locales()
{ {
$list = array(); if (installation_in_progress()) {
$folder = MIBEW_FS_ROOT . '/locales'; // We cannot get info from database during installation, thus we only
if ($handle = opendir($folder)) { // can use discovered locales as available locales.
while (false !== ($file = readdir($handle))) { // TODO: Remove this workaround after installation will be rewritten.
if (locale_pattern_check($file) && is_dir("$folder/$file")) { return discover_locales();
$list[] = $file; }
}
} // Get list of enabled locales from the database.
closedir($handle); $rows = Database::getInstance()->query(
"SELECT code FROM {locale} WHERE enabled = 1",
array(),
array('return_rows' => Database::RETURN_ALL_ROWS)
);
$enabled_locales = array();
foreach ($rows as $row) {
$enabled_locales[] = $row['code'];
}
$fs_locales = discover_locales();
return array_intersect($fs_locales, $enabled_locales);
}
/**
* Returns list of all locales that are present in the file system.
*
* @return array List of locales codes.
*/
function discover_locales()
{
static $list = null;
if (is_null($list)) {
$list = array();
$folder = MIBEW_FS_ROOT . '/locales';
if ($handle = opendir($folder)) {
while (false !== ($file = readdir($handle))) {
if (locale_pattern_check($file) && is_dir("$folder/$file")) {
$list[] = $file;
}
}
closedir($handle);
}
sort($list);
} }
sort($list);
return $list; return $list;
} }
@ -836,3 +870,59 @@ function save_message($locale, $key, $value)
); );
} }
} }
/**
* Enables specified locale.
*
* @param string $locale Locale code according to RFC 5646.
*/
function enable_locale($locale)
{
$db = Database::getInstance();
// Check if the locale exists in the database
list($count) = $db->query(
"SELECT COUNT(*) FROM {locale} WHERE code = :code",
array(':code' => $locale),
array(
'return_rows' => Database::RETURN_ONE_ROW,
'fetch_type' => Database::FETCH_NUM,
)
);
if ($count == 0) {
// The locale does not exist in the database. Create it.
$db->query(
"INSERT INTO {locale} (code, enabled) VALUES (:code, :enabled)",
array(
':code' => $locale,
':enabled' => 1,
)
);
} else {
// The locale exists in the database. Update it.
$db->query(
"UPDATE {locale} SET enabled = :enabled WHERE code = :code",
array(
':enabled' => 1,
':code' => $locale,
)
);
}
}
/**
* Disables specified locale.
*
* @param string $locale Locale code according to RFC 5646.
*/
function disable_locale($locale)
{
Database::getInstance()->query(
"UPDATE {locale} SET enabled = :enabled WHERE code = :code",
array(
':enabled' => 0,
':code' => $locale,
)
);
}

View File

@ -323,6 +323,32 @@ invite:
_controller: Mibew\Controller\InvitationController::inviteAction _controller: Mibew\Controller\InvitationController::inviteAction
_access_check: Mibew\AccessControl\Check\LoggedInCheck _access_check: Mibew\AccessControl\Check\LoggedInCheck
## Locales
locale_disable:
path: /operator/locale/{locale}/disable
defaults:
_controller: Mibew\Controller\Localization\LocaleController::disableAction
_access_check: Mibew\AccessControl\Check\PermissionsCheck
_access_permissions: [CAN_ADMINISTRATE]
requirements:
locale: "[a-z\-]{2,5}"
locale_enable:
path: /operator/locale/{locale}/enable
defaults:
_controller: Mibew\Controller\Localization\LocaleController::enableAction
_access_check: Mibew\AccessControl\Check\PermissionsCheck
_access_permissions: [CAN_ADMINISTRATE]
requirements:
locale: "[a-z\-]{2,5}"
locales:
path: /operator/locale
defaults:
_controller: Mibew\Controller\Localization\LocaleController::indexAction
_access_check: Mibew\AccessControl\Check\PermissionsCheck
_access_permissions: [CAN_ADMINISTRATE]
## Log in ## Log in
login: login:
path: /operator/login path: /operator/login
@ -588,7 +614,7 @@ style_preview:
translation_edit: translation_edit:
path: /operator/translation/{string_id}/edit path: /operator/translation/{string_id}/edit
defaults: defaults:
_controller: Mibew\Controller\TranslationController::showEditFormAction _controller: Mibew\Controller\Localization\TranslationController::showEditFormAction
_access_check: Mibew\AccessControl\Check\PermissionsCheck _access_check: Mibew\AccessControl\Check\PermissionsCheck
_access_permissions: [CAN_ADMINISTRATE] _access_permissions: [CAN_ADMINISTRATE]
requirements: requirements:
@ -598,7 +624,7 @@ translation_edit:
translation_edit_save: translation_edit_save:
path: /operator/translation/{string_id}/edit path: /operator/translation/{string_id}/edit
defaults: defaults:
_controller: Mibew\Controller\TranslationController::submitEditFormAction _controller: Mibew\Controller\Localization\TranslationController::submitEditFormAction
_access_check: Mibew\AccessControl\Check\PermissionsCheck _access_check: Mibew\AccessControl\Check\PermissionsCheck
_access_permissions: [CAN_ADMINISTRATE] _access_permissions: [CAN_ADMINISTRATE]
requirements: requirements:
@ -608,7 +634,7 @@ translation_edit_save:
translations: translations:
path: /operator/translation path: /operator/translation
defaults: defaults:
_controller: Mibew\Controller\TranslationController::indexAction _controller: Mibew\Controller\Localization\TranslationController::indexAction
_access_check: Mibew\AccessControl\Check\PermissionsCheck _access_check: Mibew\AccessControl\Check\PermissionsCheck
_access_permissions: [CAN_ADMINISTRATE] _access_permissions: [CAN_ADMINISTRATE]

View File

@ -679,7 +679,7 @@ msgstr "Your translation is saved."
msgid "page.translate.one" msgid "page.translate.one"
msgstr "Enter your translation." msgstr "Enter your translation."
msgid "page.translate.title" msgid "page.translate.title"
msgstr "Localization wizard" msgstr "Translations"
msgid "page_agent.cannot_modify" msgid "page_agent.cannot_modify"
msgstr "You are not allowed to change this person's profile." msgstr "You are not allowed to change this person's profile."
msgid "page_agent.clear_avatar" msgid "page_agent.clear_avatar"
@ -790,6 +790,26 @@ msgid "page_group.tab.main"
msgstr "General" msgstr "General"
msgid "page_group.tab.members" msgid "page_group.tab.members"
msgstr "Members" msgstr "Members"
msgid "page_locales.actions"
msgstr "Modify"
msgid "page_locales.cannot_disable_all"
msgstr "You cannot disable all locales."
msgid "page_locales.code"
msgstr "Code"
msgid "page_locales.disable.locale"
msgstr "disable"
msgid "page_locales.enable.locale"
msgstr "enable"
msgid "page_locales.intro"
msgstr "On this page you can configure locales which are used in the system"
msgid "page_locales.name"
msgstr "Name"
msgid "page_locales.title"
msgstr "Locales"
msgid "page_localization.tab.locale"
msgstr "Locales"
msgid "page_localization.tab.translation"
msgstr "Translations"
msgid "page_login.error" msgid "page_login.error"
msgstr "Entered login/password is incorrect" msgstr "Entered login/password is incorrect"
msgid "page_login.operator.disabled" msgid "page_login.operator.disabled"

View File

@ -0,0 +1,64 @@
{{#extends "_layout"}}
{{#override "menu"}}{{> _menu}}{{/override}}
{{#override "content"}}
{{l10n "page_locales.intro"}}
<br />
<br />
{{> _errors}}
{{> _tabs}}
<div class="mform">
<div class="formtop">
<div class="formtopi"></div>
</div>
<div class="forminner">
{{! It is just an empty plate under the tabs bar.}}
<br clear="all"/>
</div>
<div class="formbottom">
<div class="formbottomi"></div>
</div>
</div>
<br />
<table class="list">
<thead>
<tr class="header">
<th>{{l10n "page_locales.code"}}</th>
<th>{{l10n "page_locales.name"}}</th>
<th>{{l10n "page_locales.actions"}}</th>
</tr>
</thead>
<tbody>
{{#each localesList}}
<tr>
<td class="notlast">
{{code}}
</td>
<td class="notlast">
{{name}}
</td>
<td>
{{#if isDisabled}}
<a href="{{../mibewRoot}}/operator/locale/{{code}}/enable?{{csrfTokenInUrl}}">{{l10n "page_locales.enable.locale"}}</a>
{{else}}
<a href="{{../mibewRoot}}/operator/locale/{{code}}/disable?{{csrfTokenInUrl}}">{{l10n "page_locales.disable.locale"}}</a>
{{/if}}
</td>
</tr>
{{/each}}
</tbody>
</table>
{{/override}}
{{/extends}}

View File

@ -8,6 +8,7 @@
<br /> <br />
<form name="translateForm" method="get" action="{{mibewRoot}}/operator/translation"> <form name="translateForm" method="get" action="{{mibewRoot}}/operator/translation">
{{> _tabs}}
<div class="mform"> <div class="mform">
<div class="formtop"> <div class="formtop">