1<?php 2 3namespace dokuwiki\plugin\struct\types; 4 5use dokuwiki\plugin\struct\meta\DateFormatConverter; 6use dokuwiki\plugin\struct\meta\QueryBuilder; 7use dokuwiki\plugin\struct\meta\QueryBuilderWhere; 8use dokuwiki\plugin\struct\meta\ValidationException; 9 10class DateTime extends Date 11{ 12 protected $config = [ 13 'format' => '', // filled by constructor 14 'prefilltoday' => false, 15 'pastonly' => false, 16 'futureonly' => false, 17 ]; 18 19 /** 20 * DateTime constructor. 21 * 22 * @param array|null $config 23 * @param string $label 24 * @param bool $ismulti 25 * @param int $tid 26 */ 27 public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) 28 { 29 global $conf; 30 $this->config['format'] = DateFormatConverter::toDate($conf['dformat']); 31 32 parent::__construct($config, $label, $ismulti, $tid); 33 } 34 35 /** 36 * Return the editor to edit a single value 37 * 38 * @param string $name the form name where this has to be stored 39 * @param string $rawvalue the current value 40 * @param string $htmlID 41 * 42 * @return string html 43 */ 44 public function valueEditor($name, $rawvalue, $htmlID) 45 { 46 if ($this->config['prefilltoday'] && !$rawvalue) { 47 $rawvalue = date('Y-m-d\TH:i'); 48 } 49 $rawvalue = str_replace(' ', 'T', $rawvalue); 50 $params = [ 51 'name' => $name, 52 'value' => $rawvalue, 53 'class' => 'struct_datetime', 54 'type' => 'datetime-local', // HTML5 datetime picker 55 'id' => $htmlID, 56 ]; 57 $attributes = buildAttributes($params, true); 58 return "<input $attributes />"; 59 } 60 61 /** 62 * Validate a single value 63 * 64 * This function needs to throw a validation exception when validation fails. 65 * The exception message will be prefixed by the appropriate field on output 66 * 67 * @param string|array $rawvalue 68 * @return string 69 * @throws ValidationException 70 */ 71 public function validate($rawvalue) 72 { 73 $rawvalue = trim($rawvalue); 74 [$date, $time] = array_pad(preg_split('/[ |T]/', $rawvalue, 2), 2, ''); 75 $date = trim($date); 76 $time = trim($time); 77 78 [$year, $month, $day] = explode('-', $date, 3); 79 if (!checkdate((int)$month, (int)$day, (int)$year)) { 80 throw new ValidationException('invalid datetime format'); 81 } 82 if ($this->config['pastonly'] && strtotime($rawvalue) > time()) { 83 throw new ValidationException('pastonly'); 84 } 85 if ($this->config['futureonly'] && strtotime($rawvalue) < time()) { 86 throw new ValidationException('futureonly'); 87 } 88 89 [$h, $m] = array_pad(explode(':', $time, 3), 2, ''); // drop seconds 90 $h = (int)$h; 91 $m = (int)$m; 92 if ($h < 0 || $h > 23 || $m < 0 || $m > 59) { 93 throw new ValidationException('invalid datetime format'); 94 } 95 96 return sprintf("%d-%02d-%02d %02d:%02d", $year, $month, $day, $h, $m); 97 } 98 99 /** 100 * @param QueryBuilder $QB 101 * @param string $tablealias 102 * @param string $colname 103 * @param string $alias 104 */ 105 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) 106 { 107 $col = "$tablealias.$colname"; 108 109 // when accessing the revision column we need to convert from Unix timestamp 110 if (is_a($this->context, 'dokuwiki\plugin\struct\meta\RevisionColumn')) { 111 $rightalias = $QB->generateTableAlias(); 112 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid"); 113 $col = "DATETIME($rightalias.lastrev, 'unixepoch', 'localtime')"; 114 } 115 116 $QB->addSelectStatement($col, $alias); 117 } 118 119 /** 120 * @param QueryBuilderWhere $add 121 * @param string $tablealias 122 * @param string $colname 123 * @param string $comp 124 * @param string|\string[] $value 125 * @param string $op 126 */ 127 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 128 { 129 $col = "$tablealias.$colname"; 130 $QB = $add->getQB(); 131 132 // when accessing the revision column we need to convert from Unix timestamp 133 if (is_a($this->context, 'dokuwiki\plugin\struct\meta\RevisionColumn')) { 134 $rightalias = $QB->generateTableAlias(); 135 $col = "DATETIME($rightalias.lastrev, 'unixepoch', 'localtime')"; 136 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid"); 137 } 138 139 /** @var QueryBuilderWhere $add Where additional queries are added to */ 140 if (is_array($value)) { 141 $add = $add->where($op); // sub where group 142 $op = 'OR'; 143 } 144 foreach ((array)$value as $item) { 145 $pl = $QB->addValue($item); 146 $add->where($op, "$col $comp $pl"); 147 } 148 } 149 150 /** 151 * When sorting `%lastupdated%`, then sort the data from the `titles` table instead the `data_` table. 152 * 153 * @param QueryBuilder $QB 154 * @param string $tablealias 155 * @param string $colname 156 * @param string $order 157 */ 158 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) 159 { 160 $col = "$tablealias.$colname"; 161 162 if (is_a($this->context, 'dokuwiki\plugin\struct\meta\RevisionColumn')) { 163 $rightalias = $QB->generateTableAlias(); 164 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid"); 165 $col = "$rightalias.lastrev"; 166 } 167 168 $QB->addOrderBy("$col $order"); 169 } 170} 171