summaryrefslogtreecommitdiffstats
path: root/lib/Db
diff options
context:
space:
mode:
authorBernhard Posselt <dev@bernhard-posselt.com>2016-07-23 21:24:54 +0200
committerBernhard Posselt <dev@bernhard-posselt.com>2016-07-23 21:24:54 +0200
commit004fcbbcc7609ca83807f2e38967ef54f469bf72 (patch)
tree49eb99b4ea92b2045793fc567f719b31ec7f9042 /lib/Db
parent60abc0ed4438c9b6fda245b0dc33cb483bc2aeaf (diff)
Move to new directory structure
Diffstat (limited to 'lib/Db')
-rw-r--r--lib/Db/EntityJSONSerializer.php28
-rw-r--r--lib/Db/Feed.php189
-rw-r--r--lib/Db/FeedMapper.php166
-rw-r--r--lib/Db/FeedType.php24
-rw-r--r--lib/Db/Folder.php73
-rw-r--r--lib/Db/FolderMapper.php110
-rw-r--r--lib/Db/IAPI.php18
-rw-r--r--lib/Db/Item.php256
-rw-r--r--lib/Db/ItemMapper.php403
-rw-r--r--lib/Db/MapperFactory.php47
-rw-r--r--lib/Db/Mysql/ItemMapper.php88
-rw-r--r--lib/Db/NewsMapper.php93
-rw-r--r--lib/Db/StatusFlag.php47
13 files changed, 1542 insertions, 0 deletions
diff --git a/lib/Db/EntityJSONSerializer.php b/lib/Db/EntityJSONSerializer.php
new file mode 100644
index 000000000..c0d946452
--- /dev/null
+++ b/lib/Db/EntityJSONSerializer.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+trait EntityJSONSerializer {
+
+
+ public function serializeFields($properties) {
+ $result = [];
+ foreach($properties as $property) {
+ $result[$property] = $this->$property;
+ }
+ return $result;
+ }
+
+
+} \ No newline at end of file
diff --git a/lib/Db/Feed.php b/lib/Db/Feed.php
new file mode 100644
index 000000000..62d49c01b
--- /dev/null
+++ b/lib/Db/Feed.php
@@ -0,0 +1,189 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+use \OCP\AppFramework\Db\Entity;
+
+/**
+ * @method integer getId()
+ * @method void setId(integer $value)
+ * @method string getUserId()
+ * @method void setUserId(string $value)
+ * @method int getOrdering()
+ * @method void setOrdering(integer $value)
+ * @method string getUrlHash()
+ * @method void setUrlHash(string $value)
+ * @method string getLocation()
+ * @method void setLocation(string $value)
+ * @method string getUrl()
+ * @method string getTitle()
+ * @method void setTitle(string $value)
+ * @method string getLastModified()
+ * @method void setLastModified(integer $value)
+ * @method string getHttpLastModified()
+ * @method void setHttpLastModified(string $value)
+ * @method string getHttpEtag()
+ * @method void setHttpEtag(string $value)
+ * @method string getFaviconLink()
+ * @method void setFaviconLink(string $value)
+ * @method integer getAdded()
+ * @method void setAdded(integer $value)
+ * @method boolean getPinned()
+ * @method void setPinned(boolean $value)
+ * @method integer getFolderId()
+ * @method void setFolderId(integer $value)
+ * @method integer getFullTextEnabled()
+ * @method void setFullTextEnabled(bool $value)
+ * @method integer getUnreadCount()
+ * @method void setUnreadCount(integer $value)
+ * @method string getLink()
+ * @method boolean getPreventUpdate()
+ * @method void setPreventUpdate(boolean $value)
+ * @method integer getDeletedAt()
+ * @method void setDeletedAt(integer $value)
+ * @method integer getArticlesPerUpdate()
+ * @method void setArticlesPerUpdate(integer $value)
+ * @method integer getUpdateErrorCount()
+ * @method void setUpdateErrorCount(integer $value)
+ * @method string getLastUpdateError()
+ * @method void setLastUpdateError(string $value)
+ * @method string getBasicAuthUser()
+ * @method void setBasicAuthUser(string $value)
+ * @method string getBasicAuthPassword()
+ * @method void setBasicAuthPassword(string $value)
+ */
+class Feed extends Entity implements IAPI, \JsonSerializable {
+
+ use EntityJSONSerializer;
+
+ protected $userId;
+ protected $urlHash;
+ protected $url;
+ protected $title;
+ protected $faviconLink;
+ protected $added;
+ protected $folderId;
+ protected $unreadCount;
+ protected $link;
+ protected $preventUpdate;
+ protected $deletedAt;
+ protected $articlesPerUpdate;
+ protected $httpLastModified;
+ protected $lastModified;
+ protected $httpEtag;
+ protected $location;
+ protected $ordering;
+ protected $fullTextEnabled;
+ protected $pinned;
+ protected $updateMode;
+ protected $updateErrorCount;
+ protected $lastUpdateError;
+ protected $basicAuthUser;
+ protected $basicAuthPassword;
+
+ public function __construct(){
+ $this->addType('parentId', 'integer');
+ $this->addType('added', 'integer');
+ $this->addType('folderId', 'integer');
+ $this->addType('unreadCount', 'integer');
+ $this->addType('preventUpdate', 'boolean');
+ $this->addType('pinned', 'boolean');
+ $this->addType('deletedAt', 'integer');
+ $this->addType('articlesPerUpdate', 'integer');
+ $this->addType('ordering', 'integer');
+ $this->addType('fullTextEnabled', 'boolean');
+ $this->addType('updateMode', 'integer');
+ $this->addType('updateErrorCount', 'integer');
+ $this->addType('lastModified', 'integer');
+ }
+
+
+ /**
+ * Turns entitie attributes into an array
+ */
+ public function jsonSerialize() {
+ $serialized = $this->serializeFields([
+ 'id',
+ 'userId',
+ 'urlHash',
+ 'url',
+ 'title',
+ 'faviconLink',
+ 'added',
+ 'folderId',
+ 'unreadCount',
+ 'link',
+ 'preventUpdate',
+ 'deletedAt',
+ 'articlesPerUpdate',
+ 'location',
+ 'ordering',
+ 'fullTextEnabled',
+ 'pinned',
+ 'updateMode',
+ 'updateErrorCount',
+ 'lastUpdateError',
+ 'basicAuthUser',
+ 'basicAuthPassword'
+ ]);
+
+ $url = parse_url($this->link)['host'];
+
+ // strip leading www. to avoid css class confusion
+ if (strpos($url, 'www.') === 0) {
+ $url = substr($url, 4);
+ }
+
+ $serialized['cssClass'] = 'custom-' . str_replace('.', '-', $url);
+
+ return $serialized;
+ }
+
+
+ public function toAPI() {
+ return $this->serializeFields([
+ 'id',
+ 'url',
+ 'title',
+ 'faviconLink',
+ 'added',
+ 'folderId',
+ 'unreadCount',
+ 'ordering',
+ 'link',
+ 'pinned',
+ 'updateErrorCount',
+ 'lastUpdateError'
+ ]);
+ }
+
+
+ public function setUrl($url) {
+ $url = trim($url);
+ if(strpos($url, 'http') === 0) {
+ parent::setUrl($url);
+ $this->setUrlHash(md5($url));
+ }
+ }
+
+
+ public function setLink($url) {
+ $url = trim($url);
+ if(strpos($url, 'http') === 0) {
+ parent::setLink($url);
+ }
+ }
+
+
+}
diff --git a/lib/Db/FeedMapper.php b/lib/Db/FeedMapper.php
new file mode 100644
index 000000000..80d75e723
--- /dev/null
+++ b/lib/Db/FeedMapper.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+use OCA\News\Utility\Time;
+use OCP\IDBConnection;
+use OCP\AppFramework\Db\Entity;
+
+
+class FeedMapper extends NewsMapper {
+
+
+ public function __construct(IDBConnection $db, Time $time) {
+ parent::__construct($db, 'news_feeds', Feed::class, $time);
+ }
+
+
+ public function find($id, $userId){
+ $sql = 'SELECT `feeds`.*, COUNT(`items`.`id`) AS `unread_count` ' .
+ 'FROM `*PREFIX*news_feeds` `feeds` ' .
+ 'LEFT JOIN `*PREFIX*news_items` `items` ' .
+ 'ON `feeds`.`id` = `items`.`feed_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
+ 'AND (`items`.`status` & ' . StatusFlag::UNREAD . ') = ' .
+ StatusFlag::UNREAD . ' ' .
+ 'WHERE `feeds`.`id` = ? ' .
+ 'AND `feeds`.`user_id` = ? ' .
+ 'GROUP BY `feeds`.`id`';
+ $params = [$id, $userId];
+
+ return $this->findEntity($sql, $params);
+ }
+
+
+ public function findAllFromUser($userId){
+ $sql = 'SELECT `feeds`.*, COUNT(`items`.`id`) AS `unread_count` ' .
+ 'FROM `*PREFIX*news_feeds` `feeds` ' .
+ 'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` '.
+ 'ON `feeds`.`folder_id` = `folders`.`id` ' .
+ 'LEFT JOIN `*PREFIX*news_items` `items` ' .
+ 'ON `feeds`.`id` = `items`.`feed_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
+ 'AND (`items`.`status` & ' . StatusFlag::UNREAD . ') = ' .
+ StatusFlag::UNREAD . ' ' .
+ 'WHERE `feeds`.`user_id` = ? ' .
+ 'AND (`feeds`.`folder_id` = 0 ' .
+ 'OR `folders`.`deleted_at` = 0' .
+ ')' .
+ 'AND `feeds`.`deleted_at` = 0 ' .
+ 'GROUP BY `feeds`.`id`';
+ $params = [$userId];
+
+ return $this->findEntities($sql, $params);
+ }
+
+
+ public function findAll(){
+ $sql = 'SELECT `feeds`.*, COUNT(`items`.`id`) AS `unread_count` ' .
+ 'FROM `*PREFIX*news_feeds` `feeds` ' .
+ 'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` '.
+ 'ON `feeds`.`folder_id` = `folders`.`id` ' .
+ 'LEFT JOIN `*PREFIX*news_items` `items` ' .
+ 'ON `feeds`.`id` = `items`.`feed_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
+ 'AND (`items`.`status` & ' . StatusFlag::UNREAD . ') = ' .
+ StatusFlag::UNREAD . ' ' .
+ 'WHERE (`feeds`.`folder_id` = 0 ' .
+ 'OR `folders`.`deleted_at` = 0' .
+ ')' .
+ 'AND `feeds`.`deleted_at` = 0 ' .
+ 'GROUP BY `feeds`.`id`';
+
+ return $this->findEntities($sql);
+ }
+
+
+ public function findByUrlHash($hash, $userId){
+ $sql = 'SELECT `feeds`.*, COUNT(`items`.`id`) AS `unread_count` ' .
+ 'FROM `*PREFIX*news_feeds` `feeds` ' .
+ 'LEFT JOIN `*PREFIX*news_items` `items` ' .
+ 'ON `feeds`.`id` = `items`.`feed_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
+ 'AND (`items`.`status` & ' . StatusFlag::UNREAD . ') = ' .
+ StatusFlag::UNREAD . ' ' .
+ 'WHERE `feeds`.`url_hash` = ? ' .
+ 'AND `feeds`.`user_id` = ? ' .
+ 'GROUP BY `feeds`.`id`';
+ $params = [$hash, $userId];
+
+ return $this->findEntity($sql, $params);
+ }
+
+
+ public function delete(Entity $entity){
+ parent::delete($entity);
+
+ // someone please slap me for doing this manually :P
+ // we needz CASCADE + FKs please
+ $sql = 'DELETE FROM `*PREFIX*news_items` WHERE `feed_id` = ?';
+ $params = [$entity->getId()];
+ $this->execute($sql, $params);
+ }
+
+
+ /**
+ * @param int $deleteOlderThan if given gets all entries with a delete date
+ * older than that timestamp
+ * @param string $userId if given returns only entries from the given user
+ * @return array with the database rows
+ */
+ public function getToDelete($deleteOlderThan=null, $userId=null) {
+ $sql = 'SELECT * FROM `*PREFIX*news_feeds` ' .
+ 'WHERE `deleted_at` > 0 ';
+ $params = [];
+
+ // sometimes we want to delete all entries
+ if ($deleteOlderThan !== null) {
+ $sql .= 'AND `deleted_at` < ? ';
+ $params[] = $deleteOlderThan;
+ }
+
+ // we need to sometimes only delete feeds of a user
+ if($userId !== null) {
+ $sql .= 'AND `user_id` = ?';
+ $params[] = $userId;
+ }
+
+ return $this->findEntities($sql, $params);
+ }
+
+
+ /**
+ * Deletes all feeds of a user, delete items first since the user_id
+ * is not defined in there
+ * @param string $userId the name of the user
+ */
+ public function deleteUser($userId) {
+ $sql = 'DELETE FROM `*PREFIX*news_feeds` WHERE `user_id` = ?';
+ $this->execute($sql, [$userId]);
+ }
+
+
+}
diff --git a/lib/Db/FeedType.php b/lib/Db/FeedType.php
new file mode 100644
index 000000000..fcb42bb8a
--- /dev/null
+++ b/lib/Db/FeedType.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+
+class FeedType {
+ const FEED = 0;
+ const FOLDER = 1;
+ const STARRED = 2;
+ const SUBSCRIPTIONS = 3;
+ const SHARED = 4;
+ const EXPLORE = 5;
+} \ No newline at end of file
diff --git a/lib/Db/Folder.php b/lib/Db/Folder.php
new file mode 100644
index 000000000..8d1432a73
--- /dev/null
+++ b/lib/Db/Folder.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+use \OCP\AppFramework\Db\Entity;
+
+/**
+ * @method integer getId()
+ * @method void setId(integer $value)
+ * @method string getUserId()
+ * @method void setUserId(string $value)
+ * @method string getName()
+ * @method void setName(string $value)
+ * @method integer getParentId()
+ * @method void setParentId(integer $value)
+ * @method boolean getOpened()
+ * @method void setOpened(boolean $value)
+ * @method integer getDeletedAt()
+ * @method void setDeletedAt(integer $value)
+ * @method integer getLastModified()
+ * @method void setLastModified(integer $value)
+
+ */
+class Folder extends Entity implements IAPI, \JsonSerializable {
+
+ use EntityJSONSerializer;
+
+ protected $parentId;
+ protected $name;
+ protected $userId;
+ protected $opened;
+ protected $deletedAt;
+ protected $lastModified;
+
+ public function __construct(){
+ $this->addType('parentId', 'integer');
+ $this->addType('opened', 'boolean');
+ $this->addType('deletedAt', 'integer');
+ $this->addType('lastModified', 'integer');
+ }
+
+ /**
+ * Turns entitie attributes into an array
+ */
+ public function jsonSerialize() {
+ return $this->serializeFields([
+ 'id',
+ 'parentId',
+ 'name',
+ 'userId',
+ 'opened',
+ 'deletedAt',
+ ]);
+ }
+
+ public function toAPI() {
+ return $this->serializeFields([
+ 'id',
+ 'name'
+ ]);
+ }
+} \ No newline at end of file
diff --git a/lib/Db/FolderMapper.php b/lib/Db/FolderMapper.php
new file mode 100644
index 000000000..30acb455c
--- /dev/null
+++ b/lib/Db/FolderMapper.php
@@ -0,0 +1,110 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+use OCA\News\Utility\Time;
+use OCP\IDBConnection;
+use OCP\AppFramework\Db\Entity;
+
+class FolderMapper extends NewsMapper {
+
+ public function __construct(IDBConnection $db, Time $time) {
+ parent::__construct($db, 'news_folders', Folder::class, $time);
+ }
+
+ public function find($id, $userId){
+ $sql = 'SELECT * FROM `*PREFIX*news_folders` ' .
+ 'WHERE `id` = ? ' .
+ 'AND `user_id` = ?';
+
+ return $this->findEntity($sql, [$id, $userId]);
+ }
+
+
+ public function findAllFromUser($userId){
+ $sql = 'SELECT * FROM `*PREFIX*news_folders` ' .
+ 'WHERE `user_id` = ? ' .
+ 'AND `deleted_at` = 0';
+ $params = [$userId];
+
+ return $this->findEntities($sql, $params);
+ }
+
+
+ public function findByName($folderName, $userId){
+ $sql = 'SELECT * FROM `*PREFIX*news_folders` ' .
+ 'WHERE `name` = ? ' .
+ 'AND `user_id` = ?';
+ $params = [$folderName, $userId];
+
+ return $this->findEntities($sql, $params);
+ }
+
+
+ public function delete(Entity $entity){
+ parent::delete($entity);
+
+ // someone please slap me for doing this manually :P
+ // we needz CASCADE + FKs please
+ $sql = 'DELETE FROM `*PREFIX*news_feeds` WHERE `folder_id` = ?';
+ $params = [$entity->getId()];
+ $stmt = $this->execute($sql, $params);
+ $stmt->closeCursor();
+
+ $sql = 'DELETE FROM `*PREFIX*news_items` WHERE `feed_id` NOT IN '.
+ '(SELECT `feeds`.`id` FROM `*PREFIX*news_feeds` `feeds`)';
+
+ $stmt = $this->execute($sql);
+ $stmt->closeCursor();
+ }
+
+
+ /**
+ * @param int $deleteOlderThan if given gets all entries with a delete date
+ * older than that timestamp
+ * @param string $userId if given returns only entries from the given user
+ * @return array with the database rows
+ */
+ public function getToDelete($deleteOlderThan=null, $userId=null) {
+ $sql = 'SELECT * FROM `*PREFIX*news_folders` ' .
+ 'WHERE `deleted_at` > 0 ';
+ $params = [];
+
+ // sometimes we want to delete all entries
+ if ($deleteOlderThan !== null) {
+ $sql .= 'AND `deleted_at` < ? ';
+ $params[] = $deleteOlderThan;
+ }
+
+ // we need to sometimes only delete feeds of a user
+ if($userId !== null) {
+ $sql .= 'AND `user_id` = ?';
+ $params[] = $userId;
+ }
+
+ return $this->findEntities($sql, $params);
+ }
+
+
+ /**
+ * Deletes all folders of a user
+ * @param string $userId the name of the user
+ */
+ public function deleteUser($userId) {
+ $sql = 'DELETE FROM `*PREFIX*news_folders` WHERE `user_id` = ?';
+ $this->execute($sql, [$userId]);
+ }
+
+
+}
diff --git a/lib/Db/IAPI.php b/lib/Db/IAPI.php
new file mode 100644
index 000000000..ff9791753
--- /dev/null
+++ b/lib/Db/IAPI.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+interface IAPI {
+ public function toAPI();
+}
diff --git a/lib/Db/Item.php b/lib/Db/Item.php
new file mode 100644
index 000000000..e0d8b069b
--- /dev/null
+++ b/lib/Db/Item.php
@@ -0,0 +1,256 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+use \OCP\AppFramework\Db\Entity;
+
+/**
+ * @method integer getId()
+ * @method void setId(integer $value)
+ * @method string getGuid()
+ * @method void setGuid(string $value)
+ * @method string getGuidHash()
+ * @method void setGuidHash(string $value)
+ * @method string getUrl()
+ * @method string getTitle()
+ * @method string getAuthor()
+ * @method string getRtl()
+ * @method string getFingerprint()
+ * @method string getContentHash()
+ * @method integer getPubDate()
+ * @method void setPubDate(integer $value)
+ * @method string getBody()
+ * @method string getEnclosureMime()
+ * @method void setEnclosureMime(string $value)
+ * @method string getEnclosureLink()
+ * @method void setEnclosureLink(string $value)
+ * @method integer getFeedId()
+ * @method void setFeedId(integer $value)
+ * @method integer getStatus()
+ * @method void setStatus(integer $value)
+ * @method void setRtl(boolean $value)
+ * @method integer getLastModified()
+ * @method void setLastModified(integer $value)
+ * @method void setFingerprint(string $value)
+ * @method void setContentHash(string $value)
+ * @method void setSearchIndex(string $value)
+ */
+class Item extends Entity implements IAPI, \JsonSerializable {
+
+ use EntityJSONSerializer;
+
+ protected $contentHash;
+ protected $guidHash;
+ protected $guid;
+ protected $url;
+ protected $title;
+ protected $author;
+ protected $pubDate;
+ protected $body;
+ protected $enclosureMime;
+ protected $enclosureLink;
+ protected $feedId;
+ protected $status = 0;
+ protected $lastModified;
+ protected $searchIndex;
+ protected $rtl;
+ protected $fingerprint;
+
+ public function __construct() {
+ $this->addType('pubDate', 'integer');
+ $this->addType('feedId', 'integer');
+ $this->addType('status', 'integer');
+ $this->addType('lastModified', 'integer');
+ $this->addType('rtl', 'boolean');
+ }
+
+ public function setRead() {
+ $this->markFieldUpdated('status');
+ $this->status &= ~StatusFlag::UNREAD;
+ }
+
+ public function isRead() {
+ return !(($this->status & StatusFlag::UNREAD) === StatusFlag::UNREAD);
+ }
+
+ public function setUnread() {
+ $this->markFieldUpdated('status');
+ $this->status |= StatusFlag::UNREAD;
+ }
+
+ public function isUnread() {
+ return !$this->isRead();
+ }
+
+ public function setStarred() {
+ $this->markFieldUpdated('status');
+ $this->status |= StatusFlag::STARRED;
+ }
+
+ public function isStarred() {
+ return ($this->status & StatusFlag::STARRED) === StatusFlag::STARRED;
+ }
+
+ public function setUnstarred() {
+ $this->markFieldUpdated('status');
+ $this->status &= ~StatusFlag::STARRED;
+ }
+
+ public function isUnstarred() {
+ return !$this->isStarred();
+ }
+
+ /**
+ * Turns entitie attributes into an array
+ */
+ public function jsonSerialize() {
+ return [
+ 'id' => $this->getId(),
+ 'guid' => $this->getGuid(),
+ 'guidHash' => $this->getGuidHash(),
+ 'url' => $this->getUrl(),
+ 'title' => $this->getTitle(),
+ 'author' => $this->getAuthor(),
+ 'pubDate' => $this->getPubDate(),
+ 'body' => $this->getBody(),
+ 'enclosureMime' => $this->getEnclosureMime(),
+ 'enclosureLink' => $this->getEnclosureLink(),
+ 'feedId' => $this->getFeedId(),
+ 'unread' => $this->isUnread(),
+ 'starred' => $this->isStarred(),
+ 'lastModified' => $this->getLastModified(),
+ 'rtl' => $this->getRtl(),
+ 'intro' => $this->getIntro(),
+ 'fingerprint' => $this->getFingerprint(),
+ ];
+ }
+
+ public function toAPI() {
+ return [
+ 'id' => $this->getId(),
+ 'guid' => $this->getGuid(),
+ 'guidHash' => $this->getGuidHash(),
+ 'url' => $this->getUrl(),
+ 'title' => $this->getTitle(),
+ 'author' => $this->getAuthor(),
+ 'pubDate' => $this->getPubDate(),
+ 'body' => $this->getBody(),
+ 'enclosureMime' => $this->getEnclosureMime(),
+ 'enclosureLink' => $this->getEnclosureLink(),
+ 'feedId' => $this->getFeedId(),
+ 'unread' => $this->isUnread(),
+ 'starred' => $this->isStarred(),
+ 'lastModified' => $this->getLastModified(),
+ 'rtl' => $this->getRtl(),
+ 'fingerprint' => $this->getFingerprint(),
+ 'contentHash' => $this->getContentHash()
+ ];
+ }
+
+ public function toExport($feeds) {
+ return [
+ 'guid' => $this->getGuid(),
+ 'url' => $this->getUrl(),
+ 'title' => $this->getTitle(),
+ 'author' => $this->getAuthor(),
+ 'pubDate' => $this->getPubDate(),
+ 'body' => $this->getBody(),
+ 'enclosureMime' => $this->getEnclosureMime(),
+ 'enclosureLink' => $this->getEnclosureLink(),
+ 'unread' => $this->isUnread(),
+ 'starred' => $this->isStarred(),
+ 'feedLink' => $feeds['feed' . $this->getFeedId()]->getLink(),
+ 'rtl' => $this->getRtl(),
+ ];
+ }
+
+ public function getIntro() {
+ return strip_tags($this->getBody());
+ }
+
+ public static function fromImport($import) {
+ $item = new static();
+ $item->setGuid($import['guid']);
+ $item->setGuidHash($import['guid']);
+ $item->setUrl($import['url']);
+ $item->setTitle($import['title']);
+ $item->setAuthor($import['author']);
+ $item->setPubDate($import['pubDate']);
+ $item->setBody($import['body']);
+ $item->setEnclosureMime($import['enclosureMime']);
+ $item->setEnclosureLink($import['enclosureLink']);
+ $item->setRtl($import['rtl']);
+ if ($import['unread']) {
+ $item->setUnread();
+ } else {
+ $item->setRead();
+ }
+ if ($import['starred']) {
+ $item->setStarred();
+ } else {
+ $item->setUnstarred();
+ }
+
+ return $item;
+ }
+
+ public function setAuthor($name) {
+ parent::setAuthor(strip_tags($name));
+ }
+
+ public function setTitle($title) {
+ parent::setTitle(strip_tags($title));
+ }
+
+ public function generateSearchIndex() {
+ $this->setSearchIndex(
+ mb_strtolower(
+ html_entity_decode(strip_tags($this->getBody())) .
+ html_entity_decode($this->getAuthor()) .
+ html_entity_decode($this->getTitle()) .
+ $this->getUrl(),
+ 'UTF-8'
+ )
+ );
+ $this->setFingerprint($this->computeFingerprint());
+ $this->setContentHash($this->computeContentHash());
+ }
+
+ private function computeContentHash() {
+ return md5($this->getTitle() . $this->getUrl() . $this->getBody() .
+ $this->getEnclosureLink() . $this->getEnclosureMime() .
+ $this->getAuthor());
+ }
+
+ private function computeFingerprint() {
+ return md5($this->getTitle() . $this->getUrl() . $this->getBody() .
+ $this->getEnclosureLink());
+ }
+
+ public function setUrl($url) {
+ $url = trim($url);
+ if (strpos($url, 'http') === 0 || strpos($url, 'magnet') === 0) {
+ parent::setUrl($url);
+ }
+ }
+
+ public function setBody($body) {
+ // FIXME: this should not happen if the target="_blank" is already
+ // on the link
+ parent::setBody(str_replace(
+ '<a', '<a target="_blank" rel="noreferrer"', $body
+ ));
+ }
+
+}
diff --git a/lib/Db/ItemMapper.php b/lib/Db/ItemMapper.php
new file mode 100644
index 000000000..091022c6c
--- /dev/null
+++ b/lib/Db/ItemMapper.php
@@ -0,0 +1,403 @@
+<?php
+/**
+ * ownCloud - News
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Alessandro Cosentino <cosenal@gmail.com>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @copyright Alessandro Cosentino 2012
+ * @copyright Bernhard Posselt 2012, 2014
+ */
+
+namespace OCA\News\Db;
+
+use Exception;
+use OCA\News\Utility\Time;
+use OCP\IDBConnection;
+
+
+class ItemMapper extends NewsMapper {
+
+ public function __construct(IDBConnection $db, Time $time) {
+ parent::__construct($db, 'news_items', Item::class, $time);
+ }
+
+ private function makeSelectQuery($prependTo = '', $oldestFirst = false,
+ $distinctFingerprint = false) {
+ if ($oldestFirst) {
+ $ordering = 'ASC';
+ } else {
+ $ordering = 'DESC';
+ }
+
+ return 'SELECT `items`.* FROM `*PREFIX*news_items` `items` ' .
+ 'JOIN `*PREFIX*news_feeds` `feeds` ' .
+ 'ON `feeds`.`id` = `items`.`feed_id` ' .
+ 'AND `feeds`.`deleted_at` = 0 ' .
+ 'AND `feeds`.`user_id` = ? ' .
+ $prependTo .
+ 'LEFT OUTER JOIN `*PREFIX*new