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 13 protected $config = array( 14 'format' => '', // 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 = array( 52 'name' => $name, 53 'value' => $rawvalue, 54 'class' => 'struct_datetime', 55 'type' => 'datetime-local', // HTML5 datetime picker 56 'id' => $htmlID, 57 ); 58 $attributes = buildAttributes($params, true); 59 return "<input $attributes />"; 60 } 61 62 /** 63 * Validate a single value 64 * 65 * This function needs to throw a validation exception when validation fails. 66 * The exception message will be prefixed by the appropriate field on output 67 * 68 * @param string|array $rawvalue 69 * @return string 70 * @throws ValidationException 71 */ 72 public function validate($rawvalue) 73 { 74 $rawvalue = trim($rawvalue); 75 list($date, $time) = preg_split('/[ |T]/', $rawvalue, 2); 76 $date = trim($date); 77 $time = trim($time); 78 79 list($year, $month, $day) = explode('-', $date, 3); 80 if (!checkdate((int) $month, (int) $day, (int) $year)) { 81 throw new ValidationException('invalid datetime format'); 82 } 83 if ($this->config['pastonly'] && strtotime($rawvalue) > time()) { 84 throw new ValidationException('pastonly'); 85 } 86 if ($this->config['futureonly'] && strtotime($rawvalue) < time()) { 87 throw new ValidationException('futureonly'); 88 } 89 90 list($h, $m) = explode(':', $time, 3); // drop seconds 91 $h = (int) $h; 92 $m = (int) $m; 93 if ($h < 0 || $h > 23 || $m < 0 || $m > 59) { 94 throw new ValidationException('invalid datetime format'); 95 } 96 97 return sprintf("%d-%02d-%02d %02d:%02d", $year, $month, $day, $h, $m); 98 } 99 100 /** 101 * @param QueryBuilder $QB 102 * @param string $tablealias 103 * @param string $colname 104 * @param string $alias 105 */ 106 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) 107 { 108 $col = "$tablealias.$colname"; 109 110 // when accessing the revision column we need to convert from Unix timestamp 111 if (is_a($this->context, 'dokuwiki\plugin\struct\meta\RevisionColumn')) { 112 $rightalias = $QB->generateTableAlias(); 113 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid"); 114 $col = "DATETIME($rightalias.lastrev, 'unixepoch', 'localtime')"; 115 } 116 117 $QB->addSelectStatement($col, $alias); 118 } 119 120 /** 121 * @param QueryBuilderWhere $add 122 * @param string $tablealias 123 * @param string $colname 124 * @param string $comp 125 * @param string|\string[] $value 126 * @param string $op 127 */ 128 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 129 { 130 $col = "$tablealias.$colname"; 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 $QB = $add->getQB(); 135 $rightalias = $QB->generateTableAlias(); 136 $col = "DATETIME($rightalias.lastrev, 'unixepoch', 'localtime')"; 137 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid"); 138 } 139 140 /** @var QueryBuilderWhere $add Where additionional queries are added to*/ 141 if (is_array($value)) { 142 $add = $add->where($op); // sub where group 143 $op = 'OR'; 144 } 145 foreach ((array) $value as $item) { 146 $pl = $QB->addValue($item); 147 $add->where($op, "$col $comp $pl"); 148 } 149 } 150 151 /** 152 * When sorting `%lastupdated%`, then sort the data from the `titles` table instead the `data_` table. 153 * 154 * @param QueryBuilder $QB 155 * @param string $tablealias 156 * @param string $colname 157 * @param string $order 158 */ 159 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) 160 { 161 $col = "$tablealias.$colname"; 162 163 if (is_a($this->context, 'dokuwiki\plugin\struct\meta\RevisionColumn')) { 164 $rightalias = $QB->generateTableAlias(); 165 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid"); 166 $col = "$rightalias.lastrev"; 167 } 168 169 $QB->addOrderBy("$col $order"); 170 } 171} 172