. * */ namespace OCA\News\Db; use \OCA\AppFramework\Db\DoesNotExistException; use \OCA\AppFramework\Db\MultipleObjectsReturnedException; use \OCA\AppFramework\Db\Mapper; use \OCA\AppFramework\Core\API; class ItemMapper extends Mapper implements IMapper { public function __construct(API $api){ parent::__construct($api, 'news_items'); } protected function findAllRows($sql, $params, $limit=null, $offset=null) { $result = $this->execute($sql, $params, $limit, $offset); $items = array(); while($row = $result->fetchRow()){ $item = new Item(); $item->fromRow($row); array_push($items, $item); } return $items; } private function makeSelectQuery($prependTo){ return 'SELECT `items`.* FROM `*PREFIX*news_items` `items` '. 'JOIN `*PREFIX*news_feeds` `feeds` ' . 'ON `feeds`.`id` = `items`.`feed_id` '. 'AND `feeds`.`user_id` = ? ' . $prependTo; } private function makeSelectQueryStatus($prependTo, $status) { // Hi this is Ray and you're watching Jack Ass // Now look closely: this is how we adults handle weird bugs in our // code: we take them variables and we cast the shit out of them $status = (int) $status; // prepare for the unexpected if(!is_numeric($status)) { die(); die(); die('If you can read this something is terribly wrong'); } // now im gonna slowly stick them in the query, be careful! return $this->makeSelectQuery( // WARNING: this is a desperate attempt at making this query work // because prepared statements dont work. This is a possible // SQL INJECTION RISK WHEN MODIFIED WITHOUT THOUGHT. // think twice when changing this 'AND ((`items`.`status` & ' . $status . ') = ' . $status . ') ' . $prependTo ); } public function find($id, $userId){ $sql = $this->makeSelectQuery('AND `items`.`id` = ? '); $row = $this->findOneQuery($sql, array($userId, $id)); $item = new Item(); $item->fromRow($row); return $item; } public function starredCount($userId){ $sql = 'SELECT COUNT(*) AS size FROM `*PREFIX*news_feeds` `feeds` ' . 'JOIN `*PREFIX*news_items` `items` ' . 'ON `items`.`feed_id` = `feeds`.`id` ' . 'AND `feeds`.`user_id` = ? ' . // WARNING: this is a desperate attempt at making this query work // because prepared statements dont work. This is a possible // SQL INJECTION RISK WHEN MODIFIED WITHOUT THOUGHT. // think twice when changing this 'WHERE ((`items`.`status` & ' . StatusFlag::STARRED . ') = ' . StatusFlag::STARRED . ')'; $params = array($userId); $result = $this->execute($sql, $params)->fetchRow(); return (int) $result['size']; } public function readFeed($feedId, $highestItemId, $userId){ // its 0 when the feed was not loaded or the loaded feed // does not contain any items if($highestItemId !== 0){ $params = array(~StatusFlag::UNREAD, $feedId, $highestItemId, $userId, $feedId); $lowerSql = 'AND `id` <= ? '; } else { $lowerSql = ''; $params = array(~StatusFlag::UNREAD, $feedId, $userId, $feedId); } $sql = 'UPDATE `*PREFIX*news_items` ' . 'SET `status` = `status` & ? ' . 'WHERE `feed_id` = ? ' . $lowerSql . 'AND EXISTS (' . 'SELECT * FROM `*PREFIX*news_feeds` ' . 'WHERE `user_id` = ? ' . 'AND `id` = ? ) '; $this->execute($sql, $params); } public function findAllNew($updatedSince, $status, $userId){ $sql = $this->makeSelectQueryStatus('AND `items`.`last_modified` >= ?', $status); $params = array($userId, $updatedSince); return $this->findAllRows($sql, $params); } public function findAllNewFolder($id, $updatedSince, $status, $userId){ $sql = 'AND `feeds`.`folder_id` = ? ' . 'AND `items`.`last_modified` >= ?'; $sql = $this->makeSelectQueryStatus($sql, $status); $params = array($userId, $id, $updatedSince); return $this->findAllRows($sql, $params); } public function findAllNewFeed($id, $updatedSince, $status, $userId){ $sql = 'AND `items`.`feed_id` = ? ' . 'AND `items`.`last_modified` >= ?'; $sql = $this->makeSelectQueryStatus($sql, $status); $params = array($userId, $id, $updatedSince); return $this->findAllRows($sql, $params); } public function findAllFeed($id, $limit, $offset, $status, $userId){ $params = array($userId, $id); $sql = 'AND `items`.`feed_id` = ? '; if($offset !== 0){ $sql .= 'AND `items`.`id` < ? '; array_push($params, $offset); } $sql .= 'ORDER BY `items`.`id` DESC '; $sql = $this->makeSelectQueryStatus($sql, $status); return $this->findAllRows($sql, $params, $limit); } public function findAllFolder($id, $limit, $offset, $status, $userId){ $params = array($userId, $id); $sql = 'AND `feeds`.`folder_id` = ? '; if($offset !== 0){ $sql .= 'AND `items`.`id` < ? '; array_push($params, $offset); } $sql .= 'ORDER BY `items`.`id` DESC '; $sql = $this->makeSelectQueryStatus($sql, $status); return $this->findAllRows($sql, $params, $limit); } public function findAll($limit, $offset, $status, $userId){ $params = array($userId); $sql = ''; if($offset !== 0){ $sql .= 'AND `items`.`id` < ? '; array_push($params, $offset); } $sql .= 'ORDER BY `items`.`id` DESC '; $sql = $this->makeSelectQueryStatus($sql, $status); return $this->findAllRows($sql, $params, $limit); } public function findByGuidHash($guidHash, $feedId, $userId){ $sql = $this->makeSelectQuery( 'AND `items`.`guid_hash` = ? ' . 'AND `feeds`.`id` = ? '); $row = $this->findOneQuery($sql, array($userId, $guidHash, $feedId)); $item = new Item(); $item->fromRow($row); return $item; } /** * Delete all items for feeds that have over $threshold unread and not * starred items */ public function deleteReadOlderThanThreshold($threshold){ $status = StatusFlag::STARRED | StatusFlag::UNREAD; $sql = 'SELECT COUNT(*) `size`, `feed_id` ' . 'FROM `*PREFIX*news_items` ' . 'WHERE NOT ((`status` & ?) > 0) ' . 'GROUP BY `feed_id` ' . 'HAVING COUNT(*) > ?'; $params = array($status, $threshold); $result = $this->execute($sql, $params); while($row = $result->fetchRow()) { $limit = $threshold - $row['size']; if($limit > 0) { $params = array($status, $row['feed_id']); $sql = 'DELETE FROM `*PREFIX*news_items` `items` ' . 'WHERE NOT ((`status` & ?) > 0) ' . 'AND `feed_id` = ? ' . 'ORDER BY `items`.`id` ASC'; $this->execute($sql, $params, $limit); } } } public function getNewestItemId($userId) { $sql = 'SELECT MAX(`items`.`id`) AS `max_id` FROM `*PREFIX*news_items` `items` '. 'JOIN `*PREFIX*news_feeds` `feeds` ' . 'ON `feeds`.`id` = `items`.`feed_id` '. 'AND `feeds`.`user_id` = ?'; $params = array($userId); $result = $this->findOneQuery($sql, $params); return (int) $result['max_id']; } }