Introduce a new localized date control
Added a helped Backend::DateTime() for rendering the appropriate HTML output as a <time /> tag The meta now specifies the locale and timezone preference used here Lists now take advantage of this to display dates relative to the timezone and language (awesome sauce) @todo Still need to get apply this logic to the datepicker form widget
This commit is contained in:
parent
4df7c6704e
commit
02165a8a4a
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Date time converter.
|
||||
* See moment.js for format options.
|
||||
* http://momentjs.com/docs/#/displaying/format/
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <time
|
||||
* data-datetime-control
|
||||
* datetime="2014-11-19 01:21:57"
|
||||
* data-format="dddd Do [o]f MMMM YYYY hh:mm:ss A"
|
||||
* data-timezone="Australia/Sydney"
|
||||
* data-locale="en-au">This text will be replaced</time>
|
||||
*
|
||||
* Alias options:
|
||||
*
|
||||
* time -> 6:28 AM
|
||||
* timeLong -> 6:28:01 AM
|
||||
* date -> 04/23/2016
|
||||
* dateMin -> 4/23/2016
|
||||
* dateLong -> April 23, 2016
|
||||
* dateLongMin -> Apr 23, 2016
|
||||
* dateTime -> April 23, 2016 6:28 AM
|
||||
* dateTimeMin -> Apr 23, 2016 6:28 AM
|
||||
* dateTimeLong -> Saturday, April 23, 2016 6:28 AM
|
||||
* dateTimeLongMin -> Sat, Apr 23, 2016 6:29 AM
|
||||
*
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
var Base = $.oc.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var DateTimeConverter = function (element, options) {
|
||||
this.$el = $(element)
|
||||
this.options = options || {}
|
||||
|
||||
$.oc.foundation.controlUtils.markDisposable(element)
|
||||
Base.call(this)
|
||||
this.init()
|
||||
}
|
||||
|
||||
DateTimeConverter.prototype = Object.create(BaseProto)
|
||||
DateTimeConverter.prototype.constructor = DateTimeConverter
|
||||
|
||||
DateTimeConverter.prototype.init = function() {
|
||||
|
||||
this.initDefaults()
|
||||
|
||||
this.$el.text(this.getDateTimeValue())
|
||||
|
||||
this.$el.one('dispose-control', this.proxy(this.dispose))
|
||||
}
|
||||
|
||||
DateTimeConverter.prototype.initDefaults = function() {
|
||||
if (!this.options.timezone) {
|
||||
this.options.timezone = $('meta[name="backend-timezone"]').attr('content')
|
||||
}
|
||||
|
||||
if (!this.options.locale) {
|
||||
this.options.locale = $('meta[name="backend-locale"]').attr('content')
|
||||
}
|
||||
|
||||
if (!this.options.format) {
|
||||
this.options.format = 'llll'
|
||||
}
|
||||
|
||||
if (this.options.formatAlias) {
|
||||
this.options.format = this.getFormatFromAlias(this.options.formatAlias)
|
||||
}
|
||||
}
|
||||
|
||||
DateTimeConverter.prototype.getDateTimeValue = function() {
|
||||
this.datetime = this.$el.attr('datetime')
|
||||
var momentObj = moment(this.datetime),
|
||||
result
|
||||
|
||||
if (this.options.locale) {
|
||||
moment.locale(this.options.locale)
|
||||
}
|
||||
|
||||
if (this.options.timezone) {
|
||||
momentObj = momentObj.tz(this.options.timezone)
|
||||
}
|
||||
|
||||
if (this.options.timeSince) {
|
||||
result = momentObj.fromNow()
|
||||
}
|
||||
else if (this.options.timeTense) {
|
||||
result = momentObj.calendar()
|
||||
}
|
||||
else {
|
||||
result = momentObj.format(this.options.format)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
DateTimeConverter.prototype.getFormatFromAlias = function(alias) {
|
||||
var map = {
|
||||
time: 'LT',
|
||||
timeLong: 'LTS',
|
||||
date: 'L',
|
||||
dateMin: 'l',
|
||||
dateLong: 'LL',
|
||||
dateLongMin: 'll',
|
||||
dateTime: 'LLL',
|
||||
dateTimeMin: 'lll',
|
||||
dateTimeLong: 'LLLL',
|
||||
dateTimeLongMin: 'llll'
|
||||
}
|
||||
|
||||
return map[alias] ? map[alias] : 'llll'
|
||||
}
|
||||
|
||||
DateTimeConverter.prototype.dispose = function() {
|
||||
this.$el.off('dispose-control', this.proxy(this.dispose))
|
||||
this.$el.removeData('oc.dateTimeConverter')
|
||||
|
||||
this.$el = null
|
||||
this.options = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
DateTimeConverter.DEFAULTS = {
|
||||
format: null,
|
||||
formatAlias: null,
|
||||
timezone: null,
|
||||
locale: null,
|
||||
timeTense: false,
|
||||
timeSince: false
|
||||
}
|
||||
|
||||
// PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.dateTimeConverter
|
||||
|
||||
$.fn.dateTimeConverter = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1), items, result
|
||||
|
||||
items = this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.dateTimeConverter')
|
||||
var options = $.extend({}, DateTimeConverter.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.dateTimeConverter', (data = new DateTimeConverter(this, options)))
|
||||
if (typeof option == 'string') result = data[option].apply(data, args)
|
||||
if (typeof result != 'undefined') return false
|
||||
})
|
||||
|
||||
return result ? result : items
|
||||
}
|
||||
|
||||
$.fn.dateTimeConverter.Constructor = DateTimeConverter
|
||||
|
||||
$.fn.dateTimeConverter.noConflict = function () {
|
||||
$.fn.dateTimeConverter = old
|
||||
return this
|
||||
}
|
||||
|
||||
// Add this only if required
|
||||
$(document).render(function (){
|
||||
$('time[data-datetime-control]').dateTimeConverter()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
<?php namespace Backend\Helpers;
|
||||
|
||||
use Url;
|
||||
use Html;
|
||||
use Config;
|
||||
use Request;
|
||||
use Redirect;
|
||||
use October\Rain\Router\Helper as RouterHelper;
|
||||
use System\Helpers\DateTime as DateTimeHelper;
|
||||
use Backend\Classes\Skin;
|
||||
|
||||
/**
|
||||
|
|
@ -79,4 +81,48 @@ class Backend
|
|||
{
|
||||
return Redirect::intended($this->uri() . '/' . $path, $status, $headers, $secure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML for a date formatted in the backend.
|
||||
*/
|
||||
public function dateTime($dateTime, $value = '', $options = [])
|
||||
{
|
||||
extract(array_merge([
|
||||
'format' => null,
|
||||
'formatAlias' => null,
|
||||
'jsFormat' => null,
|
||||
'timeTense' => false,
|
||||
'timeSince' => false,
|
||||
], $options));
|
||||
|
||||
$carbon = DateTimeHelper::makeCarbon($dateTime);
|
||||
|
||||
if ($jsFormat !== null) {
|
||||
$format = $jsFormat;
|
||||
}
|
||||
else {
|
||||
$format = DateTimeHelper::momentFormat($format);
|
||||
}
|
||||
|
||||
$attributes = [
|
||||
'datetime' => $carbon,
|
||||
'data-datetime-control' => 1,
|
||||
];
|
||||
|
||||
if ($timeTense) {
|
||||
$attributes['data-time-tense'] = 1;
|
||||
}
|
||||
elseif ($timeSince) {
|
||||
$attributes['data-time-since'] = 1;
|
||||
}
|
||||
elseif ($format) {
|
||||
$attributes['data-format'] = $format;
|
||||
}
|
||||
elseif ($formatAlias) {
|
||||
$attributes['data-format-alias'] = $formatAlias;
|
||||
}
|
||||
|
||||
return '<time'.Html::attributes($attributes).'>'.e($value).'</time>'.PHP_EOL;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0, minimal-ui">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="backend-base-path" content="<?= Backend::baseUrl() ?>">
|
||||
<meta name="backend-timezone" content="<?= e(Backend\Models\Preferences::get('timezone')) ?>">
|
||||
<meta name="backend-locale" content="<?= e(Backend\Models\Preferences::get('locale')) ?>">
|
||||
<meta name="csrf-token" content="<?= csrf_token() ?>">
|
||||
<link rel="icon" type="image/png" href="<?= Backend::skinAsset('assets/images/favicon.png') ?>">
|
||||
<title data-title-template="<?= empty($this->pageTitleTemplate) ? '%s' : e($this->pageTitleTemplate) ?> | <?= e(Backend\Models\BrandSettings::get('app_name')) ?>">
|
||||
|
|
|
|||
|
|
@ -913,13 +913,19 @@ class Lists extends WidgetBase
|
|||
return null;
|
||||
}
|
||||
|
||||
$value = $this->validateDateTimeValue($value, $column);
|
||||
$dateTime = $this->validateDateTimeValue($value, $column);
|
||||
|
||||
if ($column->format !== null) {
|
||||
return $value->format($column->format);
|
||||
$value = $dateTime->format($column->format);
|
||||
}
|
||||
else {
|
||||
$value = $dateTime->toDayDateTimeString();
|
||||
}
|
||||
|
||||
return $value->toDayDateTimeString();
|
||||
return Backend::dateTime($dateTime, $value, [
|
||||
'format' => $column->format,
|
||||
'formatAlias' => 'dateTimeLongMin'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -931,13 +937,16 @@ class Lists extends WidgetBase
|
|||
return null;
|
||||
}
|
||||
|
||||
$value = $this->validateDateTimeValue($value, $column);
|
||||
$dateTime = $this->validateDateTimeValue($value, $column);
|
||||
|
||||
if ($column->format === null) {
|
||||
$column->format = 'g:i A';
|
||||
}
|
||||
$format = $column->format !== null ? $column->format : 'g:i A';
|
||||
|
||||
return $value->format($column->format);
|
||||
$value = $dateTime->format($format);
|
||||
|
||||
return Backend::dateTime($dateTime, $value, [
|
||||
'format' => $column->format,
|
||||
'formatAlias' => 'time'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -949,13 +958,19 @@ class Lists extends WidgetBase
|
|||
return null;
|
||||
}
|
||||
|
||||
$value = $this->validateDateTimeValue($value, $column);
|
||||
$dateTime = $this->validateDateTimeValue($value, $column);
|
||||
|
||||
if ($column->format !== null) {
|
||||
return $value->format($column->format);
|
||||
$value = $dateTime->format($column->format);
|
||||
}
|
||||
else {
|
||||
$value = $dateTime->toFormattedDateString();
|
||||
}
|
||||
|
||||
return $value->toFormattedDateString();
|
||||
return Backend::dateTime($dateTime, $value, [
|
||||
'format' => $column->format,
|
||||
'formatAlias' => 'dateLongMin'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -967,9 +982,13 @@ class Lists extends WidgetBase
|
|||
return null;
|
||||
}
|
||||
|
||||
$value = $this->validateDateTimeValue($value, $column);
|
||||
$dateTime = $this->validateDateTimeValue($value, $column);
|
||||
|
||||
return DateTimeHelper::timeSince($value);
|
||||
$value = DateTimeHelper::timeSince($dateTime);
|
||||
|
||||
return Backend::dateTime($dateTime, $value, [
|
||||
'timeSince' => true
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -981,9 +1000,13 @@ class Lists extends WidgetBase
|
|||
return null;
|
||||
}
|
||||
|
||||
$value = $this->validateDateTimeValue($value, $column);
|
||||
$dateTime = $this->validateDateTimeValue($value, $column);
|
||||
|
||||
return DateTimeHelper::timeTense($value);
|
||||
$value = DateTimeHelper::timeTense($dateTime);
|
||||
|
||||
return Backend::dateTime($dateTime, $value, [
|
||||
'timeTense' => true
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -991,7 +1014,7 @@ class Lists extends WidgetBase
|
|||
*/
|
||||
protected function validateDateTimeValue($value, $column)
|
||||
{
|
||||
$value = DateTimeHelper::instance()->makeCarbon($value, false);
|
||||
$value = DateTimeHelper::makeCarbon($value, false);
|
||||
|
||||
if (!$value instanceof Carbon) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ use InvalidArgumentException;
|
|||
|
||||
class DateTime
|
||||
{
|
||||
use \October\Rain\Support\Traits\Singleton;
|
||||
|
||||
/**
|
||||
* Returns a human readable time difference from the value to the
|
||||
* current time. Eg: **10 minutes ago**
|
||||
|
|
@ -18,10 +16,7 @@ class DateTime
|
|||
*/
|
||||
public static function timeSince($datetime)
|
||||
{
|
||||
return self::instance()
|
||||
->makeCarbon($datetime)
|
||||
->diffForHumans()
|
||||
;
|
||||
return self::makeCarbon($datetime)->diffForHumans();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -33,7 +28,7 @@ class DateTime
|
|||
*/
|
||||
public static function timeTense($datetime)
|
||||
{
|
||||
$datetime = self::instance()->makeCarbon($datetime);
|
||||
$datetime = self::makeCarbon($datetime);
|
||||
$yesterday = $datetime->subDays(1);
|
||||
$tomorrow = $datetime->addDays(1);
|
||||
$time = $datetime->format('H:i');
|
||||
|
|
@ -57,7 +52,7 @@ class DateTime
|
|||
*
|
||||
* @return Carbon\Carbon
|
||||
*/
|
||||
public function makeCarbon($value, $throwException = true)
|
||||
public static function makeCarbon($value, $throwException = true)
|
||||
{
|
||||
if ($value instanceof Carbon) {
|
||||
// Do nothing
|
||||
|
|
@ -78,4 +73,60 @@ class DateTime
|
|||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a PHP date format to "Moment.js" format.
|
||||
* @param string $format
|
||||
* @return string
|
||||
*/
|
||||
public static function momentFormat($format)
|
||||
{
|
||||
$replacements = [
|
||||
'd' => 'DD',
|
||||
'D' => 'ddd',
|
||||
'j' => 'D',
|
||||
'l' => 'dddd',
|
||||
'N' => 'E',
|
||||
'S' => 'o',
|
||||
'w' => 'e',
|
||||
'z' => 'DDD',
|
||||
'W' => 'W',
|
||||
'F' => 'MMMM',
|
||||
'm' => 'MM',
|
||||
'M' => 'MMM',
|
||||
'n' => 'M',
|
||||
't' => '', // no equivalent
|
||||
'L' => '', // no equivalent
|
||||
'o' => 'YYYY',
|
||||
'Y' => 'YYYY',
|
||||
'y' => 'YY',
|
||||
'a' => 'a',
|
||||
'A' => 'A',
|
||||
'B' => '', // no equivalent
|
||||
'g' => 'h',
|
||||
'G' => 'H',
|
||||
'h' => 'hh',
|
||||
'H' => 'HH',
|
||||
'i' => 'mm',
|
||||
's' => 'ss',
|
||||
'u' => 'SSS',
|
||||
'e' => 'zz', // deprecated since version 1.6.0 of moment.js
|
||||
'I' => '', // no equivalent
|
||||
'O' => '', // no equivalent
|
||||
'P' => '', // no equivalent
|
||||
'T' => '', // no equivalent
|
||||
'Z' => '', // no equivalent
|
||||
'c' => '', // no equivalent
|
||||
'r' => '', // no equivalent
|
||||
'U' => 'X',
|
||||
];
|
||||
|
||||
foreach ($replacements as $from => $to) {
|
||||
$replacements['\\'.$from] = '['.$from.']';
|
||||
}
|
||||
|
||||
$momentFormat = strtr($format, $replacements);
|
||||
return $momentFormat;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue