xref: /plugin/struct/types/DateTime.php (revision 7fe2cdf28c472c686961bf42f0123eb33d2f3e60)
14a2883e0SAndreas Gohr<?php
2d6d97f60SAnna Dabrowska
34a2883e0SAndreas Gohrnamespace dokuwiki\plugin\struct\types;
44a2883e0SAndreas Gohr
564cf7cc6SAndreas Gohruse dokuwiki\plugin\struct\meta\DateFormatConverter;
6b92072ddSAndreas Gohruse dokuwiki\plugin\struct\meta\QueryBuilder;
7b92072ddSAndreas Gohruse dokuwiki\plugin\struct\meta\QueryBuilderWhere;
84a2883e0SAndreas Gohruse dokuwiki\plugin\struct\meta\ValidationException;
94a2883e0SAndreas Gohr
10d6d97f60SAnna Dabrowskaclass DateTime extends Date
11d6d97f60SAnna Dabrowska{
127234bfb1Ssplitbrain    protected $config = [
13*7fe2cdf2SAndreas Gohr        'format' => '', // filled by constructor
14218490cfSAnna Dabrowska        'prefilltoday' => false,
15733fd33bSAnna Dabrowska        'pastonly' => false,
167234bfb1Ssplitbrain        'futureonly' => false,
177234bfb1Ssplitbrain    ];
184a2883e0SAndreas Gohr
194a2883e0SAndreas Gohr    /**
2064cf7cc6SAndreas Gohr     * DateTime constructor.
2164cf7cc6SAndreas Gohr     *
2264cf7cc6SAndreas Gohr     * @param array|null $config
2364cf7cc6SAndreas Gohr     * @param string $label
2464cf7cc6SAndreas Gohr     * @param bool $ismulti
2564cf7cc6SAndreas Gohr     * @param int $tid
2664cf7cc6SAndreas Gohr     */
27d6d97f60SAnna Dabrowska    public function __construct($config = null, $label = '', $ismulti = false, $tid = 0)
28d6d97f60SAnna Dabrowska    {
2964cf7cc6SAndreas Gohr        global $conf;
3064cf7cc6SAndreas Gohr        $this->config['format'] = DateFormatConverter::toDate($conf['dformat']);
3164cf7cc6SAndreas Gohr
3264cf7cc6SAndreas Gohr        parent::__construct($config, $label, $ismulti, $tid);
3364cf7cc6SAndreas Gohr    }
3464cf7cc6SAndreas Gohr
3564cf7cc6SAndreas Gohr    /**
364a2883e0SAndreas Gohr     * Return the editor to edit a single value
374a2883e0SAndreas Gohr     *
384a2883e0SAndreas Gohr     * @param string $name the form name where this has to be stored
39c0230d2cSAndreas Gohr     * @param string $rawvalue the current value
40ee983135SMichael Große     * @param string $htmlID
41ee983135SMichael Große     *
424a2883e0SAndreas Gohr     * @return string html
434a2883e0SAndreas Gohr     */
44d6d97f60SAnna Dabrowska    public function valueEditor($name, $rawvalue, $htmlID)
45d6d97f60SAnna Dabrowska    {
46c0230d2cSAndreas Gohr        if ($this->config['prefilltoday'] && !$rawvalue) {
4716daff3dSAnna Dabrowska            $rawvalue = date('Y-m-d\TH:i');
484a2883e0SAndreas Gohr        }
49b80ca5e1SMichael Große        $rawvalue = str_replace(' ', 'T', $rawvalue);
507234bfb1Ssplitbrain        $params = [
513e7a5b3cSMichael Große            'name' => $name,
523e7a5b3cSMichael Große            'value' => $rawvalue,
533e7a5b3cSMichael Große            'class' => 'struct_datetime',
54*7fe2cdf2SAndreas Gohr            'type' => 'datetime-local', // HTML5 datetime picker
553e7a5b3cSMichael Große            'id' => $htmlID,
567234bfb1Ssplitbrain        ];
573e7a5b3cSMichael Große        $attributes = buildAttributes($params, true);
583e7a5b3cSMichael Große        return "<input $attributes />";
594a2883e0SAndreas Gohr    }
604a2883e0SAndreas Gohr
614a2883e0SAndreas Gohr    /**
624a2883e0SAndreas Gohr     * Validate a single value
634a2883e0SAndreas Gohr     *
644a2883e0SAndreas Gohr     * This function needs to throw a validation exception when validation fails.
654a2883e0SAndreas Gohr     * The exception message will be prefixed by the appropriate field on output
664a2883e0SAndreas Gohr     *
6723169abeSAndreas Gohr     * @param string|array $rawvalue
684a2883e0SAndreas Gohr     * @return string
694a2883e0SAndreas Gohr     * @throws ValidationException
704a2883e0SAndreas Gohr     */
71d6d97f60SAnna Dabrowska    public function validate($rawvalue)
72d6d97f60SAnna Dabrowska    {
7323169abeSAndreas Gohr        $rawvalue = trim($rawvalue);
747234bfb1Ssplitbrain        [$date, $time] = array_pad(preg_split('/[ |T]/', $rawvalue, 2), 2, '');
754a2883e0SAndreas Gohr        $date = trim($date);
764a2883e0SAndreas Gohr        $time = trim($time);
774a2883e0SAndreas Gohr
787234bfb1Ssplitbrain        [$year, $month, $day] = explode('-', $date, 3);
794978faf1SAndreas Gohr        if (!checkdate((int)$month, (int)$day, (int)$year)) {
804978faf1SAndreas Gohr            throw new ValidationException('invalid datetime format');
814a2883e0SAndreas Gohr        }
82733fd33bSAnna Dabrowska        if ($this->config['pastonly'] && strtotime($rawvalue) > time()) {
83d8bfcd03SAnna Dabrowska            throw new ValidationException('pastonly');
84218490cfSAnna Dabrowska        }
85733fd33bSAnna Dabrowska        if ($this->config['futureonly'] && strtotime($rawvalue) < time()) {
86d8bfcd03SAnna Dabrowska            throw new ValidationException('futureonly');
87218490cfSAnna Dabrowska        }
884a2883e0SAndreas Gohr
897234bfb1Ssplitbrain        [$h, $m] = array_pad(explode(':', $time, 3), 2, ''); // drop seconds
904a2883e0SAndreas Gohr        $h = (int)$h;
914a2883e0SAndreas Gohr        $m = (int)$m;
92b80ca5e1SMichael Große        if ($h < 0 || $h > 23 || $m < 0 || $m > 59) {
934a2883e0SAndreas Gohr            throw new ValidationException('invalid datetime format');
944a2883e0SAndreas Gohr        }
954a2883e0SAndreas Gohr
96b80ca5e1SMichael Große        return sprintf("%d-%02d-%02d %02d:%02d", $year, $month, $day, $h, $m);
974a2883e0SAndreas Gohr    }
984a2883e0SAndreas Gohr
99b92072ddSAndreas Gohr    /**
100b92072ddSAndreas Gohr     * @param QueryBuilder $QB
101b92072ddSAndreas Gohr     * @param string $tablealias
102b92072ddSAndreas Gohr     * @param string $colname
103b92072ddSAndreas Gohr     * @param string $alias
104b92072ddSAndreas Gohr     */
105d6d97f60SAnna Dabrowska    public function select(QueryBuilder $QB, $tablealias, $colname, $alias)
106d6d97f60SAnna Dabrowska    {
107b92072ddSAndreas Gohr        $col = "$tablealias.$colname";
108e9259fa1SMichael Grosse
109e9259fa1SMichael Grosse        // when accessing the revision column we need to convert from Unix timestamp
110e9259fa1SMichael Grosse        if (is_a($this->context, 'dokuwiki\plugin\struct\meta\RevisionColumn')) {
111e9259fa1SMichael Grosse            $rightalias = $QB->generateTableAlias();
112e9259fa1SMichael Grosse            $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid");
113e9259fa1SMichael Grosse            $col = "DATETIME($rightalias.lastrev, 'unixepoch', 'localtime')";
114b92072ddSAndreas Gohr        }
115b92072ddSAndreas Gohr
116b92072ddSAndreas Gohr        $QB->addSelectStatement($col, $alias);
117b92072ddSAndreas Gohr    }
118b92072ddSAndreas Gohr
119b92072ddSAndreas Gohr    /**
120af993d55SMichael Grosse     * @param QueryBuilderWhere $add
121b92072ddSAndreas Gohr     * @param string $tablealias
122b92072ddSAndreas Gohr     * @param string $colname
123b92072ddSAndreas Gohr     * @param string $comp
124b92072ddSAndreas Gohr     * @param string|\string[] $value
125b92072ddSAndreas Gohr     * @param string $op
126b92072ddSAndreas Gohr     */
127d6d97f60SAnna Dabrowska    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op)
128d6d97f60SAnna Dabrowska    {
129b92072ddSAndreas Gohr        $col = "$tablealias.$colname";
13089579610SChris MacMackin        $QB = $add->getQB();
131e9259fa1SMichael Grosse
132e9259fa1SMichael Grosse        // when accessing the revision column we need to convert from Unix timestamp
133e9259fa1SMichael Grosse        if (is_a($this->context, 'dokuwiki\plugin\struct\meta\RevisionColumn')) {
134e9259fa1SMichael Grosse            $rightalias = $QB->generateTableAlias();
135e9259fa1SMichael Grosse            $col = "DATETIME($rightalias.lastrev, 'unixepoch', 'localtime')";
136e9259fa1SMichael Grosse            $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid");
137b92072ddSAndreas Gohr        }
138b92072ddSAndreas Gohr
1391ca21e17SAnna Dabrowska        /** @var QueryBuilderWhere $add Where additional queries are added to */
140b92072ddSAndreas Gohr        if (is_array($value)) {
141af993d55SMichael Grosse            $add = $add->where($op); // sub where group
142b92072ddSAndreas Gohr            $op = 'OR';
143b92072ddSAndreas Gohr        }
144b92072ddSAndreas Gohr        foreach ((array)$value as $item) {
145b92072ddSAndreas Gohr            $pl = $QB->addValue($item);
146b92072ddSAndreas Gohr            $add->where($op, "$col $comp $pl");
147b92072ddSAndreas Gohr        }
148b92072ddSAndreas Gohr    }
149b92072ddSAndreas Gohr
150e9259fa1SMichael Grosse    /**
151e9259fa1SMichael Grosse     * When sorting `%lastupdated%`, then sort the data from the `titles` table instead the `data_` table.
152e9259fa1SMichael Grosse     *
153e9259fa1SMichael Grosse     * @param QueryBuilder $QB
154e9259fa1SMichael Grosse     * @param string $tablealias
155e9259fa1SMichael Grosse     * @param string $colname
156e9259fa1SMichael Grosse     * @param string $order
157e9259fa1SMichael Grosse     */
158d6d97f60SAnna Dabrowska    public function sort(QueryBuilder $QB, $tablealias, $colname, $order)
159d6d97f60SAnna Dabrowska    {
160e9259fa1SMichael Grosse        $col = "$tablealias.$colname";
161e9259fa1SMichael Grosse
162e9259fa1SMichael Grosse        if (is_a($this->context, 'dokuwiki\plugin\struct\meta\RevisionColumn')) {
163e9259fa1SMichael Grosse            $rightalias = $QB->generateTableAlias();
164e9259fa1SMichael Grosse            $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid");
165e9259fa1SMichael Grosse            $col = "$rightalias.lastrev";
166e9259fa1SMichael Grosse        }
167e9259fa1SMichael Grosse
168e9259fa1SMichael Grosse        $QB->addOrderBy("$col $order");
169e9259fa1SMichael Grosse    }
1704a2883e0SAndreas Gohr}
171