summaryrefslogtreecommitdiffstats
path: root/db/itemmapper.php
blob: 18684af82e6750271c554aa4bf60e885001808e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
Language:        Cpp
# BasedOnStyle:  LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands:   true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:   
  AfterCaseLabel:  false
  AfterClass:      false
  AfterControlStatement: false
  AfterEnum:       false
  AfterFunction:   false
  AfterNamespace:  false
  AfterObjCDeclaration: false
  AfterStruct:     false
  AfterUnion:      false
  AfterExternBlock: false
  BeforeCatch:     false
  BeforeElse:      false
  IndentBraces:    false
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit:     80
CommentPragmas:  '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat:   false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:   
  - foreach
  - Q_FOREACH
  - BOOST_FOREACH
IncludeBlocks:   Preserve
IncludeCategories: 
  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
    Priority:        2
  - Regex:           '^(<|"(gtest|gmock|isl|json)/)'
    Priority:        3
  - Regex:           '.*'
    Priority:        1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth:     2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd:   ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments:  true
SortIncludes:    true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles:  false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard:        Cpp11
StatementMacros: 
  - Q_UNUSED
  - Q
<?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\IDb;
use \OCP\AppFramework\Db\Mapper;


class ItemMapper extends Mapper implements IMapper {

	public function __construct(IDb $db){
		parent::__construct($db, 'news_items', '\OCA\News\Db\Item');
	}


