Spade
Mini Shell
| Directory:~$ /proc/self/root/home/lmsyaran/public_html/joomla5/plugins/fabrik_element/jdate/ |
| [Home] [System Details] [Kill Me] |
<?php
/**
* Plugin element to render date picker
*
* @package Joomla.Plugin
* @subpackage Fabrik.element.date
* @copyright Copyright (C) 2005-2020 Media A-Team, Inc. - All rights
reserved.
* @license GNU/GPL http://www.gnu.org/copyleft/gpl.html
*/
// No direct access
defined('_JEXEC') or die('Restricted access');
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutInterface;
use Joomla\CMS\Date\Date;
use Joomla\CMS\Profiler\Profiler;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\Utilities\ArrayHelper;
use Joomla\String\StringHelper;
use Fabrik\Helpers\Php;
/**
* Plugin element to render date picker
*
* @package Joomla.Plugin
* @subpackage Fabrik.element.date
* @since 3.0
*/
class PlgFabrik_ElementJdate extends PlgFabrik_ElementList
{
/**
* States the element should be ignored from advanced search all queries.
*
* @var bool True, ignore in extended search all.
*/
protected $ignoreSearchAllDefault = true;
/**
* Toggle to determine if storeDatabaseFormat resets the date to GMT
*
* @var bool
*/
protected $resetToGMT = true;
/**
* Is the element a ranged filter (can depend on request data)
*
* @var bool
*/
protected $rangeFilterSet = false;
/**
* Date offset with TZ, for use in front end display
*
* @var string
*/
protected $offsetDate = null;
/**
* Does the element contain sub elements e.g checkboxes radiobuttons
*
* @var bool
*/
public $hasSubElements = false;
/**
* Shows the data formatted for the list view
*
* @param string $data Elements data
* @param stdClass &$thisRow All the data in the lists current row
* @param array $opts Rendering options
*
* @return string formatted value
*/
public function renderListData($data, stdClass &$thisRow, $opts =
array())
{
$profiler = Profiler::getInstance('Application');
JDEBUG ? $profiler->mark("renderListData:
{$this->element->plugin}: start: {$this->element->name}")
: null;
if ($data == '')
{
return parent::renderListData($data, $thisRow, $opts);
}
// @TODO: deal with time options (currently can be defined in
jdate_table_format param).
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$params = $this->getParams();
$data = FabrikWorker::JSONtoData($data, true);
$f = $params->get('jdate_table_format',
'Y-m-d');
$format = array();
if (strstr($f, '%'))
{
$f = FabDate::strftimeFormatToDateFormat($f);
}
foreach ($data as $d)
{
if (FabrikWorker::isDate($d))
{
$date = Factory::getDate($d);
$view = FArrayHelper::getValue($opts, 'view',
'list');
if ($this->shouldApplyTz($view))
{
$date->setTimeZone($timeZone);
}
if ($f == '{age}')
{
$format[] = date('Y') - $date->format('Y',
true);
}
else
{
$format[] = $date->format($f, true);
}
}
else
{
$format[] = '';
}
}
$data = json_encode($format);
return parent::renderListData($data, $thisRow, $opts);
}
/**
* Prepares the element data for CSV export
*
* @param string $data Element data
* @param object &$thisRow All the data in the lists current row
*
* @return string formatted value
*/
public function renderListData_csv($data, &$thisRow)
{
// @TODO: deal with time options (currently can be defined in
jdate_table_format param).
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$params = $this->getParams();
$this->getGroup();
$data = FabrikWorker::JSONtoData($data, true);
$f = $params->get('jdate_table_format',
'Y-m-d');
/* $$$ hugh - see http://fabrikar.com/forums/showthread.php?p=87507
* Really don't think we need to worry about $app->input
'incraw' here. The raw, GMT/MySQL data will get
* included in the _raw version of the element if incraw is selected.
Here we just want to output
* the regular non-raw, formatted, TZ'ed version.
*/
$incRaw = false;
$format = array();
foreach ($data as $d)
{
if (FabrikWorker::isDate($d))
{
if ($incRaw)
{
$format[] = $d;
}
else
{
$date = Factory::getDate($d);
if ($this->shouldApplyTz('list'))
{
$date->setTimeZone($timeZone);
}
if ($f == '{age}')
{
$format[] = date('Y') - $date->format('Y',
true);
}
else
{
$format[] = $date->format($f, true);
}
}
}
else
{
$format[] = '';
}
}
if (count($format) > 1)
{
return json_encode($format);
}
else
{
return implode('', $format);
}
}
/**
* Used in things like date when its id is suffixed with _cal
* called from getLabel();
*
* @param string &$id Initial id
*
* @return void
*/
protected function modHTMLId(&$id)
{
$id = $id . '_cal';
}
/**
* Called by form model to build an array of values to encrypt
*
* Dates should have tz applied if needed - their value should be the
* same as the displayed readonly date.
*
* @param array &$values Previously encrypted values
* @param array $data Form data
* @param int $c Repeat group counter
*
* @return void
*/
public function getValuesToEncrypt(&$values, $data, $c)
{
$name = $this->getFullName(true, false);
$group = $this->getGroup();
if ($group->canRepeat())
{
if (!array_key_exists($name, $values))
{
$values[$name]['data'] = array();
}
$gmt = $this->getValue($data, $c);
$localDate = $this->displayDate($gmt);
$values[$name]['data'][$c] = FabrikWorker::isDate($gmt) ?
$localDate->toSql(true) : '';
}
else
{
$gmt = $this->getValue($data, $c);
$localDate = $this->displayDate($gmt);
$values[$name]['data'] = FabrikWorker::isDate($gmt) ?
$localDate->toSql(true) : '';
}
}
/**
* Draws the html form element
*
* @param array $data To pre-populate element with
* @param int $repeatCounter Repeat group counter
*
* @return string elements html
*/
public function render($data, $repeatCounter = 0)
{
$this->offsetDate = '';
$name = $this->getHTMLName($repeatCounter);
$id = $this->getHTMLId($repeatCounter);
$params = $this->getParams();
$element = $this->getElement();
$format = $params->get('jdate_form_format',
$params->get('jdate_table_format', 'Y-m-d'));
if (strstr($format, '%'))
{
$format = FabDate::strftimeFormatToDateFormat($format);
}
$timeFormat = $this->getTimeFormat();
// Value is in mySQL format GMT
$gmt = $this->getValue($data, $repeatCounter);
if (!FabrikWorker::isDate($gmt))
{
$date = '';
$time = '';
}
else
{
// Set the date to display local time
$localDate = $this->displayDate($gmt);
// Get the formatted time
$time = ($params->get('jdate_showtime', 0)) ? ' '
. $localDate->format($timeFormat, true) : '';
// Formatted date
$date = $localDate->format($format, true);
}
if (!$this->isEditable())
{
return $date . $time;
}
// Build HTML widget
if ($params->get('jdate_showtime', 0) &&
!$element->hidden)
{
// Can't have names as simply [] as json only picks up the last one
$timeElName = $name . '[time]';
$name .= '[date]';
}
$class = 'fabrikinput inputbox input ' .
$params->get('bootstrap_class', 'col-sm-6');
$element->width = (int) $element->width < 0 ? 1 : (int)
$element->width;
$calOpts = array('class' => $class, 'size'
=> $element->width, 'maxlength' => '19');
if ($params->get('jdate_allow_typing_in_field', true) ==
false)
{
//$calOpts['readonly'] = 'readonly';
}
if ($placeholder = $params->get('placeholder'))
{
$calOpts['placeholder'] = Text::_($placeholder);
}
if ($params->get('jdate_showtime', '0'))
{
$calOpts['showTime'] = '1';
$calOpts['timeFormat'] =
$params->get('jdate_time_24', '1') ? '24'
: '12';
$format .= ' ' . $timeFormat;
}
// J!4.4.0 issue breaks the time picker if 24h enabled; showing week
number will make it work again
$calOpts['weekNumbers'] = true;
//$params->get('jdate_show_week_numbers', '0') ===
'1';
$str[] = '<div class="fabrikSubElementContainer"
id="' . $id . '">';
$str[] = $this->calendar($this->offsetDate, $name, $id .
'_cal', $format, $calOpts, $repeatCounter);
$str[] = '</div>';
return implode("\n", $str);
}
/**
* Render time button
*
* @param string $timeElName Element name
* @param string $time Time value
* @param array &$str Markup to append time button to
*
* @since 3.1b2
*
* @return void
*/
protected function timeButton($timeElName, $time, &$str)
{
$params = $this->getParams();
$timeFormat = $params->get('jdate_time_format',
'H:i');
$class = 'inputbox fabrikinput timeField input ' .
$params->get('bootstrap_time_class', 'col-sm-2');
$readOnly = $params->get('jdate_allow_typing_in_field',
true) == false ? ' readonly="readonly" ' :
'';
$str[] = '<div class="input-append">';
$timeLength = StringHelper::strlen($timeFormat);
FabrikHelperHTML::addPath(COM_FABRIK_BASE .
'plugins/fabrik_element/date/images/', 'image',
'form', false);
$str[] = '<input type="text" class="' .
$class . '" ' . $readOnly . ' size="' .
$timeLength . '" value="' . $time . '"
name="'
. $timeElName . '" />';
$opts = array('alt' =>
Text::_('PLG_ELEMENT_JDATE_TIME'), 'class' =>
'timeButton');
$file = 'clock';
$btnLayout = FabrikHelperHTML::getLayout('fabrik-button');
$layoutData = (object) array(
'class' => 'timeButton',
'label' => FabrikHelperHTML::image($file, 'form',
@$this->tmpl, $opts),
'aria' => ' aria-label="' .
Text::_('PLG_ELEMENT_JDATE_ARIA_LABEL_TIME') . '"'
);
$str[] = $btnLayout->render($layoutData);
if ($j3)
{
$str[] = '</div>';
}
}
/**
* Create the local datetime
*
* @param string $gmt Datetime
*
* @since 3.0.9
*
* @return Date
*/
protected function displayDate($gmt)
{
$date = Factory::getDate($gmt);
if ($this->shouldApplyTz())
{
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$date->setTimeZone($timeZone);
}
$this->offsetDate = $date->toSql(true);
return $date;
}
/**
* Should we apply the users timezone offset to the date for display
purposes
*
* This is a bit long winded and could be reduced. BUT the logic is such
that I think the
* verbosity maintains the readability of the logic
*
* @param string $view Component view [list/form]
*
* @since 3.0.9
*
* @return boolean
*/
protected function shouldApplyTz($view = 'form')
{
// if formatting for plugins (getProcessData), TZ has already been dealt
with
if ($view == 'email')
{
return false;
}
$formModel = $this->getFormModel();
$params = $this->getParams();
$storeAsLocal = (bool) $params->get('jdate_store_as_local',
0);
$newRecord = $formModel->isNewRecord();
$alwaysToday = $params->get('jdate_alwaystoday', false);
$defaultToday = $params->get('jdate_defaulttotoday');
if ($storeAsLocal)
{
if ($view === 'list')
{
return false;
}
if ($newRecord)
{
if (($alwaysToday || $defaultToday) || $formModel->hasErrors())
//if (($alwaysToday || $defaultToday))
{
// User supplied defaults should be in GMT, they are only applied if
no other default found.
return true;
}
}
if (!$newRecord)
{
return false;
}
}
else
{
if ($newRecord)
{
if (($alwaysToday || $defaultToday) || $formModel->hasErrors() ||
$view === 'list')
{
// User supplied defaults should be in GMT, they are only applied if
no other default found.
return true;
}
}
if (!$newRecord)
{
return true;
}
}
return false;
}
/**
* Individual store database format
*
* @param string $val Value
*
* @return string MySQL formatted date
*/
private function _indStoreDBFormat($val)
{
$params = $this->getParams();
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$storeAsLocal = (bool) $params->get('jdate_store_as_local',
false);
$alwaysToday = $params->get('jdate_alwaystoday', false);
if ($alwaysToday)
{
if (!$storeAsLocal)
{
$val = Factory::getDate('now');
}
else
{
$val = Factory::getDate('now', $timeZone);
}
$val = $val->toSql(true);
return $val;
}
// $$$ hugh - sometimes still getting $val as an array with date and
time,
// like on AJAX submissions? Or maybe from getEmailData()? Or both?
if (is_array($val))
{
$val = FArrayHelper::getValue($val, 'date', '');
}
else
{
$val = empty($val) ? $val : urldecode($val);
}
if (!FabrikWorker::isDate($val))
{
return null;
}
jimport('joomla.utilities.date');
$listModel = $this->getListModel();
// $$$ hugh - offset_tz of 1 means 'in MySQL format, GMT'
// $$$ hugh - offset_tz of 2 means 'in MySQL format, Local TZ'
if ($listModel->importingCSV &&
$params->get('jdate_csv_offset_tz', '0') ==
'1')
{
return $val;
}
elseif ($listModel->importingCSV &&
$params->get('jdate_csv_offset_tz', '0') ==
'2')
{
return $this->toMySQLGMT(Factory::getDate($val));
}
// $$$ rob - as the date js code formats to the db format - just return
the value.
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$val = Factory::getDate($val, $timeZone)->toSql($storeAsLocal);
return $val;
}
/**
* reset the date to GMT - reversing the offset
*
* @param object $date Date to convert
*
* @return string MySQL formatted GMT date
*/
protected function toMySQLGMT($date)
{
if ($this->resetToGMT)
{
// $$$ rob 3.0 offset is no longer an integer but a timezone string
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$hours = $timeZone->getOffset($date) / (60 * 60);
$invert = false;
if ($hours < 0)
{
$invert = true;
// Intervals can only be positive - set invert property
$hours = $hours * -1;
}
// 5.3 only
if (class_exists('DateInterval'))
{
// need to use minutes, as DateInterval will barf on fractional hours
like 5.5 (India)
$dateInterval = new DateInterval('PT' . $hours * 60 .
'M');
$dateInterval->invert = $invert;
$date->sub($dateInterval);
}
else
{
$date->modify('+' . $hours . ' hour');
}
return $date->toSql(true);
}
return $date->toSql();
}
/**
* Manipulates posted form data for insertion into database
*
* @param mixed $val This elements posted form data
* @param array $data Posted form data
*
* @return mixed
*/
public function storeDatabaseFormat($val, $data)
{
if (!is_array($val))
{
/* $$$ hugh - we really need to work out why some AJAX data is not
getting urldecoded.
* but for now ... a bandaid.
*/
$val = empty($val) ? $val : urldecode($val);
}
// @TODO: deal with failed validations
$groupModel = $this->getGroup();
if ($groupModel->isJoin() && is_array($val))
{
$val = FArrayHelper::getValue($val, 'date', null);
}
else
{
if ($groupModel->canRepeat())
{
if (is_array($val))
{
$res = array();
foreach ($val as $v)
{
$res[] = $this->_indStoreDBFormat($v);
}
return json_encode($res);
}
}
}
return $this->_indStoreDBFormat($val);
}
/**
* Used to format the data when shown in the form's email
*
* @param mixed $value Element's data
* @param array $data Form records data
* @param int $repeatCounter Repeat group counter
*
* @return string formatted value
*/
public function getEmailValue($value, $data = array(), $repeatCounter = 0)
{
if (
(is_array($value) && empty($value))
|| (
is_array($value)
&& FArrayHelper::getValue($value, 'date',
'') === ''
&& FArrayHelper::getValue($value, 'time',
'') === ''
)
|| (!is_array($value) && trim($value) === '')
)
{
return '';
}
$groupModel = $this->getGroup();
if ($groupModel->isJoin() && $groupModel->canRepeat())
{
$value = $value[$repeatCounter];
}
if (is_array($value))
{
$date = FArrayHelper::getValue($value, 'date');
$d = Factory::getDate($date);
$time = FArrayHelper::getValue($value, 'time', '');
if ($time !== '')
{
$bits = explode(':', $time);
$h = FArrayHelper::getValue($bits, 0, 0);
$m = FArrayHelper::getValue($bits, 1, 0);
$s = FArrayHelper::getValue($bits, 2, 0);
$d->setTime($h, $m, $s);
}
$value = $d->toSql();
}
// $$$ hugh - need to convert to database format so we GMT-ified date
$dummy = new stdClass;
$opts = array(
'view' => 'email'
);
return $this->renderListData($value, $dummy, $opts);
/* $$$ rob - no need to covert to db format now as its posted as db
format already.
*return $this->renderListData($this->storeDatabaseFormat($value,
$data), new stdClass);
*/
}
/**
* Determines the label used for the browser title
* in the form/detail views
*
* @param array $data Form data
* @param int $repeatCounter When repeating joined groups we need to
know what part of the array to access
* @param array $opts Options
*
* @return string Text to add to the browser's title
*/
public function getTitlePart($data, $repeatCounter = 0, $opts = array())
{
$gmt_date = $this->getValue($data, $repeatCounter, $opts);
/* OK, now we've got the GMT date, convert it
* ripped the following off from renderListData ... SURELY we must have a
func
* somewhere that does this?
*/
$params = $this->getParams();
$storeAsLocal = (int) $params->get('jdate_store_as_local',
0);
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$f = $params->get('jdate_table_format',
'Y-m-d');
$tz_date = '';
if (FabrikWorker::isDate($gmt_date))
{
$date = Factory::getDate($gmt_date);
if (!$storeAsLocal)
{
$date->setTimeZone($timeZone);
}
if ($f == '{age}')
{
$tz_date = date('Y') - $date->format('Y', true);
}
else
{
$tz_date = $date->format($f, true);
}
}
return $tz_date;
}
/**
* Converts a raw value into its label equivalent
*
* @param string &$v Raw value
*
* @return void
*/
protected function toLabel(&$v)
{
$params = $this->getParams();
$f = $params->get('jdate_table_format',
'Y-m-d');
$timeZone = new
\DateTimeZone($this->config->get('offset'));
if (FabrikWorker::isDate($v))
{
$date = Factory::getDate($v);
/**
* $$$ rob - if not time selector then the date gets stored as
2009-11-13 00:00:00
* if we have a -1 timezone then date gets set to 2009-11-12 23:00:00
* then shown as 2009-11-12 which is wrong
*/
if ($params->get('jdate_showtime'))
{
$date->setTimeZone($timeZone);
}
if ($f == '{age}')
{
$v = date('Y') - $date->format('Y', true);
}
else
{
$v = $date->format($f, true);
}
}
else
{
$v = '';
}
}
/**
* Displays a calendar control field
*
* hacked from behaviour as you need to check if the element exists
* it might not as you could be using a custom template
*
* @param string $value The date value (must be in the same
format as supplied by $format)
* @param string $name The name of the text field
* @param string $id The id of the text field
* @param string $format The date format (not used)
* @param array $attribs Additional html attributes
* @param int $repeatCounter Repeat group counter (not used)
*
* @return string
*/
public function calendar($value, $name, $id, $format = 'Y-m-d',
$attribs = null, $repeatCounter = 0)
{
if (!strstr($format, '%'))
{
$format = FabDate::dateFormatToStrftimeFormat($format);
}
//$attribs['onChange'] = 'Fabrik.calSelect()';
return HTMLHelper::calendar($value, $name, $id, $format, $attribs);
}
/**
* get the options used for the date elements calendar
*
* @param int $id Repeat counter
*
* @return object ready for js encoding
*/
protected function _CalendarJSOpts($id)
{
$params = $this->getParams();
$opts = new stdClass;
$opts->inputField = $id;
$opts->button = $id . "_btn";
$opts->align = "Tl";
$opts->singleClick = true;
$opts->firstDay =
intval($params->get('jdate_firstday'));
$format = $params->get('jdate_form_format',
$params->get('jdate_table_format', 'Y-m-d'));
if (strstr($format, '%'))
{
$format = FabDate::strftimeFormatToDateFormat($format);
}
$timeFormat = $this->getTimeFormat();
if (!empty($timeFormat))
{
$format .= ' ' . $timeFormat;
}
$opts->timeFormat = 24;
$opts->ifFormat = FabDate::dateFormatToStrftimeFormat($format);
$opts->dateAllowFunc = $params->get('jdate_allow_func');
return $opts;
}
/**
* Returns javascript which creates an instance of the class defined in
formJavascriptClass()
*
* @param int $repeatCounter Repeat group counter
*
* @return array
*/
public function elementJavascript($repeatCounter)
{
$params = $this->getParams();
$element = $this->getElement();
$id = $this->getHTMLId($repeatCounter);
$opts = $this->getElementJSOptions($repeatCounter);
// if read only, convert back to local display time
if (!$this->isEditable() && !empty($opts->value))
{
$localDate = $this->displayDate($opts->value);
$opts->value = $localDate->toSql(true);
}
$opts->hidden = (bool) $this->getElement()->hidden;
// Used uniquely in reset();
$opts->defaultVal = $this->getFrontDefaultValue();
$opts->showtime = (!$element->hidden &&
$params->get('jdate_showtime', 0)) ? true : false;
$opts->timelabel =
Text::_('PLG_ELEMENT_JDATE_TIME_LABEL', true);
$opts->typing = (bool)
$params->get('jdate_allow_typing_in_field', true);
$opts->timedisplay = $params->get('jdate_timedisplay',
1);
//$opts->dateTimeFormat =
$params->get('jdate_time_format', '');
$opts->allowedDates = $this->getAllowedPHPDates();
$opts->watchElement = $this->getWatchId();
$opts->id = $this->getId();
$opts->locale =
Factory::getApplication()->getLanguage()->getTag();
// For reuse if element is duplicated in repeat group
$opts->calendarSetup = $this->_CalendarJSOpts($id);
$opts->advanced = true;
return array('FbJDateTime', $id, $opts);
}
/**
* Get the HTML id for the watch element
*
* @param int $repeatCounter repeat group counter
*
* @return string
*/
protected function getWatchId($repeatCounter = 0)
{
$elementModel = $this->getWatchElement();
return $elementModel ? $elementModel->getHTMLId($repeatCounter) :
'';
}
/**
* Get the element to watch. Changes to this element will trigger the
cdd's lookup
*
* @return plgFabrik_Element
*/
protected function getWatchElement()
{
if (!isset($this->watchElement))
{
$watch = $this->getParams()->get('jdate_observe',
'');
if ($watch === '')
{
return false;
}
$this->watchElement = $this->getFormModel()->getElement($watch,
true);
if (!$this->watchElement)
{
// This element is a child element, so $watch is in the parent element
(in another form)
$pluginManager = FabrikWorker::getPluginManager();
$parent = $pluginManager->getElementPlugin($watch);
// These are the possible watch elements
$children = $parent->getElementDescendents();
// Match the possible element ids with the current form's element
ids
$elIds = $this->getFormModel()->getElementIds();
$matched = array_values(array_intersect($elIds, $children));
// Load the matched element
$this->watchElement =
$pluginManager->getElementPlugin($matched[0]);
}
}
return $this->watchElement;
}
/**
* Ajax - get the allowed dates - called when watched element changes
*
* @return void
*/
public function onAjax_getAllowedDates()
{
$this->loadMeForAjax();
$dates = $this->getAllowedPHPDates();
echo json_encode($dates);
}
/**
* Get the allowed dates based on evaluated PHP code
*
* @return multitype:|array
*/
protected function getAllowedPHPDates()
{
$params = $this->getParams();
$data = $this->getFormModel()->data;
$php = $params->get('jdate_allow_php_func',
'');
$dates = array();
if ($php === '')
{
return $dates;
}
FabrikWorker::clearEval();
$dates = Php::Eval(['code' => $php,
'vars'=>['data'=>$data]]);
FabrikWorker::logEval($dates, 'Eval exception : ' .
$this->getElement()->name . '::getAllowedPHPDates() : %s');
return (array) $dates;
}
/**
* Get database field description
*
* @return string Db field type
*/
public function getFieldDescription()
{
if ($this->encryptMe())
{
return 'BLOB';
}
return "DATETIME";
}
/**
* Get an array of element html ids and their corresponding
* js events which trigger a validation.
* Examples of where this would be overwritten include timedate element
with time field enabled
*
* @param int $repeatCounter Repeat group counter
*
* @return array html ids to watch for validation
*/
public function getValidationWatchElements($repeatCounter)
{
$return = array();
$id = $this->getHTMLId($repeatCounter);
$return[] = array('id' => $id, 'triggerEvent'
=> 'blur');
return $return;
}
/**
* Element plugin specific method for setting unencrypted values back into
post data
*
* @param array &$post Data passed by ref
* @param string $key Key
* @param string $data Elements unencrypted data
*
* @return void
*/
public function setValuesFromEncryt(&$post, $key, $data)
{
$group = $this->getGroup();
if ($group->canRepeat())
{
foreach ($data as &$d)
{
if (FabrikWorker::isDate($d)) {
// Only apply date logic to actual date data, if blank
for example we should leave blank
$date = Factory::getDate($d);
$date = $date->toSql();
// Put in the correct format
list($dateOnly, $time) = explode(' ', $date);
}
else {
$date = '';
$time = '';
}
$d = array('date' => $date, 'time'
=> $time);
}
}
else
{
$d = $data[0];
if (FabrikWorker::isDate($d)) {
// Only apply date logic to actual date data, if blank for
example we should leave blank
$date = Factory::getDate($d);
$date = $date->toSql();
// Put in the correct format
list($dateOnly, $time) = explode(' ', $date);
}
else {
$date = '';
$time = '';
}
$data = array('date' => $date, 'time'
=> $time);
}
parent::setValuesFromEncryt($post, $key, $data);
}
/**
* Get the GMT Date time - tz offset applied in render() if needed
*
* @param array $data Form data date will be GMT if store as
local OFF, otherwise as local time
* @param int $repeatCounter When repeating joined groups we need to
know what part of the array to access
* @param array $opts Options
*
* @return string value Date as GMT time
*/
public function getValue($data, $repeatCounter = 0, $opts = array())
{
$params = $this->getParams();
$alwaysToday = $params->get('jdate_alwaystoday', false);
$defaultToday = $params->get('jdate_defaulttotoday', false);
$formModel = $this->getFormModel();
$value = parent::getValue($data, $repeatCounter, $opts);
$db = FabrikWorker::getDbo();
if (is_array($value))
{
// Submission posted as array but date & time in date key. Can be
keyed to 0 if parent class casts string to array.
$value = FArrayHelper::getValue($value, 'date',
FArrayHelper::getValue($value, 0));
}
// in some corner cases, date will be db name quoted, like in CSV export
after an advanced search!
$value = trim($value??"", "'");
$value = $value == "" ? null : $value;
//if ($input->get('task') == 'form.process' ||
($app->isClient('administrator') &&
$input->get('task') == 'process'))
if (FabrikWorker::inFormProcess())
{
// Don't mess with posted value - can cause double offsets -
instead do in _indStoareDBFormat();
return $value;
}
// Element could be a date element (in which case no time stored) - check
for both datetime and date null dates.
$nullDate = $db->getNullDate();
$shortNullDate = explode(' ', $nullDate);
$shortNullDate = FArrayHelper::getValue($shortNullDate, 0);
$isNullDate = $nullDate == $value || $shortNullDate == $value;
if ($isNullDate) $value = null;
if ($formModel->isEditable() && ($alwaysToday ||
(($formModel->isNewRecord() || $this->newGroup) &&
$defaultToday))){
$value = 'now';
}
// Don't offset if null date.
if ($value === null)
{
return $value;
}
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$date = Factory::getDate($value, $timeZone);
// Querystring value passed into new record
if ($formModel->isNewRecord() && $value !== '')
{
// OK for : Default to current = no Local time = yes
if (!$defaultToday && !$formModel->failedValidation())
{
$date = new Date($date, $timeZone);
return $date->format('Y-m-d H:i:s');
}
// Ok for : Default to current = yes, Local time = yes OR no
$date = new Date($date, $timeZone);
$date->setTimeZone(new \DateTimeZone('UTC'));
return $date->format('Y-m-d H:i:s');
}
$local = $formModel->hasErrors();
$local = $local === true ? $local :
$params->get('jdate_store_as_local', 0) == 1;
$local = $local === true ? $local : $value != 'now';
// $local = $formModel->hasErrors() ||
$params->get('jdate_store_as_local', 0) == 1) &&
$value != 'now' ? false : true;
$value = $date->toSQL($local);
return $value;
}
/**
* Does the format string contain time formatting options
*
* @param string $format Date format
*
* @since 2.1.1
*
* @return bool
*/
protected function formatContainsTime($format)
{
$format = FabDate::dateFormatToStrftimeFormat($format);
$times = array('%H', '%I', '%l',
'%M', '%p', '%P', '%r',
'%R', '%S', '%T', '%X',
'%z', '%Z');
foreach ($times as $t)
{
if (strpos($format, $t))
{
return true;
}
}
return false;
}
/**
* Should the element's data be returned in the search all?
*
* @param bool $advancedMode Is the elements' list is extended
search all mode?
* @param string $search Search string
*
* @return bool true
*/
public function includeInSearchAll($advancedMode = false, $search =
'')
{
if (!FabrikWorker::isDate($search))
{
return false;
}
return parent::includeInSearchAll($advancedMode);
}
/**
* Builds an array containing the filters value and condition
*
* @param string $value Initial value
* @param string $condition Initial $condition
* @param string $eval How the value should be handled
*
* @return array (value condition)
*/
public function getFilterValue($value, $condition, $eval)
{
/* if its a search all value it may not be a date - so use parent method.
* see http://fabrikar.com/forums/showthread.php?t=25255
*/
if (!is_array($value) && !FabrikWorker::isDate($value))
{
if (($this->rangeFilterSet))
{
// It's already been set as a range expression - so split that
into an array
$condition = 'between';
$value = explode(' AND ', $value);
foreach ($value as &$v)
{
$v = str_replace(array("'", '"'),
'', $v);
}
$filterType = FABRIKFILTER_NOQUOTES;
}
else
{
$filterType = FabrikWorker::isNullDate($value) ? FABRIKFILTER_TEXT :
$eval;
}
return parent::getFilterValue($value, $condition, $filterType);
}
$params = $this->getParams();
$storeAsLocal = (int) $params->get('jdate_store_as_local',
0);
$timeZone = $storeAsLocal ? new
\DateTimeZone($this->config->get('offset')) : null;
if (!$params->get('jdate_showtime', 0) || $storeAsLocal)
{
$this->resetToGMT = false;
}
$exactTime =
$this->formatContainsTime($params->get('jdate_table_format'));
// $$$ rob if filtering in querystring and ranged value set then force
filter type to range
$filterType = is_array($value) ? 'range' :
$this->getFilterType();
switch ($filterType)
{
case 'range':
// Ranged dates should be sent in sql format
break;
case 'field':
case 'dropdown':
case 'auto-complete':
default:
// Oddity when filtering from qs
$value = str_replace("'", '', $value);
/**
* parse through Date, to allow for special filters such as
'now' 'tomorrow' etc.
* for searches on simply the year - Date will presume its a timestamp
and mung the results
* so we have to use this specific format string to get now and next
*/
if (is_numeric($value) && StringHelper::strlen($value) == 4)
{
// Will only work on php 5.3.6
$value = Factory::getDate('first day of January ' .
$value)->toSql();
$next = Factory::getDate('first day of January ' . ($value
+ 1));
}
elseif ($this->isMonth($value))
{
$value = Factory::getDate('first day of ' .
$this->untranslateMonth($value))->toSql();
$next = Factory::getDate('last day of ' .
$this->untranslateMonth($value))->setTime(23, 59, 59);
}
elseif (trim(StringHelper::strtolower($value)) === 'last
week')
{
$value = Factory::getDate('last week')->toSql();
$next = Factory::getDate();
}
elseif (trim(StringHelper::strtolower($value)) === 'last
month')
{
$value = Factory::getDate('last month')->toSql();
$next = Factory::getDate();
}
elseif (trim(StringHelper::strtolower($value)) === 'last
year')
{
$value = Factory::getDate('last year')->toSql();
$next = Factory::getDate();
}
elseif (trim(StringHelper::strtolower($value)) === 'next
week')
{
$value = Factory::getDate()->toSql();
$next = Factory::getDate('next week');
}
elseif (trim(StringHelper::strtolower($value)) === 'next
month')
{
$value = Factory::getDate()->toSql();
$next = Factory::getDate('next month');
}
elseif (trim(StringHelper::strtolower($value)) === 'next
year')
{
$value = Factory::getDate()->toSql();
$next = Factory::getDate('next year');
}
else
{
$value = Factory::getDate($value, $timeZone)->toSql();
/**
* $$$ hugh - strip time if not needed. Specific case is element
filter,
* first time submitting filter from list, will have arbitrary
"now" time.
* Dunno if this will break anything else!
*/
if (!$exactTime)
{
$value = $this->setMySQLTimeToZero($value);
}
$next = Factory::getDate(strtotime($this->addDays($value, 1)) - 1,
$timeZone);
/**
* $$$ now we need to reset $value to GMT.
* Probably need to take $storeAsLocal into account here?
*/
if (!$storeAsLocal)
{
$this->resetToGMT = true;
$value = $this->toMySQLGMT(Factory::getDate($value));
$this->resetToGMT = false;
}
else
{
$seconds = $timeZone->getOffset($next);
$invert = true;
if ($seconds < 0)
{
$invert = false;
$seconds = $seconds * -1;
}
// need to use minutes, as DateInterval will barf on fractional hours
like 5.5 (India)
$dateInterval = new DateInterval('PT' . $seconds .
'S');
$dateInterval->invert = $invert;
$next->sub($dateInterval);
}
}
// Only set to a range if condition is matching (so don't set to
range for < or > conditions)
if ($condition == 'contains' || $condition == '='
|| $condition == 'REGEXP')
{
if (!$params->get('jdate_showtime', 0) || $exactTime ==
false)
{
// $$$ rob turn into a ranged filter to search the entire day values
should be in sql format
$value = (array) $value;
$condition = 'BETWEEN';
$value[1] = $next->toSql();
// Set a flat to stop getRangedFilterValue from adding an additional
day to end value
$this->rangeFilterSet = true;
}
}
elseif ($condition == 'is null')
{
$value = "";
}
break;
}
$this->resetToGMT = true;
$value = parent::getFilterValue($value, $condition, $eval);
return $value;
}
/**
* Is a string a month?
*
* @param string $test String to test
*
* @return bool
*/
protected function isMonth($test)
{
$months = array(Text::_('JANUARY_SHORT'),
Text::_('JANUARY'), Text::_('FEBRUARY_SHORT'),
Text::_('FEBRUARY'), Text::_('MARCH_SHORT'),
Text::_('MARCH'), Text::_('APRIL'),
Text::_('APRIL_SHORT'), Text::_('MAY_SHORT'),
Text::_('MAY'), Text::_('JUNE_SHORT'),
Text::_('JUNE'), Text::_('JULY_SHORT'),
Text::_('JULY'), Text::_('AUGUST_SHORT'),
Text::_('AUGUST'), Text::_('SEPTEMBER_SHORT'),
Text::_('SEPTEMBER'), Text::_('OCTOBER_SHORT'),
Text::_('OCTOBER'), Text::_('NOVEMBER_SHORT'),
Text::_('NOVEMBER'),
Text::_('DECEMBER_SHORT'), Text::_('DECEMBER'));
return in_array($test, $months);
}
/**
* Get English name for translated month
*
* @param string $test Month name
*
* @return string|boolean
*/
protected function untranslateMonth($test)
{
switch ($test)
{
case Text::_('JANUARY_SHORT'):
case Text::_('JANUARY'):
return 'January';
break;
case Text::_('FEBRUARY_SHORT'):
case Text::_('FEBRUARY'):
return 'February';
break;
case Text::_('MARCH_SHORT'):
case Text::_('MARCH'):
return 'March';
break;
case Text::_('APRIL_SHORT'):
case Text::_('APRIL'):
return 'April';
break;
case Text::_('MAY_SHORT'):
case Text::_('MAY'):
return 'May';
break;
case Text::_('JUNE_SHORT'):
case Text::_('JUNE'):
return 'June';
break;
case Text::_('JULY_SHORT'):
case Text::_('JULY'):
return 'July';
break;
case Text::_('AUGUST_SHORT'):
case Text::_('AUGUST'):
return 'August';
break;
case Text::_('SEPTEMBER_SHORT'):
case Text::_('SEPTEMBER'):
return 'September';
break;
case Text::_('OCTOBER_SHORT'):
case Text::_('OCTOBER'):
return 'October';
break;
case Text::_('NOVEMBER_SHORT'):
case Text::_('NOVEMBER'):
return 'November';
break;
case Text::_('DECEMBER_SHORT'):
case Text::_('DECEMBER'):
return 'December';
break;
}
return false;
}
/**
* Get a value to use as an empty filter value
*
* @return string
*/
public function emptyFilterValue()
{
$listModel = $this->getListModel();
$db = $listModel->getDb();
return $db->quote($db->getNullDate());
}
/**
* Get the list filter for the element
* Note: uses FabDate as if date element first to be found in advanced
search, and advanced search run on another
* element the list model in getAdvancedSearchElementList() builds the
first filter (this element) with the data
* from the first search which was throwing '"500 -
DateTime::__construct() ' errors
*
* see: http://fabrikar.com/forums/showthread.php?t=28231
*
* @param int $counter Filter order
* @param bool $normal Do we render as a normal filter or as an
advanced search filter
* if normal include the hidden fields as well
(default true, use false for advanced filter
* rendering)
*
* @return string filter html
*/
public function getFilter($counter = 0, $normal = true, $container =
'')
{
$listModel = $this->getListModel();
$table = $listModel->getTable();
$element = $this->getElement();
$origTable = $table->db_table_name;
$elName = $this->getFullName(true, false);
$v = $this->filterName($counter, $normal);
// Correct default got
$default = $this->getDefaultFilterVal($normal,
$counter);
$this->filterDisplayValues = (array) $default;
// $$$ hugh - in advanced search, _aJoins wasn't getting set
$joins = $listModel->getJoins();
foreach ($joins as $aJoin)
{
// Not sure why the group id key wasn't found - but put here to
remove error
if (property_exists($aJoin, 'group_id'))
{
if ($aJoin->group_id == $element->group_id &&
$aJoin->element_id == 0)
{
$fromTable = $aJoin->table_join;
$elName = str_replace($origTable . '.', $fromTable .
'.', $elName);
}
}
}
$where = $listModel->buildQueryPrefilterWhere($this);
$elName = FabrikString::safeColName($elName);
$requestName = $elName . '___filter';
if (array_key_exists($elName, $_REQUEST))
{
if (is_array($_REQUEST[$elName]) &&
array_key_exists('value', $_REQUEST[$elName]))
{
$_REQUEST[$requestName] = $_REQUEST[$elName]['value'];
}
}
$fType = $this->getFilterType();
if (in_array($fType, array('dropdown', 'checkbox',
'multiselect')))
{
$rows = $this->filterValueList($normal);
$this->getFilterDisplayValues($default, $rows);
}
$return = array();
switch ($fType)
{
case 'checkbox':
$return[] = $this->checkboxFilter($rows, $default, $v);
break;
case 'range':
case 'range-hidden':
$return[] = $this->rangeFilter($default, $v);
break;
case 'dropdown':
case 'multiselect':
$return[] = $this->selectListFilter($rows, $default, $v);
break;
default:
case 'field':
$return[] = $this->singleFilter($default, $v);
break;
case 'hidden':
$return[] = $this->hiddenFilter($default, $v);
break;
case 'auto-complete':
$return[] = $this->autoCompleteFilter($default, $v);
break;
}
if ($normal)
{
$return[] = $this->getFilterHiddenFields($counter, $elName, false,
$normal);
}
else
{
$return[] = $this->getAdvancedFilterHiddenFields();
}
return implode("\n", $return);
}
/**
* Override for main model getFilterRO to handle search all corner case
*
* @param mixed $data String or array of filter value(s)
*
* @since 3.0.7
*
* @return string
*/
public function getFilterRO($data)
{
/**
* This rather fugly chunk of code is needed to handle 'search
all', where the filter data has already
* been converted into textual AND format.
*/
$v = FArrayHelper::getValue($data, $this->getFullName(true, false),
'');
if (is_string($v) && strstr($v, ' AND '))
{
foreach (explode(' AND ', FArrayHelper::getValue($data,
$this->getFullName(true, false), array())) as $d)
{
$return[] = $this->getROElement(trim($d, "'"));
}
return Text::_('COM_FABRIK_BETWEEN') . '<br
/>' . implode('<br />' .
Text::_('COM_FABRIK_AND') . "<br />", $return);
}
return parent::getFilterRO($data);
}
/**
* Build the HTML for the auto-complete filter
*
* @param string $default Label
* @param string $v Field name
* @param string $labelValue Label value
* @param bool $normal Do we render as a normal filter or as an
advanced search filter
* if normal include the hidden fields as well
(default true, use false for advanced
* filter rendering)
*
* @return string LayoutInterface render
*/
protected function autoCompleteFilter($default, $v, $labelValue = null,
$normal = true, $container = null)
{
$default = htmlspecialchars($default);
$htmlId = $this->getHTMLId();
$layout =
$this->getLayout('list-filter-autocomplete');
$displayData = new stdClass;
$displayData->class = $this->filterClass();
$displayData->name = $v;
$displayData->default = $default;
$displayData->htmlId = $htmlId;
$autoId = '#' . $htmlId . '-auto-complete';
if (!$normal)
{
$autoId = '.advanced-search-list .autocomplete-trigger';
}
FabrikHelperHTML::autoComplete($autoId, $this->getElement()->id,
$this->getFormModel()->getId(), 'date');
return $layout->render($displayData);
}
/**
* @param $default
* @param $v
*
* @return string filter
*/
protected function hiddenFilter($default, $v)
{
if (is_array($default))
{
$default = array_shift($default);
}
$default = htmlspecialchars($default);
$class = $this->filterClass();
// Don't add id as caused issues with inline edit plugin and
clashing ids.
return '<input type="hidden" name="' . $v .
'" class="' . $class . '" value="'
. $default . '" />';
}
/**
* @param $rows
* @param $default
* @param $v
*
* @return string filter
*/
protected function selectListFilter($rows, $default, $v)
{
$element = $this->getElement();
$listModel = $this->getListModel();
$fabrikDb = $listModel->getDb();
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$params = $this->getParams();
$format = $params->get('jdate_table_format',
'Y-m-d');
$storeAsLocal = $params->get('jdate_store_as_local',
'0') == '1';
/**
* cant do the format in the MySQL query as its not the same formatting
* e.g. M in MySQL is month and J's date code its minute
*/
$max = count($rows) < 7 ? count($rows) : 7;
$fType = $this->getFilterType();
$size = $element->filter_type === 'multiselect' ?
'multiple size="' . $max . '"' :
'size="1"';
$v = $fType === 'multiselect' ? $v . '[]' : $v;
$class = [];
jimport('joomla.utilities.date');
$ddData = array();
foreach ($rows as $k => $o)
{
if ($fabrikDb->getNullDate() === $o->text)
{
$o->text = '';
$o->value = '';
}
else
{
$d = new FabDate($o->text);
if (!$storeAsLocal)
{
$d->setTimeZone($timeZone);
$o->value = $d->toSql(true);
$o->text = $d->format($format, true);
}
else
{
$o->value = $d->toSql(false);
$o->text = $d->format($format, false);
}
}
if (!array_key_exists($o->value, $ddData))
{
$ddData[$o->value] = $o;
}
}
array_unshift($ddData, HTMLHelper::_('select.option',
'', $this->filterSelectLabel()));
$layout =
$this->getLayout('list-filter-dropdown');
$displayData = new stdClass;
$displayData->rows = $ddData;
$displayData->name = $v;
$displayData->class = "form-select "
.$this->filterClass();
$displayData->size = $size;
$displayData->default = $default;
$displayData->htmlId = $this->getHTMLId();
return $layout->render($displayData);
}
/**
* Create a input type field filter
*
* @param $default
* @param $v
*
* @return string filter
*/
protected function singleFilter($default, $v, $type = 'text')
{
$params = $this->getParams();
$format = $params->get('jdate_table_format',
'Y-m-d');
$rawValue = $default;
if (is_array($default))
{
$default = array_shift($default);
}
if ($default !== '')
{
$d = new FabDate($default);
$default = $d->format($format);
}
$layout = $this->getLayout('list-filter-field');
$displayData = new stdClass;
$from = new stdClass;
$from->id = $this->getFilterHtmlId(0);
$from->value = $default;
$from->name = $v;
$from->rawValue = $rawValue;
$imageOpts = array('alt' => 'calendar');
$from->img = FabrikHelperHTML::image('calendar',
'form', @$this->tmpl, $imageOpts);
$displayData->from = $from;
$displayData->format = $format;
$displayData->calOpts = $this->filterCalendarOpts();
$displayData->jCal = $this->calendar($rawValue, $v,
$this->getFilterHtmlId(0), $format, $this->filterCalendarOpts(), 0);
return $layout->render($displayData);
}
/**
* @param $default
* @param $v
*
* @return string filter
*/
protected function rangeFilter($default, $v)
{
$params = $this->getParams();
$format = $params->get('jdate_table_format',
'Y-m-d');
if (empty($default))
{
$default = array('', '');
$rawValues = array('', '');
}
else
{
$rawValues = array(
$default[0],
$default[1]
);
$d = new FabDate($default[0]);
$default[0] = $d->format($format);
$d = new FabDate($default[1]);
$default[1] = $d->format($format);
}
// Add wrapper div for list filter toggling
$return[] = '<div
class="fabrik_filter_container">';
$layout =
$this->getLayout('list-filter-range');
$displayData = new stdClass;
$displayData->htmlId = $this->getHTMLId();
$displayData->class = $this->filterClass();
$from = new stdClass;
$from->id = $this->getFilterHtmlId(0);
$from->value = $default[0];
$from->name = $v . '[0]';
$imageOpts = array('alt' => 'calendar');
$from->img = FabrikHelperHTML::image('calendar',
'form', @$this->tmpl, $imageOpts);
$displayData->from = $from;
$displayData->jCalFrom = $this->calendar($rawValues[0],
$v.'[0]', $this->getFilterHtmlId(0), $format,
$this->filterCalendarOpts(), 0);
$displayData->format = $format;
$displayData->calOpts = $this->filterCalendarOpts();
$to = new stdClass;
$to->id = $this->getFilterHtmlId(1);
$to->value = $default[1];
$to->name = $v . '[1]';
$imageOpts = array('alt' => 'calendar');
$to->img = FabrikHelperHTML::image('calendar',
'form', @$this->tmpl, $imageOpts);
$displayData->to = $to;
$displayData->filterType = $this->getFilterType();
$displayData->jCalTo = $this->calendar($rawValues[1],
$v.'[1]', $this->getFilterHtmlId(1), $format,
$this->filterCalendarOpts(), 0);
return $layout->render($displayData);
}
/**
* Get filter HTML id
*
* @param int $range Which ranged filter we are getting
*
* @return string html filter id
*/
protected function getFilterHtmlId($range)
{
$input = $this->app->input;
$counter = $input->get('counter', 0);
$listRef = $this->getListModel()->getRenderContext();
return $this->getHTMLId() . '_' . $listRef .
'_filter_range_' . $range . '_' .
$input->get('task') . '.' . $counter;
}
/**
* Cache method to populate auto-complete options
*
* @param plgFabrik_Element $elementModel Element model
* @param string $search Search string
* @param array $opts Options, 'label'
=> field to use for label (db join)
*
* @since 3.0.7
*
* @return string json encoded search results
*/
public static function cacheAutoCompleteOptions($elementModel, $search,
$opts = array())
{
$listModel = $elementModel->getListModel();
$table = $listModel->getTable();
$db = $listModel->getDb();
$name = $elementModel->getFullName(false, false);
$query = $db->getQuery(true);
$params = $elementModel->getParams();
$format = $params->get('jdate_table_format');
$elementModel->strftimeTFormatToMySQL($format);
$search = $db->quote('%' . addslashes($search) .
'%');
$query->select('DISTINCT(' . $name . ') AS value,
' . $name . ' AS text')->from($table->db_table_name)
->where($name . ' LIKE ' . $search . ' OR
DATE_FORMAT(' . $name . ', "' . $format . '"
) LIKE ' . $search);
$db->setQuery($query);
$tmp = $db->loadObjectList();
$ddData = array();
foreach ($tmp as &$t)
{
$elementModel->toLabel($t->text);
if (!array_key_exists($t->text, $ddData))
{
$ddData[$t->text] = $t;
}
}
$ddData = array_values($ddData);
echo json_encode($ddData);
}
/**
* When importing csv data you can run this function on all the data to
* format it into the format that the form would have submitted the date
*
* @param array &$data To prepare
* @param string $key List column heading
* @param bool $isRaw Data is raw
*
* @return array data
*/
public function prepareCSVData(&$data, $key, $isRaw = false)
{
if ($isRaw)
{
return;
}
$params = $this->getParams();
$csvFormat = $params->get('jdate_csv_offset_tz',
'0');
// 0 is "format format", 1 is "list format"
if ($csvFormat === '0' || $csvFormat === '3')
{
$paramName = $csvFormat === '0' ?
'jdate_form_format' : 'jdate_table_format';
$format = $params->get($paramName, 'Y-m-d H:i:s');
// Go through data and convert specified format to MySQL
for ($j = 0; $j < count($data); $j++)
{
$orig_data = $data[$j][$key];
if (empty($orig_data))
{
$data[$j][$key] = '0000-00-00 00:00:00';
continue;
}
try
{
$date = Date::createFromFormat($format, $orig_data);
$exactTime = $this->formatContainsTime($format);
if ($date !== false)
{
if (!$params->get('jdate_showtime', 0) || $exactTime ==
false)
{
$date->setTime(0, 0, 0);
}
$data[$j][$key] = $date->format('Y-m-d H:i:s');
}
}
catch (Exception $e)
{
// Suppress date time format error
}
}
}
}
/**
* Does the element consider the data to be empty
* Used in isempty validation rule
*
* @param array $data Data to test against
* @param int $repeatCounter Repeat group #
*
* @return bool
*/
public function dataConsideredEmpty($data, $repeatCounter)
{
return ($data == '') ? true : false;
}
/**
* This builds an array containing the filters value and condition
* when using a ranged search
*
* @param array $value Initial value
* @param string $condition Filter condition e.g BETWEEN
*
* @return array (value condition)
*/
protected function getRangedFilterValue($value, $condition = '')
{
$db = FabrikWorker::getDbo();
$params = $this->getParams();
/* $$$ hugh - need to convert dates to MySQL format for the query
* $$$ hugh - not any more, since we changed to always submit in MySQL
format
* $$$ hugh - removing the MySQL conversion has broken
'special' range handling,
* which used to happen in the MySQL conversion function. So ...
* Created new helper function specialStrToMySQL() which turns things
* like 'midnight yesterday' etc. into MySQL dates, defaulting
to GMT.
* This lets us do ranged query string and content plugin filters like
...
*
table___date[value][]=midnight%20yesterday&table___date[value][]=midnight%20today&table___date[condition]=BETWEEN
*/
$value[0] =
FabrikWorker::specialStrToMySQL(FArrayHelper::getValue($value, 0));
$value[1] =
FabrikWorker::specialStrToMySQL(FArrayHelper::getValue($value, 1));
// $$$ hugh - if the first date is later than the second, swap 'em
round to keep 'BETWEEN' in the query happy
if (strtotime($value[0]) > strtotime($value[1]))
{
$tmp_value = $value[0];
$value[0] = $value[1];
$value[1] = $tmp_value;
}
$exactTime =
$this->formatContainsTime($params->get('jdate_table_format'));
if (!$params->get('jdate_showtime', 0) || $exactTime ==
false)
{
// Range values could already have been set in getFilterValue
if (!$this->rangeFilterSet)
{
/* $$$ due to some changes in how we handle ranges, the following was
no longer getting
* applied in getFilterValue, needed because on first submit of a
filter an arbitrary time
* is being set (i.e. time "now").
*/
$value[0] = $this->setMySQLTimeToZero($value[0]);
$value[1] = $this->setMySQLTimeToZero($value[1]);
/* $$$ hugh - need to back this out by one second, otherwise we're
including next day.
* So ... say we are searching from '2009-07-17' to
'2009-07-21', the
* addDays(1) changes '2009-07-21 00:00:00' to
'2009-07-22 00:00:00',
* but what we really want is '2009-07-21 23:59:59'
*/
$value[1] = date("Y-m-d H:i:s",
strtotime($this->addDays($value[1], 1)) - 1);
}
}
// $$$ rob 20/07/2012 Date is posted as local time, need to set it back
to GMT. Seems needed even if dates are saved without timeselector
// $$$ hugh - think we may need to take 'store as local' in to
account here?
$localTimeZone = new
\DateTimeZone($this->config->get('offset'));
$params = $this->getParams();
$storeAsLocal = $params->get('jdate_store_as_local',
'0') == '1';
$date = Factory::getDate($value[0], $localTimeZone);
$value[0] = $date->toSql($storeAsLocal);
$date = Factory::getDate($value[1], $localTimeZone);
/* $$$ hugh - why are we setting the 'local' arg on toSql() for
end date but not the start date of the range?
* This ends up with queries like "BETWEEN '2012-01-26
06:00:00' AND '2012-01-26 23:59:59'"
* with CST (GMT -6), which chops out 6 hours of the day range.
* Also, see comment above about maybe needing to take "save as
local" in to account on this.
*/
// $value[1] = $date->toSql(true);
$value[1] = $date->toSql($storeAsLocal);
$value = $db->quote($value[0]) . ' AND ' .
$db->quote($value[1]);
$condition = 'BETWEEN';
return array($value, $condition);
}
/**
* simple minded method to set a MySQL formatted date's time to
00:00:00
*
* @param string $date In MySQL format
*
* @return string MySQL formatted date with time set to 0
*/
protected function setMySQLTimeToZero($date)
{
$date_array = explode(' ', $date);
$date_array[1] = '00:00:00';
return implode(' ', $date_array);
}
/**
* Add days to a date
*
* @param mixed $date The initial time for the FabDate object
* @param integer $add Number of days to add (negative to remove days)
*
* @return string MySQL formatted date
*/
protected function addDays($date, $add = 0)
{
$params = $this->getParams();
$storeAsLocal = $params->get('jdate_store_as_local', 0)
== 1;
$date = Factory::getDate($date);
/*
$PHPDate = getdate($date->toUnix());
$PHPDate['mday'] = $PHPDate['mday'] + $add;
$v = mktime($PHPDate['hours'],
$PHPDate['minutes'], $PHPDate['seconds'],
$PHPDate['mon'], $PHPDate['mday'],
$PHPDate['year']);
$date = Factory::getDate($v);
*/
$hours = 'PT' . abs($add) * 24 . 'H';
if ($add < 0)
{
$date->sub(new DateInterval($hours));
}
else
{
$date->add(new DateInterval($hours));
}
return $date->toSql($storeAsLocal);
}
/**
* Build the query for the avg calculation
*
* @param model &$listModel list model
* @param array $labels Labels
*
* @return string sql statement
*/
protected function getAvgQuery(&$listModel, $labels = array())
{
$label = count($labels) == 0 ? "'calc' AS label" :
'CONCAT(' . implode(', " & " , ',
$labels) . ') AS label';
$table = $listModel->getTable();
$db = $listModel->getDb();
$joinSQL = $listModel->buildQueryJoin();
$whereSQL = $listModel->buildQueryWhere();
$name = $this->getFullName(false, false);
return 'SELECT FROM_UNIXTIME(AVG(UNIX_TIMESTAMP(' . $name .
'))) AS value, ' . $label . ' FROM '
. $db->qn($table->db_table_name) . ' ' . $joinSQL .
' ' . $whereSQL;
}
/**
* Get sum query
*
* @param object &$listModel List model
* @param array $labels Label
*
* @return string
*/
protected function getSumQuery(&$listModel, $labels = array())
{
$label = count($labels) == 0 ? "'calc' AS label" :
'CONCAT(' . implode(', " & " , ',
$labels) . ') AS label';
$table = $listModel->getTable();
$db = $listModel->getDb();
$joinSQL = $listModel->buildQueryJoin();
$whereSQL = $listModel->buildQueryWhere();
$name = $this->getFullName(false, false);
// $$$rob not actually likely to work due to the query easily exceeding
MySQL's TIMESTAMP_MAX_VALUE value but the query in itself is correct
return 'SELECT FROM_UNIXTIME(SUM(UNIX_TIMESTAMP(' . $name .
'))) AS value, ' . $label . ' FROM '
. $db->qn($table->db_table_name) . ' ' . $joinSQL .
' ' . $whereSQL;
}
/**
* find an average from a set of data
* can be overwritten in plugin - see date for example of averaging dates
*
* @param array $data To average
*
* @return string average result
*/
public function simpleAvg($data)
{
$avg = $this->simpleSum($data) / count($data);
return Factory::getDate($avg)->toSql();
}
/**
* find the sum from a set of data
* can be overwritten in plugin - see date for example of averaging dates
*
* @param array $data To sum
*
* @return string sum result
*/
public function simpleSum($data)
{
$sum = 0;
foreach ($data as $d)
{
$date = Factory::getDate($d);
$sum += $date->toUnix();
}
return $sum;
}
/**
* Takes date's time value and turns it into seconds. Used in timer
*
* @param string $date Object $date
*
* @return int seconds
*/
protected function toSeconds($date)
{
return (int) ($date->format('H') * 60 * 60) + (int)
($date->format('i') * 60) + (int)
$date->format('s');
}
/**
* Takes strftime time formatting -
http://fr.php.net/manual/en/function.strftime.php
* and converts to format used in MySQL DATE_FORMAT
* http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html
*
* @param string &$format PHP date format string => MySQL string
format
*
* @return void
*/
protected function strftimeTFormatToMySQL(&$format)
{
/**
* $$$ hugh - can't do direct %x to %y, because str_replace's
left to right processing,
* so (for instance) %B translates to %M, which then gets translated
again to %i
* So ... do %x to ^@y (hopefully nobody will ever use ^@ in their format
string!),
* then replace all ^@'s with %'s.
*/
$search = array('%e', '%j', '%u',
'%V', '%W', '%h', '%B',
'%C', '%g', '%G', '%M',
'%P', '%r', '%R', '%T',
'%X', '%z', '%Z', '%D',
'%F', '%s',
'%x', '%A');
$replace = array('^@e', '^@j', '^@w',
'^@U', '^@U', '^@b', '^@M',
'', '^@y', '^@Y', '^@i',
'^@p', '^@I:^@i:^@S ^@p', '^@H:^@i',
'^@H:^@i:^@S', '', '',
'^@H:^@i:^@S', '^@m/^@c/^@y', '^@Y-^@m-^@c',
'', '^@Y-^@m-^@c', '^@W');
$format = str_replace($search, $replace, $format);
$format = str_replace('^@', '%', $format);
}
/**
* Build the filter query for the given element.
* Can be overwritten in plugin - e.g. see checkbox element which checks
for partial matches
*
* @param string $key Element name in format
`tablename`.`elementname`
* @param string $condition =/like etc
* @param string $value Search string - already quoted if
specified in filter array options
* @param string $originalValue Original filter value without quotes or
%'s applied
* @param string $type Filter type
advanced/normal/prefilter/search/querystring/searchall
* @param string $evalFilter evaled
*
* @return string sql query part e,g, "key = value"
*/
public function getFilterQuery($key, $condition, $value, $originalValue,
$type = 'normal', $evalFilter = '0')
{
$this->encryptFieldName($key);
switch ($condition)
{
case 'thisyear':
$query = ' YEAR(' . $this->addConvert($key) . ') =
YEAR(NOW()) ';
break;
case 'earlierthisyear':
$query = ' (DAYOFYEAR(' . $this->addConvert($key) .
') <= DAYOFYEAR(NOW()) AND YEAR(' . $this->addConvert($key)
. ') = YEAR(NOW())) ';
break;
case 'laterthisyear':
$query = ' (DAYOFYEAR(' . $this->addConvert($key) .
') >= DAYOFYEAR(NOW()) AND YEAR(' . $this->addConvert($key)
. ') = YEAR(NOW())) ';
break;
case 'today':
$query = ' (' . $this->addConvert($key) . ' >=
CURDATE() AND ' . $this->addConvert($key) . ' < CURDATE() +
INTERVAL 1 DAY) ';
break;
case 'yesterday':
$query = ' (' . $this->addConvert($key) . ' >=
CURDATE() - INTERVAL 1 DAY AND ' . $this->addConvert($key) . '
< CURDATE()) ';
break;
case 'tomorrow':
$query = ' (' . $this->addConvert($key) . ' >=
CURDATE() + INTERVAL 1 DAY AND ' . $this->addConvert($key) .
' < CURDATE() + INTERVAL 2 DAY ) ';
break;
case 'thismonth':
$query = ' (' . $this->addConvert($key) . ' >=
DATE_ADD(LAST_DAY(DATE_SUB(now(), INTERVAL 1 MONTH)), INTERVAL 1 DAY) AND
' . $this->addConvert($key)
. ' <= LAST_DAY(NOW()) ) ';
break;
case 'lastmonth':
$query = ' (' . $this->addConvert($key) . ' >=
DATE_ADD(LAST_DAY(DATE_SUB(now(), INTERVAL 2 MONTH)), INTERVAL 1 DAY) AND
' . $this->addConvert($key)
. ' <= LAST_DAY(DATE_SUB(NOW(), INTERVAL 1 MONTH)) ) ';
break;
case 'nextmonth':
$query = ' (' . $this->addConvert($key) . ' >=
DATE_ADD(LAST_DAY(now()), INTERVAL 1 DAY) AND ' .
$this->addConvert($key)
. ' <= DATE_ADD(LAST_DAY(NOW()), INTERVAL 1 MONTH) ) ';
break;
case 'nextweek1':
$query = ' (YEARWEEK(' . $this->addConvert($key) .
',1) = YEARWEEK(DATE_ADD(NOW(), INTERVAL 1 WEEK), 1))';
break;
case 'birthday':
$query = '(MONTH(' . $this->addConvert($key) . ') =
MONTH(CURDATE()) AND DAY(' . $this->addConvert($key) . ') =
DAY(CURDATE())) ';
break;
default:
$params = $this->getParams();
$format = $params->get('jdate_table_format');
if ($format == '%a' || $format == '%A')
{
/**
* special cases where we want to search on a given day of the week
* note it wont work with ranged searches
*/
$this->strftimeTFormatToMySQL($format);
$key = "DATE_FORMAT( $key , '$format')";
}
elseif ($format == '%Y %B')
{
/* $$$ hugh - testing horrible hack for different languages, initially
for andorapro's site
* Problem is, he has multiple language versions of the site, and
needs to filter tables
* by "%Y %B" dropdown (i.e. "2010 November") in
multiple languages.
* FabDate automagically uses the selected language when we render the
date
* but when we get to this point, month names are still localized,
i.e. in French or German
* which MySQL won't grok (until 5.1.12)
* So we need to translate them back again, *sigh*
* FIXME - need to make all this more generic, so we can handle any
date format which uses
* month or day names.
*/
$matches = array();
if (preg_match('#\d\d\d\d\s+(\S+)\b#', $value, $matches))
{
$this_month = $matches[1];
$en_month = $this->_monthToEnglish($this_month);
$value = str_replace($this_month, $en_month, $value);
$this->strftimeTFormatToMySQL($format);
$key = "DATE_FORMAT( $key , '$format')";
}
}
if ($type == 'querystring' &&
StringHelper::strtolower($value) == 'now')
{
$value = 'NOW()';
}
$query = " $key $condition $value ";
break;
}
return $query;
}
/**
* Called when copy row list plugin called
*
* @param mixed $val Value to copy into new record
*
* @return mixed value to copy into new record
*/
public function onCopyRow($val)
{
if ($this->defaultOnCopy())
{
$val = $this->getFrontDefaultValue();
}
if (!FabrikWorker::isDate($val))
{
return $val;
}
$params = $this->getParams();
if ($params->get('jdate_showtime', 0))
{
$storeAsLocal = (int) $params->get('jdate_store_as_local',
0);
if (!$storeAsLocal)
{
$date = Factory::getDate($val);
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$date->setTimeZone($timeZone);
$val = $date->toSql(true);
}
}
return $val;
}
/**
* Called when save as copy form button clicked
*
* @param mixed $val value to copy into new record
*
* @return mixed value to copy into new record
*/
public function onSaveAsCopy($val)
{
return $this->onCopyRow($val);
}
/**
* Used by validations
*
* @param string $data This elements data
* @param string $cond What condition to apply
* @param string $compare Data to compare element's data to (if
date already set to Y-m-d H:I:S so no need to
* apply storeDatabaseForm() on it
*
* @return bool
*/
public function greaterOrLessThan($data, $cond, $compare)
{
$data = $this->storeDatabaseFormat($data, null);
/**
* $$$ rob 30/06/2011 the line below was commented out - but if doing
date compare on 2 fields
* formatting %d/%m/%Y then the compare unix time was not right
*/
$compare = $this->storeDatabaseFormat($compare, null);
$data = Factory::getDate($data)->toUnix();
$compare = Factory::getDate($compare)->toUnix();
return parent::greaterOrLessThan($data, $cond, $compare);
}
/**
* Part of horrible hack for translating non-English words back
* to something MySQL will understand.
*
* @param string $month Original month name
* @param bool $abbr Is the month abbreviated
*
* @return string English month name
*/
private function _monthToEnglish($month, $abbr = false)
{
if ($abbr)
{
if (StringHelper::strcmp($month, Text::_('JANUARY_SHORT')) ===
0)
{
return 'Jan';
}
if (StringHelper::strcmp($month, Text::_('FEBRUARY_SHORT'))
=== 0)
{
return 'Feb';
}
if (StringHelper::strcmp($month, Text::_('MARCH_SHORT')) ===
0)
{
return 'Mar';
}
if (StringHelper::strcmp($month, Text::_('APRIL_SHORT')) ===
0)
{
return 'Apr';
}
if (StringHelper::strcmp($month, Text::_('MAY_SHORT')) === 0)
{
return 'May';
}
if (StringHelper::strcmp($month, Text::_('JUNE_SHORT')) === 0)
{
return 'Jun';
}
if (StringHelper::strcmp($month, Text::_('JULY_SHORT')) === 0)
{
return 'Jul';
}
if (StringHelper::strcmp($month, Text::_('AUGUST_SHORT')) ===
0)
{
return 'Aug';
}
if (StringHelper::strcmp($month, Text::_('SEPTEMBER_SHORT'))
=== 0)
{
return 'Sep';
}
if (StringHelper::strcmp($month, Text::_('OCTOBER_SHORT')) ===
0)
{
return 'Oct';
}
if (StringHelper::strcmp($month, Text::_('NOVEMBER_SHORT'))
=== 0)
{
return 'Nov';
}
if (StringHelper::strcmp($month, Text::_('DECEMBER_SHORT'))
=== 0)
{
return 'Dec';
}
}
else
{
if (StringHelper::strcmp($month, Text::_('JANUARY')) === 0)
{
return 'January';
}
if (StringHelper::strcmp($month, Text::_('FEBRUARY')) === 0)
{
return 'February';
}
if (StringHelper::strcmp($month, Text::_('MARCH')) === 0)
{
return 'March';
}
if (StringHelper::strcmp($month, Text::_('APRIL')) === 0)
{
return 'April';
}
if (StringHelper::strcmp($month, Text::_('MAY')) === 0)
{
return 'May';
}
if (StringHelper::strcmp($month, Text::_('JUNE')) === 0)
{
return 'June';
}
if (StringHelper::strcmp($month, Text::_('JULY')) === 0)
{
return 'July';
}
if (StringHelper::strcmp($month, Text::_('AUGUST')) === 0)
{
return 'August';
}
if (StringHelper::strcmp($month, Text::_('SEPTEMBER')) === 0)
{
return 'September';
}
if (StringHelper::strcmp($month, Text::_('OCTOBER')) === 0)
{
return 'October';
}
if (StringHelper::strcmp($month, Text::_('NOVEMBER')) === 0)
{
return 'November';
}
if (StringHelper::strcmp($month, Text::_('DECEMBER')) === 0)
{
return 'December';
}
}
return $month;
}
/**
* load a new set of default properties and params for the element
*
* @param array $properties Default props
*
* @return FabrikTableElement element (id = 0)
*/
public function getDefaultProperties($properties = array())
{
$item = parent::getDefaultProperties();
$item->hidden = 1;
return $item;
}
/**
* convert XML format data into fabrik data (used by web services)
*
* @param mixed $v Data
*
* @return mixed data
*/
public function fromXMLFormat($v)
{
return Factory::getDate($v)->toSql();
}
/**
* If used as a filter add in some JS code to watch observed filter
element's changes
* when it changes update the contents of this elements dd filter's
options
*
* @param bool $normal Is the filter a normal (true) or advanced
filter
* @param string $container Container
*
* @return void
*/
public function filterJS($normal, $container)
{
$element = $this->getElement();
$type = $this->getFilterType();
if ($normal && ($type !== 'field' && $type !==
'range'))
{
return;
}
$params = $this->getParams();
$id = $this->getFilterHtmlId(0);
$id2 = $this->getFilterHtmlId(1);
$opts = new stdClass;
$opts->calendarSetup = $this->_CalendarJSOpts($id);
$opts->calendarSetup->ifFormat =
$params->get('jdate_table_format', 'Y-m-d');
$opts->calendarSetup->ifFormat =
FabDate::dateFormatToStrftimeFormat($opts->calendarSetup->ifFormat);
$opts->type = $type;
$opts->ids = $type == 'field' ? array($id) : array($id,
$id2);
$opts->buttons = $type == 'field' ? array($id .
'_btn') : array($id . '_btn', $id2 . '_btn');
$opts = json_encode($opts);
$script = 'Fabrik.filter_' . $container .
'.addFilter(\'' . $element->plugin . '\', new
JDateFilter(' . $opts . '));' . "\n";
if ($normal)
{
FabrikHelperHTML::script('plugins/fabrik_element/jdate/filter.js');
return $script;
}
else
{
FabrikHelperHTML::script('plugins/fabrik_element/jdate/filter.js',
$script);
}
}
/**
* Get calendar filter widget options
*
* @return array options
*/
protected function filterCalendarOpts()
{
$params = $this->getParams();
$class = $this->filterClass();
$calOpts = array('class' => $class, 'maxlength'
=> '19', 'size' => 16);
if ($params->get('jdate_allow_typing_in_field', true) ==
false)
{
//$calOpts['readonly'] = 'readonly';
}
return $calOpts;
}
/**
* Get the class to manage the form element
* to ensure that the file is loaded only once
*
* @param array &$srcs Scripts previously loaded
* @param string $script Script to load once class has loaded
* @param array &$shim Dependant class names to load before
loading the class - put in requirejs.config shim
*
* @return void
*/
public function formJavascriptClass(&$srcs, $script = '',
&$shim = array())
{
$key = FabrikHelperHTML::isDebug() ? 'element/jdate/jdate' :
'element/jdate/jdate-min';
// Ensure that we keep advanced dependencies from previous date elements
regardless of current elements settings.
$deps = array_key_exists($key, $shim) ? $shim[$key]->deps : array();
$params = $this->getParams();
$globalShim = new stdClass();
if (empty($deps))
{
/**
* Main datejs files (core, parser) require the globalization to be
loaded first,
* to add the Date.CultureInfo object, so we have to shim it thusly
*/
$s = new stdClass;
$s->deps = [];
$globalShim->deps = ['lib/datejs/globalization/' .
Factory::getApplication()->getLanguage()->getTag()];
$s->deps[] = 'lib/datejs/globalization/' .
Factory::getApplication()->getLanguage()->getTag();
$s->deps[] = 'lib/datejs/core';
$shim['lib/datejs/core'] = $globalShim;
$s->deps[] = 'lib/datejs/parser';
$shim['lib/datejs/parser'] = $globalShim;
$s->deps[] = 'lib/datejs/extras';
$shim['lib/datejs/extras'] = $globalShim;
$shim[$key] = $s;
}
/* If the jdate element is loaded on an ajax page, we need to manually
include the j! calendar & javascript files */
if (FabrikHelperHTML::inAjaxLoadedPage()) {
$min = FabrikHelperHTML::isDebug() ? '.min' : '';
$jsloc = Juri::root() .'media/system/js/fields/';
$cssloc = Juri::root() .'media/system/css/fields/';
echo "<script
src='".$jsloc."calendar-locales/date/gregorian/date-helper".$min.".js'
type='text/javascript'></script>";
echo "<script
src='".$jsloc."calendar".$min.".js'
type='text/javascript'></script>";
echo "<link
href='".$cssloc."calendar".$min.".css'
rel='stylesheet'>";
/* We also need to add them to the shim so they are loaded before the
calendar is initialized */
$shim[$key]->deps[] = Juri::root()
.'media/system/js/fields/calendar.min.js';
$shim[Juri::root() .'media/system/js/fields/calendar.min.js']
= $globalShim;
$shim[$key]->deps[] = Juri::root()
.'media/system/js/fields/calendar-locales/date/gregorian/date-helper.min.js';
$shim[Juri::root()
.'media/system/js/fields/calendar-locales/date/gregorian/date-helper.min.js']
= $globalShim;
}
parent::formJavascriptClass($srcs, $script, $shim);
// Return false, as we need to be called on per-element (not per-plugin)
basis
return false;
}
/**
* Get the Front end JS default date
*
* @param array $data Form data
*
* @return string
*/
public function getFrontDefaultValue($data = array())
{
$params = $this->getParams();
$db =
Factory::getContainer()->get('DatabaseDriver');
$alwaysToday = $params->get('jdate_alwaystoday', false);
$defaultToday = $params->get('jdate_defaulttotoday', false);
if ($alwaysToday || $defaultToday)
{
$this->default = HTMLHelper::_('date', 'now',
$db->getDateFormat());
}
else
{
$this->default = parent::getDefaultValue($data);
}
return $this->default;
}
/*
* If date is stored as UTC, wrap the necessary CONVERT_TZ() around the
key name to offset it, so 'foo'
* becomes 'CONVERT_TZ(foo, "+0:00",
"+5:00")'. If storing as local, leave key intact. Used
when building things
* like pre-filter queries for "today".
*/
protected function addConvert($key)
{
$params = $this->getParams();
$storeAsLocal = (int) $params->get('jdate_store_as_local',
0);
if ($params->get('jdate_store_as_local', '0') !==
'1')
{
$timeZone = new
\DateTimeZone($this->config->get('offset'));
$zoneDate = new Date('now', $timeZone);
$tzStr = $zoneDate->format('P');
$key = 'CONVERT_TZ(' . $key . ', "+0:00",
"' . $tzStr . '")';
}
return $key;
}
/**
* Internal element validation
*
* For the date element, this is just here to add the element to the
'modified' array for AJAX validation,
* so it gets re-displayed. Need to do this because we change the
displayed date format when submitting (or on
* change page in multipage forms, etc).
*
* @param array $data form data
* @param int $repeatCounter repeat group counter
*
* @return bool
*/
public function validate($data, $repeatCounter = 0)
{
if ($this->app->input->get('fabrik_ajax',
'0') === '1')
{
if (FabrikWorker::isDate($data))
{
$params = $this->getParams();
$localDate = $this->displayDate($data);
$formModel = $this->getFormModel();
$name = $this->getFullName(true, false);
$group = $this->getGroup();
if ($group->canRepeat())
{
$formModel->modifiedValidationData[$name][$repeatCounter] =
$localDate->format('Y-m-d H:i:s', true);
}
else
{
$formModel->modifiedValidationData[$name] =
$localDate->format('Y-m-d H:i:s', true);
}
}
}
return true;
}
/**
* Get the time format according to time settings
*
* @return mixed|string
*/
private function getTimeFormat()
{
$params = $this->getParams();
$timeFormat = '';
if ($params->get('jdate_showtime', '0'))
{
$timeFormat = $params->get('jdate_time_format',
'');
if (empty($timeFormat))
{
if ($params->get('jdate_time_24', '1'))
{
$timeFormat = 'H:i';
}
else
{
// NOTE - 't usedon 'g' (hours without leading 0) as it
translates to %l, which won't work on Windows
$timeFormat = 'h:i A';
}
}
}
return $timeFormat;
}
public function beforeSave(&$row)
{
$groups = $this->getFormModel()->getGroupsHiarachy();
$found = false;
foreach ($groups as $groupModel)
{
$elementModels = $groupModel->getPublishedElements();
foreach ($elementModels as $elementModel)
{
if ($elementModel->element->plugin ===
'date') {
$found = true;
break 2;
}
}
}
if ($found)
{
$this->app->enqueueMessage(Text::_('PLG_ELEMENT_JDATE_DATE_WARNING'));
}
}
/**
* run on formModel::setFormData()
*
* purpose is to convert an empty string value into a null, current sql
does not support an empty string value
*
* @param int $c repeat group counter
*
* @return void
*/
public function preProcess($c)
{
$input = $this->app->input;
$form = $this->getFormModel();
$data = unserialize(serialize($form->formData));
$key = $this->getFullName(true, false);
$rawKey = $key . '_raw';
if ($data[$key] != '') return;
/* We have an empty string for a date field, convert it into null */
$form->updateFormData($key, null);
$form->updateFormData($rawKey, null);
$input->post->set($key, null);
$input->post->set($rawKey, null);
}
}
/**
* very small override to Date to stop 500 errors occurring (when Jdebug is
on) if $date is not a valid date string
*
* @package Fabrik
* @since 3.0
*/
class FabDate extends Date
{
/**
* GMT Date
*
* @var DateTimeZone
*/
protected static $gmt;
/**
* Default tz date
*
* @var DateTimeZone
*/
protected static $stz;
/**
* Construct
*
* @param string $date Date
* @param mixed $tz Timezone
*/
public function __construct($date = 'now', $tz = null)
{
$app = Factory::getApplication();
$orig = $date;
$date = $this->stripDays($date);
/* not sure if this one needed?
* $date = $this->monthToInt($date);
*/
$date = $this->removeDashes($date);
try
{
$dt = new Date($date);
}
catch (Exception $e)
{
JDEBUG ? $app->enqueueMessage('date format unknown for ' .
$orig . ' replacing with today\'s date', 'notice')
: '';
$date = 'now';
/* catches 'Failed to parse time string (ublingah!) at position 0
(u)' exception.
* don't use this object
*/
}
// Create the base GMT and server time zone objects.
if (empty(self::$gmt) || empty(self::$stz))
{
self::$gmt = new \DateTimeZone('GMT');
self::$stz = new \DateTimeZone(@date_default_timezone_get());
}
parent::__construct($date, $tz);
}
/**
* Remove '-' from string
*
* @param string $str String to remove - from
*
* @return string
*/
protected function removeDashes($str)
{
$str = FabrikString::ltrimword($str, '-');
return $str;
}
/**
* Month name to integer
*
* @param string $str Month name
*
* @return int month number
*/
protected function monthToInt($str)
{
$abbrs = array(true, false);
for ($a = 0; $a < count($abbrs); $a++)
{
for ($i = 0; $i < 13; $i++)
{
$month = $this->monthToString($i, $abbrs[$a]);
if (StringHelper::stristr($str, $month))
{
$monthNum = StringHelper::strlen($i) === 1 ? '0' . $i : $i;
$str = StringHelper::str_ireplace($month, $monthNum, $str);
}
}
}
return $str;
}
/**
* Converts strftime format into PHP date() format
*
* @param string $format Strftime format
*
* @since 3.0.7
*
* @return string converted format
*/
static public function strftimeFormatToDateFormat($format)
{
$app = Factory::getApplication();
if (strstr($format, '%C'))
{
$app->enqueueMessage('Cant convert %C strftime date format to
date format, substituted with Y', 'notice');
return;
}
$search = array('%e', '%j', '%u',
'%V', '%W', '%h', '%B',
'%C', '%g', '%G', '%M',
'%P', '%r', '%R', '%T',
'%X', '%z', '%Z', '%D',
'%F', '%s',
'%x', '%A', '%Y', '%m',
'%d', '%H', '%S');
$replace = array('j', 'z', 'w',
'W', 'W', 'M', 'F', 'Y',
'y', 'Y', 'i', 'a',
'"g:i:s a', 'H:i', 'H:i:s',
'H:i:s', 'O', 'O', 'm/d/y"',
'Y-m-d', 'U',
'Y-m-d', 'l', 'Y', 'm',
'd', 'H', 's');
return str_replace($search, $replace, $format);
}
/**
* Convert strftime to PHP time format
*
* @param string $format Format
*
* @return string converted format
*/
static public function dateFormatToStrftimeFormat($format)
{
$trs = array(
'd' => '%d',
'D' => '%a',
'j' => '%d', //@@@trob: force j to %d (instead of
the 'related' %e which is not working in form view);
'l' => '%A',
'N' => '%u',
'S' => '',
'w' => '%w',
'z' => '%j',
'W' => '%V',
'F' => '%B',
'm' => '%m',
'M' => '%b',
'n' => '%m',
't' => '',
'L' => '',
'o' => '%g',
'Y' => '%Y',
'y' => '%y',
'a' => '%P',
'A' => '%p',
'B' => '',
'g' => '%l',
'G' => '%H',
'h' => '%I',
'H' => '%H',
'i' => '%M',
's' => '%S',
'e' => '%z',
'u' => '',
'I' => '',
'O' => '',
'P' => '',
'T' => '%z',
'Z' => '',
'c' => '%c',
'r' => '%a, %d %b %Y %H:%M:%S %z',
'U' => '%s'
);
return strtr($format, $trs);
}
/**
* Strip days
*
* @param string $str Date string
*
* @return string date without days
*/
protected function stripDays($str)
{
$abbrs = array(true, false);
for ($a = 0; $a < count($abbrs); $a++)
{
for ($i = 0; $i < 7; $i++)
{
$day = $this->dayToString($i, $abbrs[$a]);
if (StringHelper::stristr($str, $day))
{
$str = StringHelper::str_ireplace($day, '', $str);
}
}
}
return $str;
}
}