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