1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-22 21:22:40 +00:00

Add article selection by tag

This commit is contained in:
J. King 2019-03-07 11:07:22 -05:00
parent e6f70527cf
commit 5de1844f6d
4 changed files with 160 additions and 89 deletions

View file

@ -12,6 +12,8 @@ use JKingWeb\Arsse\Misc\Date;
class ExclusionContext { class ExclusionContext {
public $folder; public $folder;
public $folderShallow; public $folderShallow;
public $tag;
public $tagName;
public $subscription; public $subscription;
public $edition; public $edition;
public $article; public $article;
@ -101,6 +103,14 @@ class ExclusionContext {
return $this->act(__FUNCTION__, func_num_args(), $spec); return $this->act(__FUNCTION__, func_num_args(), $spec);
} }
public function tag(int $spec = null) {
return $this->act(__FUNCTION__, func_num_args(), $spec);
}
public function tagName(string $spec = null) {
return $this->act(__FUNCTION__, func_num_args(), $spec);
}
public function subscription(int $spec = null) { public function subscription(int $spec = null) {
return $this->act(__FUNCTION__, func_num_args(), $spec); return $this->act(__FUNCTION__, func_num_args(), $spec);
} }

View file

@ -1325,6 +1325,21 @@ class Database {
$q->setWhereNot("arsse_articles.id in (select article from labelled where label_name = ?)", "str", $context->not->labelName); $q->setWhereNot("arsse_articles.id in (select article from labelled where label_name = ?)", "str", $context->not->labelName);
} }
} }
if ($context->tag() || $context->not->tag() || $context->tagName() || $context->not->tagName()) {
$q->setCTE("tagged(id,name,subscription)","SELECT arsse_tags.id, arsse_tags.name, arsse_tag_members.subscription FROM arsse_tag_members join arsse_tags on arsse_tags.id = arsse_tag_members.tag WHERE arsse_tags.owner = ? and assigned = 1", "str", $user);
if ($context->tag()) {
$q->setWhere("arsse_subscriptions.id in (select subscription from tagged where id = ?)", "int", $context->tag);
}
if ($context->not->tag()) {
$q->setWhereNot("arsse_subscriptions.id in (select subscription from tagged where id = ?)", "int", $context->not->tag);
}
if ($context->tagName()) {
$q->setWhere("arsse_subscriptions.id in (select subscription from tagged where name = ?)", "str", $context->tagName);
}
if ($context->not->tagName()) {
$q->setWhereNot("arsse_subscriptions.id in (select subscription from tagged where name = ?)", "str", $context->not->tagName);
}
}
if ($context->folder()) { if ($context->folder()) {
// add a common table expression to list the folder and its children so that we select from the entire subtree // add a common table expression to list the folder and its children so that we select from the entire subtree
$q->setCTE("folders(folder)", "SELECT ? union select id from arsse_folders join folders on parent = folder", "int", $context->folder); $q->setCTE("folders(folder)", "SELECT ? union select id from arsse_folders join folders on parent = folder", "int", $context->folder);

View file

@ -28,25 +28,6 @@ trait SeriesArticle {
["john.doe@example.net", "", "John Doe"], ["john.doe@example.net", "", "John Doe"],
], ],
], ],
'arsse_folders' => [
'columns' => [
'id' => "int",
'owner' => "str",
'parent' => "int",
'name' => "str",
],
'rows' => [
[1, "john.doe@example.com", null, "Technology"],
[2, "john.doe@example.com", 1, "Software"],
[3, "john.doe@example.com", 1, "Rocketry"],
[4, "jane.doe@example.com", null, "Politics"],
[5, "john.doe@example.com", null, "Politics"],
[6, "john.doe@example.com", 2, "Politics"],
[7, "john.doe@example.net", null, "Technology"],
[8, "john.doe@example.net", 7, "Software"],
[9, "john.doe@example.net", null, "Politics"],
]
],
'arsse_feeds' => [ 'arsse_feeds' => [
'columns' => [ 'columns' => [
'id' => "int", 'id' => "int",
@ -69,6 +50,42 @@ trait SeriesArticle {
[13,"http://example.com/13", "Feed 13"], [13,"http://example.com/13", "Feed 13"],
] ]
], ],
'arsse_folders' => [
'columns' => [
'id' => "int",
'owner' => "str",
'parent' => "int",
'name' => "str",
],
'rows' => [
[1, "john.doe@example.com", null, "Technology"],
[2, "john.doe@example.com", 1, "Software"],
[3, "john.doe@example.com", 1, "Rocketry"],
[4, "jane.doe@example.com", null, "Politics"],
[5, "john.doe@example.com", null, "Politics"],
[6, "john.doe@example.com", 2, "Politics"],
[7, "john.doe@example.net", null, "Technology"],
[8, "john.doe@example.net", 7, "Software"],
[9, "john.doe@example.net", null, "Politics"],
]
],
'arsse_tags' => [
'columns' => [
'id' => "int",
'owner' => "str",
'name' => "str",
],
'rows' => [
[1, "john.doe@example.com", "Technology"],
[2, "john.doe@example.com", "Software"],
[3, "john.doe@example.com", "Rocketry"],
[4, "jane.doe@example.com", "Politics"],
[5, "john.doe@example.com", "Politics"],
[6, "john.doe@example.net", "Technology"],
[7, "john.doe@example.net", "Software"],
[8, "john.doe@example.net", "Politics"],
]
],
'arsse_subscriptions' => [ 'arsse_subscriptions' => [
'columns' => [ 'columns' => [
'id' => "int", 'id' => "int",
@ -94,6 +111,25 @@ trait SeriesArticle {
[14,"john.doe@example.net",4, 7,null], [14,"john.doe@example.net",4, 7,null],
] ]
], ],
'arsse_tag_members' => [
'columns' => [
'tag' => "int",
'subscription' => "int",
'assigned' => "bool",
],
'rows' => [
[1,3,1],
[1,4,1],
[2,4,1],
[5,1,0],
[5,4,1],
[5,5,1],
[6,13,1],
[6,14,1],
[7,13,1],
[8,12,1],
],
],
'arsse_articles' => [ 'arsse_articles' => [
'columns' => [ 'columns' => [
'id' => "int", 'id' => "int",
@ -387,76 +423,84 @@ trait SeriesArticle {
public function provideContextMatches() { public function provideContextMatches() {
return [ return [
"Blank context" => [new Context, [1,2,3,4,5,6,7,8,19,20]], 'Blank context' => [new Context, [1,2,3,4,5,6,7,8,19,20]],
"Folder tree" => [(new Context)->folder(1), [5,6,7,8]], 'Folder tree' => [(new Context)->folder(1), [5,6,7,8]],
"Leaf folder" => [(new Context)->folder(6), [7,8]], 'Leaf folder' => [(new Context)->folder(6), [7,8]],
"Root folder only" => [(new Context)->folderShallow(0), [1,2,3,4]], 'Root folder only' => [(new Context)->folderShallow(0), [1,2,3,4]],
"Shallow folder" => [(new Context)->folderShallow(1), [5,6]], 'Shallow folder' => [(new Context)->folderShallow(1), [5,6]],
"Subscription" => [(new Context)->subscription(5), [19,20]], 'Subscription' => [(new Context)->subscription(5), [19,20]],
"Unread" => [(new Context)->subscription(5)->unread(true), [20]], 'Unread' => [(new Context)->subscription(5)->unread(true), [20]],
"Read" => [(new Context)->subscription(5)->unread(false), [19]], 'Read' => [(new Context)->subscription(5)->unread(false), [19]],
"Starred" => [(new Context)->starred(true), [1,20]], 'Starred' => [(new Context)->starred(true), [1,20]],
"Unstarred" => [(new Context)->starred(false), [2,3,4,5,6,7,8,19]], 'Unstarred' => [(new Context)->starred(false), [2,3,4,5,6,7,8,19]],
"Starred and Read" => [(new Context)->starred(true)->unread(false), [1]], 'Starred and Read' => [(new Context)->starred(true)->unread(false), [1]],
"Starred and Read in subscription" => [(new Context)->starred(true)->unread(false)->subscription(5), []], 'Starred and Read in subscription' => [(new Context)->starred(true)->unread(false)->subscription(5), []],
"Annotated" => [(new Context)->annotated(true), [2]], 'Annotated' => [(new Context)->annotated(true), [2]],
"Not annotated" => [(new Context)->annotated(false), [1,3,4,5,6,7,8,19,20]], 'Not annotated' => [(new Context)->annotated(false), [1,3,4,5,6,7,8,19,20]],
"Labelled" => [(new Context)->labelled(true), [1,5,8,19,20]], 'Labelled' => [(new Context)->labelled(true), [1,5,8,19,20]],
"Not labelled" => [(new Context)->labelled(false), [2,3,4,6,7]], 'Not labelled' => [(new Context)->labelled(false), [2,3,4,6,7]],
"Not after edition 999" => [(new Context)->subscription(5)->latestEdition(999), [19]], 'Not after edition 999' => [(new Context)->subscription(5)->latestEdition(999), [19]],
"Not after edition 19" => [(new Context)->subscription(5)->latestEdition(19), [19]], 'Not after edition 19' => [(new Context)->subscription(5)->latestEdition(19), [19]],
"Not before edition 999" => [(new Context)->subscription(5)->oldestEdition(999), [20]], 'Not before edition 999' => [(new Context)->subscription(5)->oldestEdition(999), [20]],
"Not before edition 1001" => [(new Context)->subscription(5)->oldestEdition(1001), [20]], 'Not before edition 1001' => [(new Context)->subscription(5)->oldestEdition(1001), [20]],
"Not after article 3" => [(new Context)->latestArticle(3), [1,2,3]], 'Not after article 3' => [(new Context)->latestArticle(3), [1,2,3]],
"Not before article 19" => [(new Context)->oldestArticle(19), [19,20]], 'Not before article 19' => [(new Context)->oldestArticle(19), [19,20]],
"Modified by author since 2005" => [(new Context)->modifiedSince("2005-01-01T00:00:00Z"), [2,4,6,8,20]], 'Modified by author since 2005' => [(new Context)->modifiedSince("2005-01-01T00:00:00Z"), [2,4,6,8,20]],
"Modified by author since 2010" => [(new Context)->modifiedSince("2010-01-01T00:00:00Z"), [2,4,6,8,20]], 'Modified by author since 2010' => [(new Context)->modifiedSince("2010-01-01T00:00:00Z"), [2,4,6,8,20]],
"Not modified by author since 2005" => [(new Context)->notModifiedSince("2005-01-01T00:00:00Z"), [1,3,5,7,19]], 'Not modified by author since 2005' => [(new Context)->notModifiedSince("2005-01-01T00:00:00Z"), [1,3,5,7,19]],
"Not modified by author since 2000" => [(new Context)->notModifiedSince("2000-01-01T00:00:00Z"), [1,3,5,7,19]], 'Not modified by author since 2000' => [(new Context)->notModifiedSince("2000-01-01T00:00:00Z"), [1,3,5,7,19]],
"Marked or labelled since 2014" => [(new Context)->markedSince("2014-01-01T00:00:00Z"), [8,19]], 'Marked or labelled since 2014' => [(new Context)->markedSince("2014-01-01T00:00:00Z"), [8,19]],
"Marked or labelled since 2010" => [(new Context)->markedSince("2010-01-01T00:00:00Z"), [2,4,6,8,19,20]], 'Marked or labelled since 2010' => [(new Context)->markedSince("2010-01-01T00:00:00Z"), [2,4,6,8,19,20]],
"Not marked or labelled since 2014" => [(new Context)->notMarkedSince("2014-01-01T00:00:00Z"), [1,2,3,4,5,6,7,20]], 'Not marked or labelled since 2014' => [(new Context)->notMarkedSince("2014-01-01T00:00:00Z"), [1,2,3,4,5,6,7,20]],
"Not marked or labelled since 2005" => [(new Context)->notMarkedSince("2005-01-01T00:00:00Z"), [1,3,5,7]], 'Not marked or labelled since 2005' => [(new Context)->notMarkedSince("2005-01-01T00:00:00Z"), [1,3,5,7]],
"Marked or labelled between 2000 and 2015" => [(new Context)->markedSince("2000-01-01T00:00:00Z")->notMarkedSince("2015-12-31T23:59:59Z"), [1,2,3,4,5,6,7,8,20]], 'Marked or labelled between 2000 and 2015' => [(new Context)->markedSince("2000-01-01T00:00:00Z")->notMarkedSince("2015-12-31T23:59:59Z"), [1,2,3,4,5,6,7,8,20]],
"Marked or labelled in 2010" => [(new Context)->markedSince("2010-01-01T00:00:00Z")->notMarkedSince("2010-12-31T23:59:59Z"), [2,4,6,20]], 'Marked or labelled in 2010' => [(new Context)->markedSince("2010-01-01T00:00:00Z")->notMarkedSince("2010-12-31T23:59:59Z"), [2,4,6,20]],
"Paged results" => [(new Context)->limit(2)->oldestEdition(4), [4,5]], 'Paged results' => [(new Context)->limit(2)->oldestEdition(4), [4,5]],
"Reversed paged results" => [(new Context)->limit(2)->latestEdition(7)->reverse(true), [7,6]], 'Reversed paged results' => [(new Context)->limit(2)->latestEdition(7)->reverse(true), [7,6]],
"With label ID 1" => [(new Context)->label(1), [1,19]], 'With label ID 1' => [(new Context)->label(1), [1,19]],
"With label ID 2" => [(new Context)->label(2), [1,5,20]], 'With label ID 2' => [(new Context)->label(2), [1,5,20]],
"With label 'Interesting'" => [(new Context)->labelName("Interesting"), [1,19]], 'With label "Interesting"' => [(new Context)->labelName("Interesting"), [1,19]],
"With label 'Fascinating'" => [(new Context)->labelName("Fascinating"), [1,5,20]], 'With label "Fascinating"' => [(new Context)->labelName("Fascinating"), [1,5,20]],
"Article ID 20" => [(new Context)->article(20), [20]], 'Article ID 20' => [(new Context)->article(20), [20]],
"Edition ID 1001" => [(new Context)->edition(1001), [20]], 'Edition ID 1001' => [(new Context)->edition(1001), [20]],
"Multiple articles" => [(new Context)->articles([1,20,50]), [1,20]], 'Multiple articles' => [(new Context)->articles([1,20,50]), [1,20]],
"Multiple starred articles" => [(new Context)->articles([1,2,3])->starred(true), [1]], 'Multiple starred articles' => [(new Context)->articles([1,2,3])->starred(true), [1]],
"Multiple unstarred articles" => [(new Context)->articles([1,2,3])->starred(false), [2,3]], 'Multiple unstarred articles' => [(new Context)->articles([1,2,3])->starred(false), [2,3]],
"Multiple articles" => [(new Context)->articles([1,20,50]), [1,20]], 'Multiple articles' => [(new Context)->articles([1,20,50]), [1,20]],
"Multiple editions" => [(new Context)->editions([1,1001,50]), [1,20]], 'Multiple editions' => [(new Context)->editions([1,1001,50]), [1,20]],
"150 articles" => [(new Context)->articles(range(1, Database::LIMIT_SET_SIZE * 3)), [1,2,3,4,5,6,7,8,19,20]], '150 articles' => [(new Context)->articles(range(1, Database::LIMIT_SET_SIZE * 3)), [1,2,3,4,5,6,7,8,19,20]],
"Search title or content 1" => [(new Context)->searchTerms(["Article"]), [1,2,3]], 'Search title or content 1' => [(new Context)->searchTerms(["Article"]), [1,2,3]],
"Search title or content 2" => [(new Context)->searchTerms(["one", "first"]), [1]], 'Search title or content 2' => [(new Context)->searchTerms(["one", "first"]), [1]],
"Search title or content 3" => [(new Context)->searchTerms(["one first"]), []], 'Search title or content 3' => [(new Context)->searchTerms(["one first"]), []],
"Search title 1" => [(new Context)->titleTerms(["two"]), [2]], 'Search title 1' => [(new Context)->titleTerms(["two"]), [2]],
"Search title 2" => [(new Context)->titleTerms(["title two"]), [2]], 'Search title 2' => [(new Context)->titleTerms(["title two"]), [2]],
"Search title 3" => [(new Context)->titleTerms(["two", "title"]), [2]], 'Search title 3' => [(new Context)->titleTerms(["two", "title"]), [2]],
"Search title 4" => [(new Context)->titleTerms(["two title"]), []], 'Search title 4' => [(new Context)->titleTerms(["two title"]), []],
"Search note 1" => [(new Context)->annotationTerms(["some"]), [2]], 'Search note 1' => [(new Context)->annotationTerms(["some"]), [2]],
"Search note 2" => [(new Context)->annotationTerms(["some Note"]), [2]], 'Search note 2' => [(new Context)->annotationTerms(["some Note"]), [2]],
"Search note 3" => [(new Context)->annotationTerms(["note", "some"]), [2]], 'Search note 3' => [(new Context)->annotationTerms(["note", "some"]), [2]],
"Search note 4" => [(new Context)->annotationTerms(["some", "sauce"]), []], 'Search note 4' => [(new Context)->annotationTerms(["some", "sauce"]), []],
"Search author 1" => [(new Context)->authorTerms(["doe"]), [4,5,6,7]], 'Search author 1' => [(new Context)->authorTerms(["doe"]), [4,5,6,7]],
"Search author 2" => [(new Context)->authorTerms(["jane doe"]), [6,7]], 'Search author 2' => [(new Context)->authorTerms(["jane doe"]), [6,7]],
"Search author 3" => [(new Context)->authorTerms(["doe", "jane"]), [6,7]], 'Search author 3' => [(new Context)->authorTerms(["doe", "jane"]), [6,7]],
"Search author 4" => [(new Context)->authorTerms(["doe jane"]), []], 'Search author 4' => [(new Context)->authorTerms(["doe jane"]), []],
"Folder tree 1 excluding subscription 4" => [(new Context)->not->subscription(4)->folder(1), [5,6]], 'Folder tree 1 excluding subscription 4' => [(new Context)->not->subscription(4)->folder(1), [5,6]],
"Folder tree 1 excluding articles 7 and 8" => [(new Context)->folder(1)->not->articles([7,8]), [5,6]], 'Folder tree 1 excluding articles 7 and 8' => [(new Context)->folder(1)->not->articles([7,8]), [5,6]],
"Folder tree 1 excluding no articles" => [(new Context)->folder(1)->not->articles([]), [5,6,7,8]], 'Folder tree 1 excluding no articles' => [(new Context)->folder(1)->not->articles([]), [5,6,7,8]],
"Marked or labelled between 2000 and 2015 excluding in 2010" => [(new Context)->markedSince("2000-01-01T00:00:00Z")->notMarkedSince("2015-12-31T23:59:59")->not->markedSince("2010-01-01T00:00:00Z")->not->notMarkedSince("2010-12-31T23:59:59Z"), [1,3,5,7,8]], 'Marked or labelled between 2000 and 2015 excluding in 2010' => [(new Context)->markedSince("2000-01-01T00:00:00Z")->notMarkedSince("2015-12-31T23:59:59")->not->markedSince("2010-01-01T00:00:00Z")->not->notMarkedSince("2010-12-31T23:59:59Z"), [1,3,5,7,8]],
"Search with exclusion" => [(new Context)->searchTerms(["Article"])->not->searchTerms(["one", "two"]), [3]], 'Search with exclusion' => [(new Context)->searchTerms(["Article"])->not->searchTerms(["one", "two"]), [3]],
"Excluded folder tree" => [(new Context)->not->folder(1), [1,2,3,4,19,20]], 'Excluded folder tree' => [(new Context)->not->folder(1), [1,2,3,4,19,20]],
"Excluding label ID 2" => [(new Context)->not->label(2), [2,3,4,6,7,8,19]], 'Excluding label ID 2' => [(new Context)->not->label(2), [2,3,4,6,7,8,19]],
"Excluding label 'Fascinating'" => [(new Context)->not->labelName("Fascinating"), [2,3,4,6,7,8,19]], 'Excluding label "Fascinating"' => [(new Context)->not->labelName("Fascinating"), [2,3,4,6,7,8,19]],
"Search 501 terms" => [(new Context)->searchTerms(array_merge(range(1,500),[str_repeat("a", 1000)])), []], 'Search 501 terms' => [(new Context)->searchTerms(array_merge(range(1,500),[str_repeat("a", 1000)])), []],
'With tag ID 1' => [(new Context)->tag(1), [5,6,7,8]],
'With tag ID 5' => [(new Context)->tag(5), [7,8,19,20]],
'With tag "Technology"' => [(new Context)->tagName("Technology"), [5,6,7,8]],
'With tag "Politics"' => [(new Context)->tagName("Politics"), [7,8,19,20]],
'Excluding tag ID 1' => [(new Context)->not->tag(1), [1,2,3,4,19,20]],
'Excluding tag ID 5' => [(new Context)->not->tag(5), [1,2,3,4,5,6]],
'Excluding tag "Technology"' => [(new Context)->not->tagName("Technology"), [1,2,3,4,19,20]],
'Excluding tag "Politics"' => [(new Context)->not->tagName("Politics"), [1,2,3,4,5,6]],
]; ];
} }

View file

@ -30,6 +30,8 @@ class TestContext extends \JKingWeb\Arsse\Test\AbstractTest {
'offset' => 5, 'offset' => 5,
'folder' => 42, 'folder' => 42,
'folderShallow' => 42, 'folderShallow' => 42,
'tag' => 44,
'tagName' => "XLIV",
'subscription' => 2112, 'subscription' => 2112,
'article' => 255, 'article' => 255,
'edition' => 65535, 'edition' => 65535,