Dotclear

source: inc/core/class.dc.meta.php @ 3057:c03560e93166

Revision 3057:c03560e93166, 15.7 KB checked in by franck <carnet.franck.paul@…>, 10 years ago (diff)

Lexical sort for tags, adresses #1850 and #2058

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

Sites map