PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/inc/core/class.dc.meta.php

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