PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/comment.php

https://github.com/HabariMag/habarimag-old
PHP | 566 lines | 354 code | 49 blank | 163 comment | 55 complexity | 65939f8e44d2e11d437bd3167d1e1b75 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. /**
  3. * @package Habari
  4. *
  5. */
  6. /**
  7. * Habari CommentRecord Class
  8. *
  9. * Includes an instance of the CommentInfo class; for holding inforecords about the comment
  10. * If the Comment object describes an existing user; use the internal info object to get, set, unset and test for existence (isset) of
  11. * info records
  12. * <code>
  13. * $this->info = new CommentInfo ( 1 ); // Info records of comment with id = 1
  14. * $this->info->browser_ua= "Netscape 2.0"; // set info record with name "browser_ua" to value "Netscape 2.0"
  15. * $info_value= $this->info->browser_ua; // get value of info record with name "browser_ua" into variable $info_value
  16. * if ( isset ($this->info->browser_ua) ) // test for existence of "browser_ua"
  17. * unset ( $this->info->browser_ua ); // delete "browser_ua" info record
  18. * </code>
  19. *
  20. */
  21. class Comment extends QueryRecord implements IsContent
  22. {
  23. // our definitions for comment types and statuses
  24. const STATUS_UNAPPROVED = 0;
  25. const STATUS_APPROVED = 1;
  26. const STATUS_SPAM = 2;
  27. const STATUS_DELETED = 3;
  28. const COMMENT = 0;
  29. const PINGBACK = 1;
  30. const TRACKBACK = 2;
  31. private $post_object = null;
  32. private $inforecords = null;
  33. // static variables to hold comment status and comment type values
  34. static $comment_status_list = array();
  35. static $comment_type_list = array();
  36. static $comment_status_actions = array();
  37. /**
  38. * static function default_fields
  39. * Returns the defined database columns for a comment
  40. */
  41. public static function default_fields()
  42. {
  43. return array(
  44. 'id' => 0,
  45. 'post_id' => 0,
  46. 'name' => '',
  47. 'email' => '',
  48. 'url' => '',
  49. 'ip' => 0,
  50. 'content' => '',
  51. 'status' => self::STATUS_UNAPPROVED,
  52. 'date' => HabariDateTime::date_create(),
  53. 'type' => self::COMMENT
  54. );
  55. }
  56. /**
  57. * constructor __construct
  58. * Constructor for the Post class.
  59. * @param array an associative array of initial Post field values.
  60. */
  61. public function __construct( $paramarray = array() )
  62. {
  63. // Defaults
  64. $this->fields = array_merge( self::default_fields(), $this->fields );
  65. parent::__construct( $paramarray );
  66. $this->exclude_fields( 'id' );
  67. /* $this->fields['id'] could be null in case of a new comment. If so, the info object is _not_ safe to use till after set_key has been called. Info records can be set immediately in any other case. */
  68. }
  69. /**
  70. * static function get
  71. * Returns a single comment, by ID
  72. *
  73. * <code>
  74. * $post = Post::get( 10 );
  75. * </code>
  76. *
  77. * @param int An ID
  78. * @return array A single Comment object
  79. */
  80. static function get( $id = 0 )
  81. {
  82. if ( ! $id ) {
  83. return false;
  84. }
  85. return DB::get_row( 'SELECT * FROM {comments} WHERE id = ?', array( $id ), 'Comment' );
  86. }
  87. /**
  88. * static function create
  89. * Creates a comment and saves it
  90. * @param array An associative array of comment fields
  91. * $return Comment The comment object that was created
  92. */
  93. static function create( $paramarray )
  94. {
  95. $comment = new Comment( $paramarray );
  96. $comment->insert();
  97. return $comment;
  98. }
  99. /**
  100. * function insert
  101. * Saves a new comment to the posts table
  102. */
  103. public function insert()
  104. {
  105. $allow = true;
  106. $allow = Plugins::filter( 'comment_insert_allow', $allow, $this );
  107. if ( ! $allow ) {
  108. return;
  109. }
  110. Plugins::act( 'comment_insert_before', $this );
  111. // Invoke plugins for all fields, since they're all "chnaged" when inserted
  112. foreach ( $this->fields as $fieldname => $value ) {
  113. Plugins::act( 'comment_update_' . $fieldname, $this, $this->$fieldname, $value );
  114. }
  115. $result = parent::insertRecord( DB::table( 'comments' ) );
  116. $this->newfields['id'] = DB::last_insert_id(); // Make sure the id is set in the comment object to match the row id
  117. $this->fields = array_merge( $this->fields, $this->newfields );
  118. $this->newfields = array();
  119. $this->info->commit( $this->fields['id'] );
  120. Plugins::act( 'comment_insert_after', $this );
  121. return $result;
  122. }
  123. /**
  124. * function update
  125. * Updates an existing comment in the posts table
  126. */
  127. public function update()
  128. {
  129. $allow = true;
  130. $allow = Plugins::filter( 'comment_update_allow', $allow, $this );
  131. if ( ! $allow ) {
  132. return;
  133. }
  134. Plugins::act( 'comment_update_before', $this );
  135. // invoke plugins for all fields which have been updated
  136. foreach ( $this->newfields as $fieldname => $value ) {
  137. Plugins::act( 'comment_update_' . $fieldname, $this, $this->fields[$fieldname], $value );
  138. }
  139. $result = parent::updateRecord( DB::table( 'comments' ), array( 'id'=>$this->id ) );
  140. $this->fields = array_merge( $this->fields, $this->newfields );
  141. $this->newfields = array();
  142. $this->info->commit();
  143. Plugins::act( 'comment_update_after', $this );
  144. return $result;
  145. }
  146. /**
  147. * function delete
  148. * Deletes this comment
  149. */
  150. public function delete()
  151. {
  152. $allow = true;
  153. $allow = Plugins::filter( 'comment_delete_allow', $allow, $this );
  154. if ( ! $allow ) {
  155. return;
  156. }
  157. Plugins::act( 'comment_delete_before', $this );
  158. // Delete all info records associated with this comment
  159. $this->info->delete_all();
  160. $result = parent::deleteRecord( DB::table( 'comments' ), array( 'id'=>$this->id ) );
  161. Plugins::act( 'comment_delete_after', $this );
  162. return $result;
  163. }
  164. /**
  165. * function __get
  166. * Overrides QueryRecord __get to implement custom object properties
  167. * @param string Name of property to return
  168. * @return mixed The requested field value
  169. */
  170. public function __get( $name )
  171. {
  172. $fieldnames = array_merge( array_keys( $this->fields ), array('post', 'info', 'editlink' ) );
  173. $filter = false;
  174. if ( !in_array( $name, $fieldnames ) && strpos( $name, '_' ) !== false ) {
  175. $field_matches = implode('|', $fieldnames);
  176. if(preg_match( '/^(' . $field_matches . ')_(.+)$/', $name, $matches )) {
  177. list( $junk, $name, $filter ) = $matches;
  178. }
  179. }
  180. if ( $name == 'name' && parent::__get( $name ) == '' ) {
  181. return _t( 'Anonymous' );
  182. }
  183. switch ( $name ) {
  184. case 'post':
  185. $out = $this->get_post();
  186. break;
  187. case 'info':
  188. $out = $this->get_info();
  189. break;
  190. case 'statusname':
  191. $out = self::status_name( $this->status );
  192. break;
  193. case 'typename':
  194. $out = self::type_name( $this->type );
  195. break;
  196. case 'editlink':
  197. $out = $this->get_editlink();
  198. break;
  199. default:
  200. $out = parent::__get( $name );
  201. break;
  202. }
  203. //$out = parent::__get( $name );
  204. $out = Plugins::filter( "comment_{$name}", $out, $this );
  205. if ( $filter ) {
  206. $out = Plugins::filter( "comment_{$name}_{$filter}", $out, $this );
  207. }
  208. return $out;
  209. }
  210. /**
  211. * function __set
  212. * Overrides QueryRecord __set to implement custom object properties
  213. * @param string Name of property to return
  214. * @return mixed The requested field value
  215. */
  216. public function __set( $name, $value )
  217. {
  218. switch ( $name ) {
  219. case 'status':
  220. return $this->setstatus( $value );
  221. case 'date':
  222. if ( !( $value instanceOf HabariDateTime ) ) {
  223. $value = HabariDateTime::date_create( $value );
  224. }
  225. break;
  226. case 'post':
  227. if ( is_int( $value ) ) {
  228. // a post ID was passed
  229. $p = Post::get( array( 'id'=>$value ) );
  230. $this->post_id = $p->id;
  231. $this->post_object = $p;
  232. }
  233. elseif ( is_string( $value ) ) {
  234. // a post Slug was passed
  235. $p = Post::get( array( 'slug'=>$value ) );
  236. $this->post_id = $p->id;
  237. $this->post_object = $p;
  238. }
  239. elseif ( is_object( $value ) ) {
  240. // a Post object was passed, so just use it directly
  241. $this->post_id = $p->id;
  242. $this->post_object = $value;
  243. }
  244. return $value;
  245. }
  246. return parent::__set( $name, $value );
  247. }
  248. /**
  249. * private function get_post()
  250. * returns a Post object for the post of this comment
  251. * @param bool Whether to use the cached version or not. Default to true
  252. * @return Post a Post object for the post of the current comment
  253. */
  254. private function get_post( $use_cache = true )
  255. {
  256. if ( ! isset( $this->post_object ) || ( ! $use_cache) ) {
  257. $this->post_object = Posts::get( array('id' => $this->post_id, 'fetch_fn' => 'get_row') );
  258. }
  259. return $this->post_object;
  260. }
  261. /**
  262. * function get_info
  263. * Gets the info object for this comment, which contains data from the commentinfo table
  264. * related to this comment.
  265. * @return CommentInfo object
  266. */
  267. private function get_info()
  268. {
  269. if ( ! $this->inforecords ) {
  270. if ( 0 == $this->id ) {
  271. $this->inforecords = new CommentInfo();
  272. }
  273. else {
  274. $this->inforecords = new CommentInfo( $this->id );
  275. }
  276. }
  277. return $this->inforecords;
  278. }
  279. /**
  280. * function setstatus
  281. * @param mixed the status to set it to. String or integer.
  282. * @return integer the status of the comment
  283. * Sets the status for a comment, given a string or integer.
  284. */
  285. private function setstatus( $value )
  286. {
  287. if ( is_numeric( $value ) ) {
  288. $this->newfields['status'] = $value;
  289. }
  290. else {
  291. switch ( strtolower( $value ) ) {
  292. case "approved":
  293. case "approve":
  294. case "ham":
  295. $this->newfields['status'] = self::STATUS_APPROVED;
  296. break;
  297. case "unapproved":
  298. case "unapprove":
  299. $this->newfields['status'] = self::STATUS_UNAPPROVED;
  300. break;
  301. case "spam":
  302. $this->newfields['status'] = self::STATUS_SPAM;
  303. break;
  304. case "deleted":
  305. $this->newfields['status'] = self::STATUS_DELETED;
  306. break;
  307. }
  308. }
  309. return $this->newfields['status'];
  310. }
  311. /**
  312. * returns an associative array of comment types
  313. * @param bool whether to force a refresh of the cached values
  314. * @return array An array of comment type names => integer values
  315. */
  316. public static function list_comment_types( $refresh = false )
  317. {
  318. if ( ( ! $refresh ) && ( ! empty( self::$comment_type_list ) ) ) {
  319. return self::$comment_type_list;
  320. }
  321. self::$comment_type_list = array(
  322. self::COMMENT => 'comment',
  323. self::PINGBACK => 'pingback',
  324. self::TRACKBACK => 'trackback',
  325. );
  326. return self::$comment_type_list;
  327. }
  328. /**
  329. * returns an associative array of comment statuses
  330. * @param bool whether to force a refresh of the cached values
  331. * @return array An array of comment statuses names => interger values
  332. */
  333. public static function list_comment_statuses( $refresh = false )
  334. {
  335. if ( ( ! $refresh ) && ( ! empty( self::$comment_status_list ) ) ) {
  336. return self::$comment_status_list;
  337. }
  338. self::$comment_status_list = array(
  339. self::STATUS_UNAPPROVED => 'unapproved',
  340. self::STATUS_APPROVED => 'approved',
  341. self::STATUS_SPAM => 'spam',
  342. // 'STATUS_DELETED' => self::STATUS_DELETED, // Not supported
  343. );
  344. self::$comment_status_list = Plugins::filter( 'list_comment_statuses', self::$comment_status_list );
  345. return self::$comment_status_list;
  346. }
  347. /**
  348. * returns the action name of the comment status
  349. * @param mixed a comment status value, or name
  350. * @return string a string of the status action, or null
  351. */
  352. public static function status_action( $status )
  353. {
  354. if ( empty( self::$comment_status_actions ) ) {
  355. self::$comment_status_actions = array(
  356. self::STATUS_UNAPPROVED => _t( 'Unapprove' ),
  357. self::STATUS_APPROVED => _t( 'Approve' ),
  358. self::STATUS_SPAM => _t( 'Spam' ),
  359. );
  360. self::$comment_status_actions = Plugins::filter( 'list_comment_actions', self::$comment_status_actions );
  361. }
  362. if ( is_numeric( $status ) && isset( self::$comment_status_actions[$status] ) ) {
  363. return self::$comment_status_actions[$status];
  364. }
  365. $statuses = array_flip( Comment::list_comment_statuses() );
  366. if ( isset( $statuses[$status] ) ) {
  367. return self::$comment_status_actions[$statuses[$status]];
  368. }
  369. return '';
  370. }
  371. /**
  372. * returns the integer value of the specified comment status, or false
  373. * @param mixed a comment status name or value
  374. * @return mixed an integer or boolean false
  375. */
  376. public static function status( $name )
  377. {
  378. $statuses = Comment::list_comment_statuses();
  379. if ( is_numeric( $name ) && ( isset( $statuses[$name] ) ) ) {
  380. return $name;
  381. }
  382. $statuses = array_flip( $statuses );
  383. if ( isset( $statuses[$name] ) ) {
  384. return $statuses[$name];
  385. }
  386. return false;
  387. }
  388. /**
  389. * returns the friendly name of a comment status, or null
  390. * @param mixed a comment status value, or name
  391. * @return mixed a string of the status name, or null
  392. */
  393. public static function status_name( $status )
  394. {
  395. $statuses = Comment::list_comment_statuses();
  396. if ( is_numeric( $status ) && isset( $statuses[$status] ) ) {
  397. return $statuses[$status];
  398. }
  399. $statuses = array_flip( $statuses );
  400. if ( isset( $statuses[$status] ) ) {
  401. return $status;
  402. }
  403. return '';
  404. }
  405. /**
  406. * returns the integer value of the specified comment type, or false
  407. * @param mixed a comment type name or number
  408. * @return mixed an integer or boolean false
  409. */
  410. public static function type( $name )
  411. {
  412. $types = Comment::list_comment_types();
  413. if ( is_numeric( $name ) && ( isset( $types[$name] ) ) ) {
  414. return $name;
  415. }
  416. $types = array_flip( $types );
  417. if ( isset( $types[$name] ) ) {
  418. return $types[$name];
  419. }
  420. return false;
  421. }
  422. /**
  423. * returns the friendly name of a comment type, or null
  424. * @param mixed a comment type number, or name
  425. * @return mixed a string of the comment type, or null
  426. */
  427. public static function type_name( $type )
  428. {
  429. $types = Comment::list_comment_types();
  430. if ( is_numeric( $type ) && isset( $types[$type] ) ) {
  431. return $types[$type];
  432. }
  433. $types = array_flip( $types );
  434. if ( isset( $types[$type] ) ) {
  435. return $type;
  436. }
  437. return '';
  438. }
  439. /**
  440. * Return the content type of this object
  441. *
  442. * @return string The content type of this object
  443. * @see IsContent
  444. */
  445. public function content_type()
  446. {
  447. return Comment::type_name( $this->type );
  448. }
  449. /**
  450. * Returns an access Bitmask for the given user on this comment. Read access is determined
  451. * by the associated post. Update/delete is determined by the comment management tokens.
  452. * @param User $user The user mask to fetch
  453. * @return Bitmask
  454. */
  455. public function get_access( $user = null )
  456. {
  457. if ( ! $user instanceof User ) {
  458. $user = User::identify();
  459. }
  460. // these tokens automatically grant full access to the comment
  461. if ( $user->can( 'super_user' ) || $user->can( 'manage_all_comments' ) ||
  462. ( $user->id == $this->post->user_id && $user->can( 'manage_own_post_comments' ) ) ) {
  463. return ACL::get_bitmask( 'full' );
  464. }
  465. /* If we got this far, we can't update or delete a comment. We still need to check if we have
  466. * read access to it. Collect a list of applicable tokens
  467. */
  468. $tokens = array(
  469. 'post_any',
  470. 'post_' . Post::type_name( $this->post->content_type ),
  471. );
  472. if ( $user->id == $this->post->user_id ) {
  473. $tokens[] = 'own_posts';
  474. }
  475. $tokens = array_merge( $tokens, $this->post->get_tokens() );
  476. // grab the access masks on these tokens
  477. foreach ( $tokens as $token ) {
  478. $access = ACL::get_user_token_access( $user, $token );
  479. if ( $access instanceof Bitmask ) {
  480. $token_accesses[] = ACL::get_user_token_access( $user, $token )->value;
  481. }
  482. }
  483. // now that we have all the accesses, loop through them to build the access to the particular post
  484. if ( in_array( 0, $token_accesses ) ) {
  485. return ACL::get_bitmask( 0 );
  486. }
  487. if ( ACL::get_bitmask( Utils::array_or( $token_accesses ) )->read ) {
  488. return ACL::get_bitmask( 'read' );
  489. }
  490. // if we haven't returned by this point, we can neither manage the comment nor read it
  491. return ACL::get_bitmask( 0 );
  492. }
  493. /**
  494. * Returns a URL for the ->editlink property of this class.
  495. * @return string A url to edit this comment in the admin.
  496. */
  497. private function get_editlink()
  498. {
  499. return URL::get( 'admin', "page=comment&id={$this->id}" );
  500. }
  501. /**
  502. * Returns a list of CSS classes for the comment
  503. *
  504. * @param string|array $append Additional classes that should be added to the ones generated
  505. * @return string The resultant classes
  506. */
  507. public function css_class ( $append = array() ) {
  508. $classes = $append;
  509. $classes[] = 'comment';
  510. $classes[] = 'comment-' . $this->id;
  511. $classes[] = 'type-' . $this->typename;
  512. $classes[] = 'status-' . $this->statusname;
  513. $classes[] = 'comment-post-' . $this->post->id;
  514. return implode( ' ', $classes );
  515. }
  516. }
  517. ?>