Dotclear

source: inc/core/class.dc.meta.php @ 3733:ee69b09370ec

Revision 3733:ee69b09370ec, 18.9 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Add latest (associated entry date) and oldest (associated entry date) info when request list of metadata: it will provide a way to sort these metadata.

Plugin Tags updated in order to use this new feature (widget and templates)

Line 
1<?php
2/**
3 * @brief Dotclear metadata class.
4 *
5 * Dotclear metadata class instance is provided by dcCore $meta property.
6 *
7 * @package Dotclear
8 * @subpackage Core
9 *
10 * @copyright Olivier Meunier & Association Dotclear
11 * @copyright GPL-2.0-only
12 */
13
14if (!defined('DC_RC_PATH')) {return;}
15
16class dcMeta
17{
18    private $core;  ///< <b>dcCore</b> dcCore instance
19    private $con;   ///< <b>connection</b>    Database connection object
20    private $table; ///< <b>string</b> Media table name
21
22    /**
23    Object constructor.
24
25    @param    core        <b>dcCore</b>        dcCore instance
26     */
27    public function __construct($core)
28    {
29        $this->core  = &$core;
30        $this->con   = &$this->core->con;
31        $this->table = $this->core->prefix . 'meta';
32    }
33
34    /**
35    Splits up comma-separated values into an array of
36    unique, URL-proof metadata values.
37
38    @param    str        <b>string</b>        Comma-separated metadata.
39
40    @return    <b>Array</b>    The array of sanitized metadata
41     */
42    public function splitMetaValues($str)
43    {
44        $res = array();
45        foreach (explode(',', $str) as $i => $tag) {
46            $tag = trim($tag);
47            $tag = self::sanitizeMetaID($tag);
48
49            if ($tag != false) {
50                $res[$i] = $tag;
51            }
52        }
53
54        return array_unique($res);
55    }
56
57    /**
58    Make a metadata ID URL-proof.
59
60    @param    str        <b>string</b>    the metadata ID.
61
62    @return    <b>string</b>    The sanitized metadata
63     */
64    public static function sanitizeMetaID($str)
65    {
66        return text::tidyURL($str, false, true);
67    }
68
69    /**
70    Converts serialized metadata (for instance in dc_post post_meta)
71    into a meta array.
72
73    @param    str        <b>string</b>    the serialized metadata.
74
75    @return    <b>Array</b>    the resulting array of post meta
76     */
77    public function getMetaArray($str)
78    {
79        $meta = @unserialize($str);
80
81        if (!is_array($meta)) {
82            return array();
83        }
84
85        return $meta;
86    }
87
88    /**
89    Converts serialized metadata (for instance in dc_post post_meta)
90    into a comma-separated meta list for a given type.
91
92    @param    str        <b>string</b>    the serialized metadata.
93    @param    type    <b>string</b>    meta type to retrieve metaIDs from.
94
95    @return    <b>string</b>    the comma-separated list of meta
96     */
97    public function getMetaStr($str, $type)
98    {
99        $meta = $this->getMetaArray($str);
100
101        if (!isset($meta[$type])) {
102            return '';
103        }
104
105        return implode(', ', $meta[$type]);
106    }
107
108    /**
109    Converts serialized metadata (for instance in dc_post post_meta)
110    into a "fetchable" metadata record.
111
112    @param    str        <b>string</b>    the serialized metadata.
113    @param    type    <b>string</b>    meta type to retrieve metaIDs from.
114
115    @return    <b>record</b>    the meta recordset
116     */
117    public function getMetaRecordset($str, $type)
118    {
119        $meta = $this->getMetaArray($str);
120        $data = array();
121
122        if (isset($meta[$type])) {
123            foreach ($meta[$type] as $v) {
124                $data[] = array(
125                    'meta_id'       => $v,
126                    'meta_type'     => $type,
127                    'meta_id_lower' => mb_strtolower($v),
128                    'count'         => 0,
129                    'percent'       => 0,
130                    'roundpercent'  => 0
131                );
132            }
133        }
134
135        return staticRecord::newFromArray($data);
136    }
137
138    /**
139    @deprecated since version 2.2 : $core->meta is always defined
140    @see getMetaRecordset
141    static version of getMetaRecordset
142     */
143    public static function getMetaRecord($core, $str, $type)
144    {
145        $meta = new self($core);
146        return $meta->getMetaRecordset($str, $type);
147    }
148
149    /**
150    Checks whether the current user is allowed to change post meta
151    An exception is thrown if user is not allowed.
152
153    @param    post_id    <b>string</b>    the post_id to check.
154     */
155    private function checkPermissionsOnPost($post_id)
156    {
157        $post_id = (integer) $post_id;
158
159        if (!$this->core->auth->check('usage,contentadmin', $this->core->blog->id)) {
160            throw new Exception(__('You are not allowed to change this entry status'));
161        }
162
163        #�If user can only publish, we need to check the post's owner
164        if (!$this->core->auth->check('contentadmin', $this->core->blog->id)) {
165            $strReq = 'SELECT post_id ' .
166            'FROM ' . $this->core->prefix . 'post ' .
167            'WHERE post_id = ' . $post_id . ' ' .
168            "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
169
170            $rs = $this->con->select($strReq);
171
172            if ($rs->isEmpty()) {
173                throw new Exception(__('You are not allowed to change this entry status'));
174            }
175        }
176    }
177
178    /**
179    Updates serialized post_meta information with dc_meta table information.
180
181    @param    post_id    <b>string</b>    the post_id to update.
182     */
183    private function updatePostMeta($post_id)
184    {
185        $post_id = (integer) $post_id;
186
187        $strReq = 'SELECT meta_id, meta_type ' .
188        'FROM ' . $this->table . ' ' .
189            'WHERE post_id = ' . $post_id . ' ';
190
191        $rs = $this->con->select($strReq);
192
193        $meta = array();
194        while ($rs->fetch()) {
195            $meta[$rs->meta_type][] = $rs->meta_id;
196        }
197
198        $post_meta = serialize($meta);
199
200        $cur            = $this->con->openCursor($this->core->prefix . 'post');
201        $cur->post_meta = $post_meta;
202
203        $cur->update('WHERE post_id = ' . $post_id);
204        $this->core->blog->triggerBlog();
205    }
206
207    /**
208    Retrieves posts corresponding to given meta criteria.
209    <b>$params</b> is an array taking the following optional parameters:
210    - meta_id : get posts having meta id
211    - meta_type : get posts having meta type
212
213    @param    params    <b>array</b>    Parameters
214    @param    count_only    <b>boolean</b>        Only counts results
215
216    @return    <b>record</b>    the resulting posts record
217     */
218    public function getPostsByMeta($params = array(), $count_only = false)
219    {
220        if (!isset($params['meta_id'])) {
221            return;
222        }
223
224        $params['from'] = ', ' . $this->table . ' META ';
225        $params['sql']  = 'AND META.post_id = P.post_id ';
226
227        $params['sql'] .= "AND META.meta_id = '" . $this->con->escape($params['meta_id']) . "' ";
228
229        if (!empty($params['meta_type'])) {
230            $params['sql'] .= "AND META.meta_type = '" . $this->con->escape($params['meta_type']) . "' ";
231            unset($params['meta_type']);
232        }
233
234        unset($params['meta_id']);
235
236        return $this->core->blog->getPosts($params, $count_only);
237    }
238
239    /**
240    Retrieves comments to posts corresponding to given meta criteria.
241    <b>$params</b> is an array taking the following optional parameters:
242    - meta_id : get comments to posts having meta id
243    - meta_type : get comments to posts having meta type
244
245    @param    params    <b>array</b>    Parameters
246    @param    count_only    <b>boolean</b>        Only counts results
247
248    @return    <b>record</b>    the resulting comments record
249     */
250    public function getCommentsByMeta($params = array(), $count_only = false)
251    {
252        if (!isset($params['meta_id'])) {
253            return;
254        }
255
256        $params['from'] = ', ' . $this->table . ' META ';
257        $params['sql']  = 'AND META.post_id = P.post_id ';
258        $params['sql'] .= "AND META.meta_id = '" . $this->con->escape($params['meta_id']) . "' ";
259
260        if (!empty($params['meta_type'])) {
261            $params['sql'] .= "AND META.meta_type = '" . $this->con->escape($params['meta_type']) . "' ";
262            unset($params['meta_type']);
263        }
264
265        return $this->core->blog->getComments($params, $count_only);
266    }
267
268    /**
269    @deprecated since 2.2. Use getMetadata and computeMetaStats instead.
270    Generic-purpose metadata retrieval : gets metadatas according to given
271    criteria. Metadata get enriched with stastistics columns (only relevant
272    if limit parameter is not set). Metadata are sorted by post count
273    descending
274
275    @param    type    <b>string</b>    if not null, get metas having the given type
276    @param    limit    <b>string</b>    if not null, number of max fetched metas
277    @param    meta_id    <b>string</b>    if not null, get metas having the given id
278    @param    post_id    <b>string</b>    if not null, get metas for the given post id
279
280    @return    <b>record</b>    the meta recordset
281     */
282    public function getMeta($type = null, $limit = null, $meta_id = null, $post_id = null)
283    {
284        $params = array();
285
286        if ($type != null) {
287            $params['meta_type'] = $type;
288        }
289
290        if ($limit != null) {
291            $params['limit'] = $limit;
292        }
293
294        if ($meta_id != null) {
295            $params['meta_id'] = $meta_id;
296        }
297
298        if ($meta_id != null) {
299            $params['post_id'] = $post_id;
300        }
301
302        $rs = $this->getMetadata($params, false);
303        return $this->computeMetaStats($rs);
304    }
305
306    /**
307    Generic-purpose metadata retrieval : gets metadatas according to given
308    criteria. <b>$params</b> is an array taking the following
309    optionnal parameters:
310
311    - type: get metas having the given type
312    - meta_id: if not null, get metas having the given id
313    - post_id: get metas for the given post id
314    - limit: number of max fetched metas
315    - order: results order (default : posts count DESC)
316
317    @param    params        <b>array</b>        Parameters
318    @param    count_only    <b>boolean</b>        Only counts results
319
320    @return    <b>record</b>    the resulting comments record
321     */
322    public function getMetadata($params = array(), $count_only = false)
323    {
324        if ($count_only) {
325            $strReq = 'SELECT count(distinct M.meta_id) ';
326        } else {
327            $strReq = 'SELECT M.meta_id, M.meta_type, COUNT(M.post_id) as count, MAX(P.post_dt) as latest, MIN(P.post_dt) as oldest ';
328        }
329
330        $strReq .=
331        'FROM ' . $this->table . ' M LEFT JOIN ' . $this->core->prefix . 'post P ' .
332        'ON M.post_id = P.post_id ' .
333        "WHERE P.blog_id = '" . $this->con->escape($this->core->blog->id) . "' ";
334
335        if (isset($params['meta_type'])) {
336            $strReq .= " AND meta_type = '" . $this->con->escape($params['meta_type']) . "' ";
337        }
338
339        if (isset($params['meta_id'])) {
340            $strReq .= " AND meta_id = '" . $this->con->escape($params['meta_id']) . "' ";
341        }
342
343        if (isset($params['post_id'])) {
344            $strReq .= ' AND P.post_id ' . $this->con->in($params['post_id']) . ' ';
345        }
346
347        if (!$this->core->auth->check('contentadmin', $this->core->blog->id)) {
348            $strReq .= 'AND ((post_status = 1 ';
349
350            if ($this->core->blog->without_password) {
351                $strReq .= 'AND post_password IS NULL ';
352            }
353            $strReq .= ') ';
354
355            if ($this->core->auth->userID()) {
356                $strReq .= "OR P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
357            } else {
358                $strReq .= ') ';
359            }
360        }
361
362        if (!$count_only) {
363            if (!isset($params['order'])) {
364                $params['order'] = 'count DESC';
365            }
366
367            $strReq .=
368                'GROUP BY meta_id,meta_type,P.blog_id ' .
369                'ORDER BY ' . $params['order'];
370
371            if (isset($params['limit'])) {
372                $strReq .= $this->con->limit($params['limit']);
373            }
374        }
375
376        $rs = $this->con->select($strReq);
377        return $rs;
378    }
379
380    /**
381    Computes statistics from a metadata recordset.
382    Each record gets enriched with lowercase name, percent and roundpercent columns
383
384    @param    rs    <b>record</b>    recordset to enrich
385
386    @return    <b>record</b>    the enriched recordset
387     */
388    public function computeMetaStats($rs)
389    {
390        $rs_static = $rs->toStatic();
391
392        $max = array();
393        while ($rs_static->fetch()) {
394            $type = $rs_static->meta_type;
395            if (!isset($max[$type])) {
396                $max[$type] = $rs_static->count;
397            } else {
398                if ($rs_static->count > $max[$type]) {
399                    $max[$type] = $rs_static->count;
400                }
401            }
402        }
403
404        while ($rs_static->fetch()) {
405            $rs_static->set('meta_id_lower', dcUtils::removeDiacritics(mb_strtolower($rs_static->meta_id)));
406
407            $count   = $rs_static->count;
408            $percent = ((integer) $rs_static->count) * 100 / $max[$rs_static->meta_type];
409
410            $rs_static->set('percent', (integer) round($percent));
411            $rs_static->set('roundpercent', round($percent / 10) * 10);
412        }
413
414        return $rs_static;
415    }
416
417    /**
418    Adds a metadata to a post.
419
420    @param    post_id    <b>integer</b>    the post id
421    @param    type    <b>string</b>    meta type
422    @param    value    <b>integer</b>    meta value
423     */
424    public function setPostMeta($post_id, $type, $value)
425    {
426        $this->checkPermissionsOnPost($post_id);
427
428        $value = trim($value);
429        if ($value === false) {return;}
430
431        $cur = $this->con->openCursor($this->table);
432
433        $cur->post_id   = (integer) $post_id;
434        $cur->meta_id   = (string) $value;
435        $cur->meta_type = (string) $type;
436
437        $cur->insert();
438        $this->updatePostMeta((integer) $post_id);
439    }
440
441    /**
442    Removes metadata from a post.
443
444    @param    post_id    <b>integer</b>    the post id
445    @param    type    <b>string</b>    meta type (if null, delete all types)
446    @param    value    <b>integer</b>    meta value (if null, delete all values)
447     */
448    public function delPostMeta($post_id, $type = null, $meta_id = null)
449    {
450        $post_id = (integer) $post_id;
451
452        $this->checkPermissionsOnPost($post_id);
453
454        $strReq = 'DELETE FROM ' . $this->table . ' ' .
455            'WHERE post_id = ' . $post_id;
456
457        if ($type !== null) {
458            $strReq .= " AND meta_type = '" . $this->con->escape($type) . "' ";
459        }
460
461        if ($meta_id !== null) {
462            $strReq .= " AND meta_id = '" . $this->con->escape($meta_id) . "' ";
463        }
464
465        $this->con->execute($strReq);
466        $this->updatePostMeta((integer) $post_id);
467    }
468
469    /**
470    Mass updates metadata for a given post_type.
471
472    @param    meta_id        <b>integer</b>    old value
473    @param    new_meta    <b>integer</b>    new value
474    @param    type    <b>string</b>    meta type (if null, select all types)
475    @param    post_type    <b>integer</b>    impacted post_type (if null, select all types)
476    @return    <b>boolean</b>    true if at least 1 post has been impacted
477     */
478    public function updateMeta($meta_id, $new_meta_id, $type = null, $post_type = null)
479    {
480        $new_meta_id = self::sanitizeMetaID($new_meta_id);
481
482        if ($new_meta_id == $meta_id) {
483            return true;
484        }
485
486        $getReq = 'SELECT M.post_id ' .
487        'FROM ' . $this->table . ' M, ' . $this->core->prefix . 'post P ' .
488        'WHERE P.post_id = M.post_id ' .
489        "AND P.blog_id = '" . $this->con->escape($this->core->blog->id) . "' " .
490            "AND meta_id = '%s' ";
491
492        if (!$this->core->auth->check('contentadmin', $this->core->blog->id)) {
493            $getReq .= "AND P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
494        }
495        if ($post_type !== null) {
496            $getReq .= "AND P.post_type = '" . $this->con->escape($post_type) . "' ";
497        }
498
499        $delReq = 'DELETE FROM ' . $this->table . ' ' .
500            'WHERE post_id IN (%s) ' .
501            "AND meta_id = '%s' ";
502
503        $updReq = 'UPDATE ' . $this->table . ' ' .
504            "SET meta_id = '%s' " .
505            'WHERE post_id IN (%s) ' .
506            "AND meta_id = '%s' ";
507
508        if ($type !== null) {
509            $plus = " AND meta_type = '%s' ";
510            $getReq .= $plus;
511            $delReq .= $plus;
512            $updReq .= $plus;
513        }
514
515        $to_update = $to_remove = array();
516
517        $rs = $this->con->select(sprintf($getReq, $this->con->escape($meta_id),
518            $this->con->escape($type)));
519
520        while ($rs->fetch()) {
521            $to_update[] = $rs->post_id;
522        }
523
524        if (empty($to_update)) {
525            return false;
526        }
527
528        $rs = $this->con->select(sprintf($getReq, $new_meta_id, $type));
529        while ($rs->fetch()) {
530            if (in_array($rs->post_id, $to_update)) {
531                $to_remove[] = $rs->post_id;
532                unset($to_update[array_search($rs->post_id, $to_update)]);
533            }
534        }
535
536        # Delete duplicate meta
537        if (!empty($to_remove)) {
538            $this->con->execute(sprintf($delReq, implode(',', $to_remove),
539                $this->con->escape($meta_id),
540                $this->con->escape($type)));
541
542            foreach ($to_remove as $post_id) {
543                $this->updatePostMeta($post_id);
544            }
545        }
546
547        # Update meta
548        if (!empty($to_update)) {
549            $this->con->execute(sprintf($updReq, $this->con->escape($new_meta_id),
550                implode(',', $to_update),
551                $this->con->escape($meta_id),
552                $this->con->escape($type)));
553
554            foreach ($to_update as $post_id) {
555                $this->updatePostMeta($post_id);
556            }
557        }
558
559        return true;
560    }
561
562    /**
563    Mass delete metadata for a given post_type.
564
565    @param    meta_id        <b>integer</b>    meta value
566    @param    type    <b>string</b>    meta type (if null, select all types)
567    @param    post_type    <b>integer</b>    impacted post_type (if null, select all types)
568    @return    <b>Array</b>    the list of impacted post_ids
569     */
570    public function delMeta($meta_id, $type = null, $post_type = null)
571    {
572        $strReq = 'SELECT M.post_id ' .
573        'FROM ' . $this->table . ' M, ' . $this->core->prefix . 'post P ' .
574        'WHERE P.post_id = M.post_id ' .
575        "AND P.blog_id = '" . $this->con->escape($this->core->blog->id) . "' " .
576        "AND meta_id = '" . $this->con->escape($meta_id) . "' ";
577
578        if ($type !== null) {
579            $strReq .= " AND meta_type = '" . $this->con->escape($type) . "' ";
580        }
581
582        if ($post_type !== null) {
583            $strReq .= " AND P.post_type = '" . $this->con->escape($post_type) . "' ";
584        }
585
586        $rs = $this->con->select($strReq);
587
588        if ($rs->isEmpty()) {
589            return array();
590        }
591
592        $ids = array();
593        while ($rs->fetch()) {
594            $ids[] = $rs->post_id;
595        }
596
597        $strReq = 'DELETE FROM ' . $this->table . ' ' .
598        'WHERE post_id IN (' . implode(',', $ids) . ') ' .
599        "AND meta_id = '" . $this->con->escape($meta_id) . "' ";
600
601        if ($type !== null) {
602            $strReq .= " AND meta_type = '" . $this->con->escape($type) . "' ";
603        }
604
605        $rs = $this->con->execute($strReq);
606
607        foreach ($ids as $post_id) {
608            $this->updatePostMeta($post_id);
609        }
610
611        return $ids;
612    }
613}
Note: See TracBrowser for help on using the repository browser.

Sites map