// next compute the context, supplying the query to manipulate directly
$this->articleFilter($context, $q);
}
// return the query
return $q;
}
protected function articleFilter(Context $context, QueryFilter $q = null) {
$q = $q ?? new QueryFilter;
$colDefs = $this->articleColumns();
// handle the simple context options
// handle the simple context options
$options = [
$options = [
// each context array consists of a column identifier (see $colDefs above), a comparison operator, and a data type; the "between" operator has special handling
// each context array consists of a column identifier (see $colDefs above), a comparison operator, and a data type; the "between" operator has special handling
@ -1639,9 +1681,6 @@ class Database {
}
}
} elseif (is_array($context->$m)) {
} elseif (is_array($context->$m)) {
// context option is an array of values
// context option is an array of values
if (!$context->$m) {
throw new Db\ExceptionInput("tooShort", ['field' => $m, 'action' => $this->caller(), 'min' => 1]); // must have at least one array element
* @param string $user The user who owns the articles to be modified
* @param string $user The user who owns the articles to be modified
* @param array $data An associative array of properties to modify. Anything not specified will remain unchanged
* @param array $data An associative array of properties to modify. Anything not specified will remain unchanged
* @param Context $context The query context to match articles against
* @param RootContext $context The query context to match articles against
* @param bool $updateTimestamp Whether to also update the timestamp. This should only be false if a mark is changed as a result of an automated action not taken by the user
* @param bool $updateTimestamp Whether to also update the timestamp. This should only be false if a mark is changed as a result of an automated action not taken by the user
*/
*/
public function articleMark(string $user, array $data, Context $context = null, bool $updateTimestamp = true): int {
public function articleMark(string $user, array $data, RootContext $context = null, bool $updateTimestamp = true): int {
$data = [
$data = [
'read' => $data['read'] ?? null,
'read' => $data['read'] ?? null,
'starred' => $data['starred'] ?? null,
'starred' => $data['starred'] ?? null,
@ -2147,7 +2168,7 @@ class Database {
}
}
/** Returns the numeric identifier of the most recent edition of an article matching the given context */
/** Returns the numeric identifier of the most recent edition of an article matching the given context */
public function editionLatest(string $user, Context $context = null): int {
public function editionLatest(string $user, RootContext $context = null): int {
@ -12,6 +12,8 @@ use JKingWeb\Arsse\ExceptionType;
use JKingWeb\Arsse\Feed\Exception as FeedException;
use JKingWeb\Arsse\Feed\Exception as FeedException;
use JKingWeb\Arsse\AbstractException;
use JKingWeb\Arsse\AbstractException;
use JKingWeb\Arsse\Context\Context;
use JKingWeb\Arsse\Context\Context;
use JKingWeb\Arsse\Context\UnionContext;
use JKingWeb\Arsse\Context\RootContext;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\ImportExport\OPML;
use JKingWeb\Arsse\ImportExport\OPML;
use JKingWeb\Arsse\ImportExport\Exception as ImportException;
use JKingWeb\Arsse\ImportExport\Exception as ImportException;
@ -886,12 +888,11 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
]);
]);
}
}
protected function computeContext(array $query, Context $c = null): Context {
protected function computeContext(array $query, Context $c): RootContext {
if ($query['before'] && $query['before']->getTimestamp() === 0) {
if ($query['before'] && $query['before']->getTimestamp() === 0) {
$query['before'] = null; // NOTE: This workaround is needed for compatibility with "Microflux for Miniflux", an Android Client
$query['before'] = null; // NOTE: This workaround is needed for compatibility with "Microflux for Miniflux", an Android Client
}
}
$c = ($c ?? new Context)
$c->limit($query['limit'] ?? self::DEFAULT_ENTRY_LIMIT) // NOTE: This does not honour user preferences
->limit($query['limit'] ?? self::DEFAULT_ENTRY_LIMIT) // NOTE: This does not honour user preferences
->offset($query['offset'])
->offset($query['offset'])
->starred($query['starred'])
->starred($query['starred'])
->modifiedRange($query['after'], $query['before']) // FIXME: This may not be the correct date field
->modifiedRange($query['after'], $query['before']) // FIXME: This may not be the correct date field
@ -904,17 +905,20 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
$c->folder($query['category_id'] - 1);
$c->folder($query['category_id'] - 1);
}
}
}
}
// FIXME: specifying e.g. ?status=read&status=removed should yield all hidden articles and all read articles, but the best we can do is all read articles which are or are not hidden
$status = array_unique($query['status']);
$status = array_unique($query['status']);
sort($status);
sort($status);
if ($status === ["read", "removed"]) {
if ($status === ["read", "removed"]) {
$c->unread(false);
$c1 = $c;
$c2 = clone $c;
$c = new UnionContext($c1->unread(false), $c2->hidden(true));
} elseif ($status === ["read", "unread"]) {
} elseif ($status === ["read", "unread"]) {
$c->hidden(false);
$c->hidden(false);
} elseif ($status === ["read"]) {
} elseif ($status === ["read"]) {
$c->hidden(false)->unread(false);
$c->hidden(false)->unread(false);
} elseif ($status === ["removed", "unread"]) {
} elseif ($status === ["removed", "unread"]) {
$c->unread(true);
$c1 = $c;
$c2 = clone $c;
$c = new UnionContext($c1->unread(true), $c2->hidden(true));
'Not modified in 2010 or 2015' => [(new Context)->not->modifiedRanges([["2010-01-01T00:00:00Z", "2010-12-31T23:59:59Z"], ["2015-01-01T00:00:00Z", "2015-12-31T23:59:59Z"]]), [1,3,5,7,19]],
'Not modified in 2010 or 2015' => [(new Context)->not->modifiedRanges([["2010-01-01T00:00:00Z", "2010-12-31T23:59:59Z"], ["2015-01-01T00:00:00Z", "2015-12-31T23:59:59Z"]]), [1,3,5,7,19]],
'Modified prior to 2010 or since 2015' => [(new Context)->modifiedRanges([[null, "2009-12-31T23:59:59Z"], ["2015-01-01T00:00:00Z", null]]), [1,3,5,7,19]],
'Modified prior to 2010 or since 2015' => [(new Context)->modifiedRanges([[null, "2009-12-31T23:59:59Z"], ["2015-01-01T00:00:00Z", null]]), [1,3,5,7,19]],
'Not modified prior to 2010 or since 2015' => [(new Context)->not->modifiedRanges([[null, "2009-12-31T23:59:59Z"], ["2015-01-01T00:00:00Z", null]]), [2,4,6,8,20]],
'Not modified prior to 2010 or since 2015' => [(new Context)->not->modifiedRanges([[null, "2009-12-31T23:59:59Z"], ["2015-01-01T00:00:00Z", null]]), [2,4,6,8,20]],
'Either read or hidden' => [(new UnionContext((new Context)->unread(false), (new Context)->hidden(true))), [1, 6, 19]],