mirror of
https://github.com/go-shiori/shiori.git
synced 2025-01-16 04:48:30 +08:00
Add method to exclude or include some tags
This commit is contained in:
parent
11c6680397
commit
4e76288e09
8 changed files with 311 additions and 66 deletions
|
@ -25,6 +25,7 @@ func printCmd() *cobra.Command {
|
|||
cmd.Flags().BoolP("index-only", "i", false, "Only print the index of bookmarks")
|
||||
cmd.Flags().StringP("search", "s", "", "Search bookmark with specified keyword")
|
||||
cmd.Flags().StringSliceP("tags", "t", []string{}, "Print bookmarks with matching tag(s)")
|
||||
cmd.Flags().StringSliceP("exclude-tags", "e", []string{}, "Print bookmarks without these tag(s)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -36,6 +37,7 @@ func printHandler(cmd *cobra.Command, args []string) {
|
|||
useJSON, _ := cmd.Flags().GetBool("json")
|
||||
indexOnly, _ := cmd.Flags().GetBool("index-only")
|
||||
orderLatest, _ := cmd.Flags().GetBool("latest")
|
||||
excludedTags, _ := cmd.Flags().GetStringSlice("exclude-tags")
|
||||
|
||||
// Convert args to ids
|
||||
ids, err := parseStrIndices(args)
|
||||
|
@ -51,10 +53,11 @@ func printHandler(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
|
||||
searchOptions := database.GetBookmarksOptions{
|
||||
IDs: ids,
|
||||
Tags: tags,
|
||||
Keyword: keyword,
|
||||
OrderMethod: orderMethod,
|
||||
IDs: ids,
|
||||
Tags: tags,
|
||||
ExcludedTags: excludedTags,
|
||||
Keyword: keyword,
|
||||
OrderMethod: orderMethod,
|
||||
}
|
||||
|
||||
bookmarks, err := db.GetBookmarks(searchOptions)
|
||||
|
|
|
@ -20,13 +20,14 @@ const (
|
|||
|
||||
// GetBookmarksOptions is options for fetching bookmarks from database.
|
||||
type GetBookmarksOptions struct {
|
||||
IDs []int
|
||||
Tags []string
|
||||
Keyword string
|
||||
WithContent bool
|
||||
OrderMethod OrderMethod
|
||||
Limit int
|
||||
Offset int
|
||||
IDs []int
|
||||
Tags []string
|
||||
ExcludedTags []string
|
||||
Keyword string
|
||||
WithContent bool
|
||||
OrderMethod OrderMethod
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
// DB is interface for accessing and manipulating data in database.
|
||||
|
|
|
@ -228,28 +228,72 @@ func (db *MySQLDatabase) GetBookmarks(opts GetBookmarksOptions) ([]model.Bookmar
|
|||
// Add where clause
|
||||
args := []interface{}{}
|
||||
|
||||
// Add where clause for IDs
|
||||
if len(opts.IDs) > 0 {
|
||||
query += ` AND id IN (?)`
|
||||
args = append(args, opts.IDs)
|
||||
}
|
||||
|
||||
// Add where clause for search keyword
|
||||
if opts.Keyword != "" {
|
||||
query += ` AND (
|
||||
url LIKE ? OR
|
||||
MATCH(title, excerpt, content) AGAINST (? IN BOOLEAN MODE)
|
||||
)`
|
||||
|
||||
args = append(args,
|
||||
"%"+opts.Keyword+"%",
|
||||
opts.Keyword)
|
||||
args = append(args, "%"+opts.Keyword+"%", opts.Keyword)
|
||||
}
|
||||
|
||||
// Add where clause for tags.
|
||||
// First we check for * in excluded and included tags,
|
||||
// which means all tags will be excluded and included, respectively.
|
||||
excludeAllTags := false
|
||||
for _, excludedTag := range opts.ExcludedTags {
|
||||
if excludedTag == "*" {
|
||||
excludeAllTags = true
|
||||
opts.ExcludedTags = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
includeAllTags := false
|
||||
for _, includedTag := range opts.Tags {
|
||||
if includedTag == "*" {
|
||||
includeAllTags = true
|
||||
opts.Tags = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If all tags excluded, we will only show bookmark without tags.
|
||||
// In other hand, if all tags included, we will only show bookmark with tags.
|
||||
if excludeAllTags {
|
||||
query += ` AND id NOT IN (SELECT DISTINCT bookmark_id FROM bookmark_tag)`
|
||||
} else if includeAllTags {
|
||||
query += ` AND id IN (SELECT DISTINCT bookmark_id FROM bookmark_tag)`
|
||||
}
|
||||
|
||||
// Now we only need to find the normal tags
|
||||
if len(opts.Tags) > 0 {
|
||||
query += ` AND id IN (
|
||||
SELECT bookmark_id FROM bookmark_tag
|
||||
WHERE tag_id IN (SELECT id FROM tag WHERE name IN (?)))`
|
||||
SELECT bt.bookmark_id
|
||||
FROM bookmark_tag bt
|
||||
LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE t.name IN(?)
|
||||
GROUP BY bt.bookmark_id
|
||||
HAVING COUNT(bt.bookmark_id) = ?)`
|
||||
|
||||
args = append(args, opts.Tags)
|
||||
args = append(args, opts.Tags, len(opts.Tags))
|
||||
}
|
||||
|
||||
if len(opts.ExcludedTags) > 0 {
|
||||
query += ` AND id NOT IN (
|
||||
SELECT DISTINCT bt.bookmark_id
|
||||
FROM bookmark_tag bt
|
||||
LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE t.name IN(?))`
|
||||
|
||||
args = append(args, opts.ExcludedTags)
|
||||
}
|
||||
|
||||
// Add order clause
|
||||
|
@ -312,11 +356,13 @@ func (db *MySQLDatabase) GetBookmarksCount(opts GetBookmarksOptions) (int, error
|
|||
// Add where clause
|
||||
args := []interface{}{}
|
||||
|
||||
// Add where clause for IDs
|
||||
if len(opts.IDs) > 0 {
|
||||
query += ` AND id IN (?)`
|
||||
args = append(args, opts.IDs)
|
||||
}
|
||||
|
||||
// Add where clause for search keyword
|
||||
if opts.Keyword != "" {
|
||||
query += ` AND (
|
||||
url LIKE ? OR
|
||||
|
@ -328,12 +374,56 @@ func (db *MySQLDatabase) GetBookmarksCount(opts GetBookmarksOptions) (int, error
|
|||
opts.Keyword)
|
||||
}
|
||||
|
||||
// Add where clause for tags.
|
||||
// First we check for * in excluded and included tags,
|
||||
// which means all tags will be excluded and included, respectively.
|
||||
excludeAllTags := false
|
||||
for _, excludedTag := range opts.ExcludedTags {
|
||||
if excludedTag == "*" {
|
||||
excludeAllTags = true
|
||||
opts.ExcludedTags = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
includeAllTags := false
|
||||
for _, includedTag := range opts.Tags {
|
||||
if includedTag == "*" {
|
||||
includeAllTags = true
|
||||
opts.Tags = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If all tags excluded, we will only show bookmark without tags.
|
||||
// In other hand, if all tags included, we will only show bookmark with tags.
|
||||
if excludeAllTags {
|
||||
query += ` AND id NOT IN (SELECT DISTINCT bookmark_id FROM bookmark_tag)`
|
||||
} else if includeAllTags {
|
||||
query += ` AND id IN (SELECT DISTINCT bookmark_id FROM bookmark_tag)`
|
||||
}
|
||||
|
||||
// Now we only need to find the normal tags
|
||||
if len(opts.Tags) > 0 {
|
||||
query += ` AND id IN (
|
||||
SELECT bookmark_id FROM bookmark_tag
|
||||
WHERE tag_id IN (SELECT id FROM tag WHERE name IN (?)))`
|
||||
SELECT bt.bookmark_id
|
||||
FROM bookmark_tag bt
|
||||
LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE t.name IN(?)
|
||||
GROUP BY bt.bookmark_id
|
||||
HAVING COUNT(bt.bookmark_id) = ?)`
|
||||
|
||||
args = append(args, opts.Tags)
|
||||
args = append(args, opts.Tags, len(opts.Tags))
|
||||
}
|
||||
|
||||
if len(opts.ExcludedTags) > 0 {
|
||||
query += ` AND id NOT IN (
|
||||
SELECT DISTINCT bt.bookmark_id
|
||||
FROM bookmark_tag bt
|
||||
LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE t.name IN(?))`
|
||||
|
||||
args = append(args, opts.ExcludedTags)
|
||||
}
|
||||
|
||||
// Expand query, because some of the args might be an array
|
||||
|
|
|
@ -224,11 +224,13 @@ func (db *SQLiteDatabase) GetBookmarks(opts GetBookmarksOptions) ([]model.Bookma
|
|||
// Add where clause
|
||||
args := []interface{}{}
|
||||
|
||||
// Add where clause for IDs
|
||||
if len(opts.IDs) > 0 {
|
||||
query += ` AND b.id IN (?)`
|
||||
args = append(args, opts.IDs)
|
||||
}
|
||||
|
||||
// Add where clause for search keyword
|
||||
if opts.Keyword != "" {
|
||||
query += ` AND (b.url LIKE ? OR b.excerpt LIKE ? OR b.id IN (
|
||||
SELECT docid id
|
||||
|
@ -242,12 +244,56 @@ func (db *SQLiteDatabase) GetBookmarks(opts GetBookmarksOptions) ([]model.Bookma
|
|||
opts.Keyword)
|
||||
}
|
||||
|
||||
// Add where clause for tags.
|
||||
// First we check for * in excluded and included tags,
|
||||
// which means all tags will be excluded and included, respectively.
|
||||
excludeAllTags := false
|
||||
for _, excludedTag := range opts.ExcludedTags {
|
||||
if excludedTag == "*" {
|
||||
excludeAllTags = true
|
||||
opts.ExcludedTags = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
includeAllTags := false
|
||||
for _, includedTag := range opts.Tags {
|
||||
if includedTag == "*" {
|
||||
includeAllTags = true
|
||||
opts.Tags = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If all tags excluded, we will only show bookmark without tags.
|
||||
// In other hand, if all tags included, we will only show bookmark with tags.
|
||||
if excludeAllTags {
|
||||
query += ` AND b.id NOT IN (SELECT DISTINCT bookmark_id FROM bookmark_tag)`
|
||||
} else if includeAllTags {
|
||||
query += ` AND b.id IN (SELECT DISTINCT bookmark_id FROM bookmark_tag)`
|
||||
}
|
||||
|
||||
// Now we only need to find the normal tags
|
||||
if len(opts.Tags) > 0 {
|
||||
query += ` AND b.id IN (
|
||||
SELECT bookmark_id FROM bookmark_tag
|
||||
WHERE tag_id IN (SELECT id FROM tag WHERE name IN (?)))`
|
||||
SELECT bt.bookmark_id
|
||||
FROM bookmark_tag bt
|
||||
LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE t.name IN(?)
|
||||
GROUP BY bt.bookmark_id
|
||||
HAVING COUNT(bt.bookmark_id) = ?)`
|
||||
|
||||
args = append(args, opts.Tags)
|
||||
args = append(args, opts.Tags, len(opts.Tags))
|
||||
}
|
||||
|
||||
if len(opts.ExcludedTags) > 0 {
|
||||
query += ` AND b.id NOT IN (
|
||||
SELECT DISTINCT bt.bookmark_id
|
||||
FROM bookmark_tag bt
|
||||
LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE t.name IN(?))`
|
||||
|
||||
args = append(args, opts.ExcludedTags)
|
||||
}
|
||||
|
||||
// Add order clause
|
||||
|
@ -313,11 +359,13 @@ func (db *SQLiteDatabase) GetBookmarksCount(opts GetBookmarksOptions) (int, erro
|
|||
// Add where clause
|
||||
args := []interface{}{}
|
||||
|
||||
// Add where clause for IDs
|
||||
if len(opts.IDs) > 0 {
|
||||
query += ` AND b.id IN (?)`
|
||||
args = append(args, opts.IDs)
|
||||
}
|
||||
|
||||
// Add where clause for search keyword
|
||||
if opts.Keyword != "" {
|
||||
query += ` AND (b.url LIKE ? OR b.excerpt LIKE ? OR b.id IN (
|
||||
SELECT docid id
|
||||
|
@ -331,12 +379,56 @@ func (db *SQLiteDatabase) GetBookmarksCount(opts GetBookmarksOptions) (int, erro
|
|||
opts.Keyword)
|
||||
}
|
||||
|
||||
// Add where clause for tags.
|
||||
// First we check for * in excluded and included tags,
|
||||
// which means all tags will be excluded and included, respectively.
|
||||
excludeAllTags := false
|
||||
for _, excludedTag := range opts.ExcludedTags {
|
||||
if excludedTag == "*" {
|
||||
excludeAllTags = true
|
||||
opts.ExcludedTags = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
includeAllTags := false
|
||||
for _, includedTag := range opts.Tags {
|
||||
if includedTag == "*" {
|
||||
includeAllTags = true
|
||||
opts.Tags = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If all tags excluded, we will only show bookmark without tags.
|
||||
// In other hand, if all tags included, we will only show bookmark with tags.
|
||||
if excludeAllTags {
|
||||
query += ` AND b.id NOT IN (SELECT DISTINCT bookmark_id FROM bookmark_tag)`
|
||||
} else if includeAllTags {
|
||||
query += ` AND b.id IN (SELECT DISTINCT bookmark_id FROM bookmark_tag)`
|
||||
}
|
||||
|
||||
// Now we only need to find the normal tags
|
||||
if len(opts.Tags) > 0 {
|
||||
query += ` AND b.id IN (
|
||||
SELECT bookmark_id FROM bookmark_tag
|
||||
WHERE tag_id IN (SELECT id FROM tag WHERE name IN (?)))`
|
||||
SELECT bt.bookmark_id
|
||||
FROM bookmark_tag bt
|
||||
LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE t.name IN(?)
|
||||
GROUP BY bt.bookmark_id
|
||||
HAVING COUNT(bt.bookmark_id) = ?)`
|
||||
|
||||
args = append(args, opts.Tags)
|
||||
args = append(args, opts.Tags, len(opts.Tags))
|
||||
}
|
||||
|
||||
if len(opts.ExcludedTags) > 0 {
|
||||
query += ` AND b.id NOT IN (
|
||||
SELECT DISTINCT bt.bookmark_id
|
||||
FROM bookmark_tag bt
|
||||
LEFT JOIN tag t ON bt.tag_id = t.id
|
||||
WHERE t.name IN(?))`
|
||||
|
||||
args = append(args, opts.ExcludedTags)
|
||||
}
|
||||
|
||||
// Expand query, because some of the args might be an array
|
||||
|
|
|
@ -11,7 +11,7 @@ var template = `
|
|||
<p class="id" v-show="showId">{{id}}</p>
|
||||
</a>
|
||||
<div class="bookmark-tags" v-if="tags.length > 0">
|
||||
<a v-for="tag in tags" @click="tagClicked(tag.name)">{{tag.name}}</a>
|
||||
<a v-for="tag in tags" @click="tagClicked($event, tag.name)">{{tag.name}}</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="bookmark-menu">
|
||||
|
@ -78,8 +78,8 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
tagClicked(name) {
|
||||
this.$emit("tag-clicked", name);
|
||||
tagClicked(name, event) {
|
||||
this.$emit("tag-clicked", name, event);
|
||||
},
|
||||
selectBookmark() {
|
||||
this.$emit("select", this.eventItem);
|
||||
|
|
|
@ -8,7 +8,7 @@ var template = `
|
|||
<a title="Add new bookmark" @click="showDialogAdd">
|
||||
<i class="fas fa-fw fa-plus-circle"></i>
|
||||
</a>
|
||||
<a title="Show tags" @click="showDialogTags">
|
||||
<a v-if="tags.length > 0" title="Show tags" @click="showDialogTags">
|
||||
<i class="fas fa-fw fa-tags"></i>
|
||||
</a>
|
||||
<a title="Batch edit" @click="toggleEditMode">
|
||||
|
@ -46,6 +46,7 @@ var template = `
|
|||
:imageURL="book.imageURL"
|
||||
:hasContent="book.hasContent"
|
||||
:hasArchive="book.hasArchive"
|
||||
:tags="book.tags"
|
||||
:index="index"
|
||||
:key="book.id"
|
||||
:editMode="editMode"
|
||||
|
@ -53,7 +54,7 @@ var template = `
|
|||
:listMode="displayOptions.listMode"
|
||||
:selected="isSelected(book.id)"
|
||||
@select="toggleSelection"
|
||||
@tag-clicked="filterTag"
|
||||
@tag-clicked="bookmarkTagClicked"
|
||||
@edit="showDialogEdit"
|
||||
@delete="showDialogDelete"
|
||||
@update="showDialogUpdateCache">
|
||||
|
@ -68,8 +69,10 @@ var template = `
|
|||
<p class="empty-message" v-if="!loading && listIsEmpty">No saved bookmarks yet :(</p>
|
||||
<div class="loading-overlay" v-if="loading"><i class="fas fa-fw fa-spin fa-spinner"></i></div>
|
||||
<custom-dialog id="dialog-tags" v-bind="dialogTags">
|
||||
<a v-for="(tag, idx) in tags" @click="tagClicked(idx, tag)">
|
||||
{{tag.name}}<span>{{tag.nBookmarks}}</span>
|
||||
<a @click="filterTag('*')">(all tagged)</a>
|
||||
<a @click="filterTag('*', true)">(all untagged)</a>
|
||||
<a v-for="(tag, idx) in tags" @click="dialogTagClicked($event, idx, tag)">
|
||||
#{{tag.name}}<span>{{tag.nBookmarks}}</span>
|
||||
</a>
|
||||
</custom-dialog>
|
||||
<custom-dialog v-bind="dialog"/>
|
||||
|
@ -156,33 +159,46 @@ export default {
|
|||
fetchTags = (typeof fetchTags === "boolean") ? fetchTags : false;
|
||||
|
||||
// Parse search query
|
||||
var rxTagA = /['"]#([^'"]+)['"]/g, // "#tag with space"
|
||||
rxTagB = /(^|\s+)#(\S+)/g, // #tag-without-space
|
||||
keyword = this.search,
|
||||
var keyword = this.search,
|
||||
rxExcludeTagA = /(^|\s)-tag:["']([^"']+)["']/i, // -tag:"with space"
|
||||
rxExcludeTagB = /(^|\s)-tag:(\S+)/i, // -tag:without-space
|
||||
rxIncludeTagA = /(^|\s)tag:["']([^"']+)["']/i, // tag:"with space"
|
||||
rxIncludeTagB = /(^|\s)tag:(\S+)/i, // tag:without-space
|
||||
tags = [],
|
||||
excludedTags = [],
|
||||
rxResult;
|
||||
|
||||
// Fetch tag A first
|
||||
while (rxResult = rxTagA.exec(keyword)) {
|
||||
tags.push(rxResult[1]);
|
||||
// Get excluded tag first, while also removing it from keyword
|
||||
while (rxResult = rxExcludeTagA.exec(keyword)) {
|
||||
keyword = keyword.replace(rxResult[0], "");
|
||||
excludedTags.push(rxResult[2]);
|
||||
}
|
||||
|
||||
// Clear tag A from keyword
|
||||
keyword = keyword.replace(rxTagA, "");
|
||||
while (rxResult = rxExcludeTagB.exec(keyword)) {
|
||||
keyword = keyword.replace(rxResult[0], "");
|
||||
excludedTags.push(rxResult[2]);
|
||||
}
|
||||
|
||||
// Fetch tag B
|
||||
while (rxResult = rxTagB.exec(keyword)) {
|
||||
// Get included tags
|
||||
while (rxResult = rxIncludeTagA.exec(keyword)) {
|
||||
keyword = keyword.replace(rxResult[0], "");
|
||||
tags.push(rxResult[2]);
|
||||
}
|
||||
|
||||
// Clear tag B from keyword, then trim keyword
|
||||
keyword = keyword.replace(rxTagB, "").trim().replace(/\s+/g, " ");
|
||||
while (rxResult = rxIncludeTagB.exec(keyword)) {
|
||||
keyword = keyword.replace(rxResult[0], "");
|
||||
tags.push(rxResult[2]);
|
||||
}
|
||||
|
||||
// Trim keyword
|
||||
keyword = keyword.trim().replace(/\s+/g, " ");
|
||||
|
||||
// Prepare URL for API
|
||||
var url = new URL("/api/bookmarks", document.URL);
|
||||
url.search = new URLSearchParams({
|
||||
keyword: keyword,
|
||||
tags: tags.join(","),
|
||||
exclude: excludedTags.join(","),
|
||||
page: this.page
|
||||
});
|
||||
|
||||
|
@ -268,23 +284,59 @@ export default {
|
|||
isSelected(bookId) {
|
||||
return this.selection.findIndex(el => el.id === bookId) > -1;
|
||||
},
|
||||
tagClicked(idx, tag) {
|
||||
dialogTagClicked(event, idx, tag) {
|
||||
if (!this.dialogTags.editMode) {
|
||||
this.filterTag(tag.name);
|
||||
this.filterTag(tag.name, event.altKey);
|
||||
} else {
|
||||
this.dialogTags.visible = false;
|
||||
this.showDialogRenameTag(idx, tag);
|
||||
}
|
||||
},
|
||||
filterTag(tagName) {
|
||||
var rxSpace = /\s+/g,
|
||||
newTag = rxSpace.test(tagName) ? `"#${tagName}"` : `#${tagName}`;
|
||||
bookmarkTagClicked(event, tagName) {
|
||||
this.filterTag(tagName, event.altKey);
|
||||
},
|
||||
filterTag(tagName, excludeMode) {
|
||||
// Set default parameter
|
||||
excludeMode = (typeof excludeMode === "boolean") ? excludeMode : false;
|
||||
|
||||
if (!this.search.includes(newTag)) {
|
||||
this.search += ` ${newTag}`;
|
||||
this.search = this.search.trim();
|
||||
if (tagName === "*") {
|
||||
this.search = excludeMode ? "-tag:*" : "tag:*";
|
||||
this.loadData();
|
||||
return;
|
||||
}
|
||||
|
||||
var rxSpace = /\s+/g,
|
||||
includeTag = rxSpace.test(tagName) ? `tag:"${tagName}"` : `tag:${tagName}`,
|
||||
excludeTag = "-" + includeTag,
|
||||
rxIncludeTag = new RegExp(`(^|\\s)${includeTag}`, "ig"),
|
||||
rxExcludeTag = new RegExp(`(^|\\s)${excludeTag}`, "ig"),
|
||||
search = this.search;
|
||||
|
||||
if (excludeMode) {
|
||||
if (rxExcludeTag.test(search)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rxIncludeTag.test(search)) {
|
||||
this.search = search.replace(rxIncludeTag, "$1" + excludeTag);
|
||||
} else {
|
||||
search += ` ${excludeTag}`;
|
||||
this.search = search.trim();
|
||||
}
|
||||
} else {
|
||||
if (rxIncludeTag.test(search)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rxExcludeTag.test(search)) {
|
||||
this.search = search.replace(rxExcludeTag, "$1" + includeTag);
|
||||
} else {
|
||||
search += ` ${includeTag}`;
|
||||
this.search = search.trim();
|
||||
}
|
||||
}
|
||||
|
||||
this.loadData();
|
||||
},
|
||||
showDialogAdd() {
|
||||
this.showDialog({
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -121,14 +121,20 @@ func (h *handler) apiGetBookmarks(w http.ResponseWriter, r *http.Request, ps htt
|
|||
|
||||
// Get URL queries
|
||||
keyword := r.URL.Query().Get("keyword")
|
||||
strTags := r.URL.Query().Get("tags")
|
||||
strPage := r.URL.Query().Get("page")
|
||||
strTags := r.URL.Query().Get("tags")
|
||||
strExcludedTags := r.URL.Query().Get("exclude")
|
||||
|
||||
tags := strings.Split(strTags, ",")
|
||||
if len(tags) == 1 && tags[0] == "" {
|
||||
tags = []string{}
|
||||
}
|
||||
|
||||
excludedTags := strings.Split(strExcludedTags, ",")
|
||||
if len(excludedTags) == 1 && excludedTags[0] == "" {
|
||||
excludedTags = []string{}
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(strPage)
|
||||
if page < 1 {
|
||||
page = 1
|
||||
|
@ -136,11 +142,12 @@ func (h *handler) apiGetBookmarks(w http.ResponseWriter, r *http.Request, ps htt
|
|||
|
||||
// Prepare filter for database
|
||||
searchOptions := database.GetBookmarksOptions{
|
||||
Tags: tags,
|
||||
Keyword: keyword,
|
||||
Limit: 30,
|
||||
Offset: (page - 1) * 30,
|
||||
OrderMethod: database.ByLastAdded,
|
||||
Tags: tags,
|
||||
ExcludedTags: excludedTags,
|
||||
Keyword: keyword,
|
||||
Limit: 30,
|
||||
Offset: (page - 1) * 30,
|
||||
OrderMethod: database.ByLastAdded,
|
||||
}
|
||||
|
||||
// Calculate max page
|
||||
|
|
Loading…
Reference in a new issue