diff --git a/modules/backend/assets/js/october.datetime.js b/modules/backend/assets/js/october.datetime.js new file mode 100644 index 000000000..0bf33b976 --- /dev/null +++ b/modules/backend/assets/js/october.datetime.js @@ -0,0 +1,166 @@ +/* + * Date time converter. + * See moment.js for format options. + * http://momentjs.com/docs/#/displaying/format/ + * + * Usage: + * + * + * + * 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); diff --git a/modules/backend/helpers/Backend.php b/modules/backend/helpers/Backend.php index 7f4a66106..4b49516d1 100644 --- a/modules/backend/helpers/Backend.php +++ b/modules/backend/helpers/Backend.php @@ -1,10 +1,12 @@ 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 ''.e($value).''.PHP_EOL; + } + } diff --git a/modules/backend/layouts/_head.htm b/modules/backend/layouts/_head.htm index d11d5837b..c2be8adcf 100644 --- a/modules/backend/layouts/_head.htm +++ b/modules/backend/layouts/_head.htm @@ -2,6 +2,8 @@ + + diff --git a/modules/backend/widgets/Lists.php b/modules/backend/widgets/Lists.php index 5743d50a7..3403e9fc4 100644 --- a/modules/backend/widgets/Lists.php +++ b/modules/backend/widgets/Lists.php @@ -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( diff --git a/modules/system/helpers/DateTime.php b/modules/system/helpers/DateTime.php index ee03e8125..bf29521f8 100644 --- a/modules/system/helpers/DateTime.php +++ b/modules/system/helpers/DateTime.php @@ -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; + } + }