	private function makeSelectQuery($prependTo, $oldestFirst=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*news_folders` `folders` ' .
				'ON `folders`.`id` = `feeds`.`folder_id` ' .
			'WHERE `feeds`.`folder_id` = 0 ' .
				'OR `folders`.`deleted_at` = 0 ' .
			'ORDER BY `items`.`id` ' . $ordering;
	}

	private function makeSelectQueryStatus($prependTo, $status, $oldestFirst=false) {
		// 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('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, $oldestFirst
		);
	}


	public function find($id, $userId){
		$sql = $this->makeSelectQuery('AND `items`.`id` = ? ');
		return $this->findEntity($sql, [$userId, $id]);
	}


	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 don't 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 = [$userId];

		$result = $this->execute($sql, $params)->fetch();

		return (int) $result['size'];
	}


	public function readAll($highestItemId, $time, $userId) {
		$sql = 'UPDATE `*PREFIX*news_items` ' .
			'SET `status` = `status` & ? ' .
			', `last_modified` = ? ' .
			'WHERE `feed_id` IN (' .
				'SELECT `id` FROM `*PREFIX*news_feeds` ' .
					'WHERE `user_id` = ? ' .
				') '.
			'AND `id` <= ?';
		$params = [~StatusFlag::UNREAD, $time, $userId, $highestItemId];
		$this->execute($sql, $params);
	}


	public function readFolder($folderId, $highestItemId, $time, $userId) {
		$sql = 'UPDATE `*PREFIX*news_items` ' .
			'SET `status` = `status` & ? ' .
			', `last_modified` = ? ' .
			'WHERE `feed_id` IN (' .
				'SELECT `id` FROM `*PREFIX*news_feeds` ' .
					'WHERE `folder_id` = ? ' .
					'AND `user_id` = ? ' .
				') '.
			'AND `id` <= ?';
		$params = [~StatusFlag::UNREAD, $time, $folderId, $userId,
			$highestItemId];
		$this->execute($sql, $params);
	}


	public function readFeed($feedId, $highestItemId, $time, $userId){
		$sql = 'UPDATE `*PREFIX*news_items` ' .
			'SET `status` = `status` & ? ' .
			', `last_modified` = ? ' .
				'WHERE `feed_id` = ? ' .
				'AND `id` <= ? ' .
				'AND EXISTS (' .
					'SELECT * FROM `*PREFIX*news_feeds` ' .
					'WHERE `user_id` = ? ' .
					'AND `id` = ? ) ';
		$params = [~StatusFlag::UNREAD, $time, $feedId, $highestItemId,
			$userId, $feedId];

		$this->execute($sql, $params);
	}


	private function getOperator($oldestFirst) {
		if($oldestFirst) {
			return '>';
		} else {
			return '<';
		}
	}


	public function findAllNew($updatedSince, $status, $userId){
		$sql = $this->makeSelectQueryStatus(
			'AND `items`.`last_modified` >= ? ', $status);
		$params = [$userId, $updatedSince];
		return $this->findEntities($sql, $params);
	}


	public function findAllNewFolder($id, $updatedSince, $status, $userId){
		$sql = 'AND `feeds`.`folder_id` = ? ' .
				'AND `items`.`last_modified` >= ? ';
		$sql = $this->makeSelectQueryStatus($sql, $status);
		$params = [$userId, $id, $updatedSince];
		return $this->findEntities($sql, $params);
	}


	public function findAllNewFeed($id, $updatedSince, $status, $userId){
		$sql = 'AND `items`.`feed_id` = ? ' .
				'AND `items`.`last_modified` >= ? ';
		$sql = $this->makeSelectQueryStatus($sql, $status);
		$params = [$userId, $id, $updatedSince];
		return $this->findEntities($sql, $params);
	}


	public function findAllFeed($id, $limit, $offset, $status, $oldestFirst, $userId){
		$params = [$userId, $id];
		$sql = 'AND `items`.`feed_id` = ? ';
		if($offset !== 0){
			$sql .= 'AND `items`.`id` ' . $this->getOperator($oldestFirst) . ' ? ';
			$params[] = $offset;
		}
		$sql = $this->makeSelectQueryStatus($sql, $status);
		return $this->findEntities($sql, $params, $limit);
	}


	public function findAllFolder($id, $limit, $offset, $status, $oldestFirst, $userId){
		$params = [$userId, $id];
		$sql = 'AND `feeds`.`folder_id` = ? ';
		if($offset !== 0){
			$sql .= 'AND `items`.`id` ' . $this->getOperator($oldestFirst) . ' ? ';
			$params[] = $offset;
		}
		$sql = $this->makeSelectQueryStatus($sql, $status);
		return $this->findEntities($sql, $params, $limit);
	}


	public function findAll($limit, $offset, $status, $oldestFirst, $userId){
		$params = [$userId];
		$sql = '';
		if($offset !== 0){
			$sql .= 'AND `items`.`id` ' . $this->getOperator($oldestFirst) . ' ? ';
			$params[] = $offset;
		}
		$sql = $this->makeSelectQueryStatus($sql, $status);
		return $this->findEntities($sql, $params, $limit);
	}


	public function findAllUnreadOrStarred($userId) {
		$params = [$userId];
		$status = StatusFlag::UNREAD | StatusFlag::STARRED;
		$sql = 'AND ((`items`.`status` & ' . $status . ') > 0) ';
		$sql = $this->makeSelectQuery($sql);
		return $this->findEntities($sql, $params);
	}


	public function findByGuidHash($guidHash, $feedId, $userId){
		$sql = $this->makeSelectQuery(
			'AND `items`.`guid_hash` = ? ' .
			'AND `feeds`.`id` = ? ');

		return $this->findEntity($sql, [$userId, $guidHash, $feedId]);
	}


	/**
	 * 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(*) - `feeds`.`articles_per_update` AS `size`, ' .
		'`items`.`feed_id` AS `feed_id` ' .
			'FROM `*PREFIX*news_items` `items` ' .
			'JOIN `*PREFIX*news_feeds` `feeds` ' .
				'ON `feeds`.`id` = `items`.`feed_id` ' .
			'WHERE NOT ((`items`.`status` & ?) > 0) ' .
			'GROUP BY `items`.`feed_id`, `feeds`.`articles_per_update` ' .
			'HAVING COUNT(*) > ?';
		$params = [$status, $threshold];
		$result = $this->execute($sql, $params);

		while($row = $result->fetch()) {

			$size = (int) $row['size'];
			$limit = $size - $threshold;

			if($limit > 0) {
				$params = [$status, $row['feed_id']];

				$sql = 'DELETE FROM `*PREFIX*news_items` ' .
				'WHERE NOT ((`status` & ?) > 0) ' .
				'AND `feed_id` = ? ' .
				'ORDER BY `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 = [$userId];

		$result = $this->findOneQuery($sql, $params);

		return (int) $result['max_id'];
	}


	/**
	 * Deletes all items of a user
	 * @param string $userId the name of the user
	 */
	public function deleteUser($userId) {
		$sql = 'DELETE FROM `*PREFIX*news_items` ' .
			'WHERE `feed_id` IN (' .
				'SELECT `feeds`.`id` FROM `*PREFIX*news_feeds` `feeds` ' .
					'WHERE `feeds`.`user_id` = ?' .
				')';

		$this->execute($sql, [$userId]);
	}


}