Localisation support for dates throughout the front-end using moment.js.
Closes #734 Closes #732
This commit is contained in:
parent
5dced5f990
commit
cd4ff6c4ea
|
@ -13,6 +13,7 @@ use b8\Config;
|
|||
|
||||
/**
|
||||
* Languages Helper Class - Handles loading strings files and the strings within them.
|
||||
*
|
||||
* @package PHPCI\Helper
|
||||
*/
|
||||
class Lang
|
||||
|
@ -23,6 +24,7 @@ class Lang
|
|||
|
||||
/**
|
||||
* Get a specific string from the language file.
|
||||
*
|
||||
* @param $string
|
||||
* @return mixed|string
|
||||
*/
|
||||
|
@ -48,6 +50,7 @@ class Lang
|
|||
|
||||
/**
|
||||
* Get the currently active language.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getLanguage()
|
||||
|
@ -57,7 +60,9 @@ class Lang
|
|||
|
||||
/**
|
||||
* Try and load a language, and if successful, set it for use throughout the system.
|
||||
*
|
||||
* @param $language
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function setLanguage($language)
|
||||
|
@ -73,6 +78,7 @@ class Lang
|
|||
|
||||
/**
|
||||
* Return a list of available languages and their names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getLanguageOptions()
|
||||
|
@ -90,6 +96,7 @@ class Lang
|
|||
|
||||
/**
|
||||
* Get the strings for the currently active language.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getStrings()
|
||||
|
@ -99,6 +106,7 @@ class Lang
|
|||
|
||||
/**
|
||||
* Initialise the Language helper, try load the language file for the user's browser or the configured default.
|
||||
*
|
||||
* @param Config $config
|
||||
*/
|
||||
public static function init(Config $config)
|
||||
|
@ -137,6 +145,7 @@ class Lang
|
|||
|
||||
/**
|
||||
* Load a specific language file.
|
||||
*
|
||||
* @return string[]|null
|
||||
*/
|
||||
protected static function loadLanguage()
|
||||
|
@ -168,4 +177,24 @@ class Lang
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a time tag for localization.
|
||||
*
|
||||
* See http://momentjs.com/docs/#/displaying/format/ for a list of supported formats.
|
||||
*
|
||||
* @param \DateTime $dateTime The dateTime to represent.
|
||||
* @param string $format The moment.js format to use.
|
||||
*
|
||||
* @return string The formatted tag.
|
||||
*/
|
||||
public static function formatDateTime(\DateTime $dateTime, $format = 'lll')
|
||||
{
|
||||
return sprintf(
|
||||
'<time datetime="%s" data-format="%s">%s</time>',
|
||||
$dateTime->format(\DateTime::ISO8601),
|
||||
$format,
|
||||
$dateTime->format(\DateTime::RFC2822)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
?>
|
||||
<li class="time-label">
|
||||
<span class="bg-gray">
|
||||
<?php print $last->format('M j Y'); ?>
|
||||
<?php print Lang::formatDateTime($last, 'll'); ?>
|
||||
</span>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
@ -58,7 +58,7 @@
|
|||
<li>
|
||||
<i class="fa fa-<?php print $build->getProject()->getIcon(); ?> bg-<?php print $color; ?>"></i>
|
||||
<div class="timeline-item">
|
||||
<span class="time"><i class="fa fa-clock-o"></i> <?php print $updated->format('H:i'); ?></span>
|
||||
<span class="time"><i class="fa fa-clock-o"></i> <?php print Lang::formatDateTime($updated, 'LT'); ?></span>
|
||||
<h3 class="timeline-header">
|
||||
<a href="<?php print PHPCI_URL; ?>project/view/<?php print $build->getProjectId(); ?>">
|
||||
<?php print $build->getProject()->getTitle(); ?>
|
||||
|
|
|
@ -27,12 +27,12 @@ foreach($projects as $project):
|
|||
case 2:
|
||||
$successes++;
|
||||
$statuses[] = 'ok';
|
||||
$success = is_null($success) && !is_null($build->getFinished()) ? $build->getFinished()->format('M j Y g:ia') : $success;
|
||||
$success = is_null($success) && !is_null($build->getFinished()) ? Lang::formatDateTime($build->getFinished()) : $success;
|
||||
break;
|
||||
case 3:
|
||||
$failures++;
|
||||
$statuses[] = 'failed';
|
||||
$failure = is_null($failure) && !is_null($build->getFinished()) ? $build->getFinished()->format('M j Y g:ia') : $failure;
|
||||
$failure = is_null($failure) && !is_null($build->getFinished()) ? Lang::formatDateTime($build->getFinished()) : $failure;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ foreach($projects as $project):
|
|||
$message = Lang::get('x_of_x_failed', $failures, $buildCount);
|
||||
|
||||
if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinished())) {
|
||||
$message .= Lang::get('last_successful_build', $lastSuccess->getFinished()->format('M j Y'));
|
||||
$message .= Lang::get('last_successful_build', Lang::formatDateTime($lastSuccess->getFinished()));
|
||||
} else {
|
||||
$message .= Lang::get('never_built_successfully');
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ foreach($projects as $project):
|
|||
$shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount);
|
||||
|
||||
if (!is_null($lastFailure) && !is_null($lastFailure->getFinished())) {
|
||||
$message .= Lang::get('last_failed_build', $lastFailure->getFinished()->format('M j Y'));
|
||||
$message .= Lang::get('last_failed_build', Lang::formatDateTime($lastFailure->getFinished()));
|
||||
} else {
|
||||
$message .= Lang::get('never_failed_build');
|
||||
}
|
||||
|
|
|
@ -275,6 +275,7 @@
|
|||
|
||||
<script src="<?php print PHPCI_URL; ?>assets/js/plugins/daterangepicker/daterangepicker.js" type="text/javascript"></script>
|
||||
<script src="<?php print PHPCI_URL; ?>assets/js/plugins/datepicker/bootstrap-datepicker.js" type="text/javascript"></script>
|
||||
<script src="<?php print PHPCI_URL; ?>assets/js/plugins/datepicker/locales/bootstrap-datepicker.<?php print Lang::getLanguage(); ?>.js" type="text/javascript"></script>
|
||||
<script src="<?php print PHPCI_URL; ?>assets/js/AdminLTE/app.js" type="text/javascript"></script>
|
||||
|
||||
</body>
|
||||
|
|
28
Tests/PHPCI/Helper/LangTest.php
Normal file
28
Tests/PHPCI/Helper/LangTest.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCI\Tests\Helper;
|
||||
|
||||
use DateTime;
|
||||
use PHPCI\Helper\Lang;
|
||||
use Prophecy\PhpUnit\ProphecyTestCase;
|
||||
|
||||
class LangTest extends ProphecyTestCase
|
||||
{
|
||||
public function testLang_UsePassedParameters()
|
||||
{
|
||||
$dateTime = $this->prophesize('DateTime');
|
||||
$dateTime->format(DateTime::ISO8601)->willReturn("ISODATE");
|
||||
$dateTime->format(DateTime::RFC2822)->willReturn("RFCDATE");
|
||||
|
||||
$this->assertEquals('<time datetime="ISODATE" data-format="FORMAT">RFCDATE</time>', Lang::formatDateTime($dateTime->reveal(), 'FORMAT'));
|
||||
}
|
||||
|
||||
public function testLang_UseDefaultFormat()
|
||||
{
|
||||
$dateTime = $this->prophesize('DateTime');
|
||||
$dateTime->format(DateTime::ISO8601)->willReturn("ISODATE");
|
||||
$dateTime->format(DateTime::RFC2822)->willReturn("RFCDATE");
|
||||
|
||||
$this->assertEquals('<time datetime="ISODATE" data-format="lll">RFCDATE</time>', Lang::formatDateTime($dateTime->reveal()));
|
||||
}
|
||||
}
|
|
@ -18,11 +18,16 @@
|
|||
margin: 4px;
|
||||
}
|
||||
|
||||
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar {
|
||||
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar,
|
||||
.daterangepicker.openscenter .ranges, .daterangepicker.openscenter .calendar {
|
||||
float: right;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges {
|
||||
width: 160px;
|
||||
text-align: left;
|
||||
|
@ -41,6 +46,14 @@
|
|||
max-width: 270px;
|
||||
}
|
||||
|
||||
.daterangepicker.show-calendar .calendar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar.single .calendar-date {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar th, .daterangepicker .calendar td {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
white-space: nowrap;
|
||||
|
@ -48,7 +61,8 @@
|
|||
min-width: 32px;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges label {
|
||||
.daterangepicker .daterangepicker_start_input label,
|
||||
.daterangepicker .daterangepicker_end_input label {
|
||||
color: #333;
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
|
@ -66,7 +80,6 @@
|
|||
}
|
||||
|
||||
.daterangepicker .ranges .input-mini {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
color: #555;
|
||||
|
@ -153,6 +166,37 @@
|
|||
content: '';
|
||||
}
|
||||
|
||||
.daterangepicker.openscenter:before {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: inline-block;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
border-left: 7px solid transparent;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
content: '';
|
||||
}
|
||||
|
||||
.daterangepicker.openscenter:after {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: inline-block;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
border-left: 6px solid transparent;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.daterangepicker.opensright:before {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
|
@ -196,7 +240,7 @@
|
|||
color: #999;
|
||||
}
|
||||
|
||||
.daterangepicker td.disabled {
|
||||
.daterangepicker td.disabled, .daterangepicker option.disabled {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
|
@ -211,6 +255,24 @@
|
|||
border-radius: 0;
|
||||
}
|
||||
|
||||
.daterangepicker td.start-date {
|
||||
-webkit-border-radius: 4px 0 0 4px;
|
||||
-moz-border-radius: 4px 0 0 4px;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.daterangepicker td.end-date {
|
||||
-webkit-border-radius: 0 4px 4px 0;
|
||||
-moz-border-radius: 0 4px 4px 0;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.daterangepicker td.start-date.end-date {
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.daterangepicker td.active, .daterangepicker td.active:hover {
|
||||
background-color: #357ebd;
|
||||
border-color: #3071a9;
|
||||
|
@ -239,7 +301,20 @@
|
|||
width: 40%;
|
||||
}
|
||||
|
||||
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.ampmselect {
|
||||
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
|
||||
width: 50px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.daterangepicker_start_input {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.daterangepicker_end_input {
|
||||
float: left;
|
||||
padding-left: 11px
|
||||
}
|
||||
|
||||
.daterangepicker th.month {
|
||||
width: auto;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,16 @@ var PHPCI = {
|
|||
intervals: {},
|
||||
|
||||
init: function () {
|
||||
// Setup the date locale
|
||||
moment.locale(PHPCI_LANGUAGE);
|
||||
|
||||
$(document).ready(function () {
|
||||
// Format datetimes
|
||||
$('time[datetime]').each(function() {
|
||||
var $this = $(this);
|
||||
$this.text(moment(this.dateTime).format($this.data('format') || 'lll'));
|
||||
});
|
||||
|
||||
// Update latest builds every 5 seconds:
|
||||
PHPCI.getBuilds();
|
||||
PHPCI.intervals.getBuilds = setInterval(PHPCI.getBuilds, 5000);
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue