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