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