xref: /plugin/struct/types/DateTime.php (revision 8ec87814898d9c0f4ea1082a8bacaa17ac6b1bf7)
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