Compare commits
6 Commits
a3cfb7ade1
...
15e50c7bab
Author | SHA1 | Date | |
---|---|---|---|
15e50c7bab | |||
d4e35c0279 | |||
0e99fee643 | |||
b1178a9df2 | |||
cd400b2d71 | |||
f324da0369 |
@ -1,6 +1,7 @@
|
|||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
#include "commandtag.h"
|
#include "commandtag.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "tagmanager.h"
|
||||||
|
|
||||||
int CommandTag::handle(QStringList arguments)
|
int CommandTag::handle(QStringList arguments)
|
||||||
{
|
{
|
||||||
@ -52,7 +53,8 @@ int CommandTag::handle(QStringList arguments)
|
|||||||
paths[i] = absolutePath;
|
paths[i] = absolutePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool result = this->dbService->addTag(tag, paths);
|
TagManager tagManager{*this->dbService};
|
||||||
|
bool result = tagManager.addPathsToTag(tag, paths);
|
||||||
if(!result)
|
if(!result)
|
||||||
{
|
{
|
||||||
Logger::error() << "Failed to assign tags" << Qt::endl;
|
Logger::error() << "Failed to assign tags" << Qt::endl;
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
|
#include <QWidgetAction>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include "clicklabel.h"
|
#include "clicklabel.h"
|
||||||
@ -42,6 +45,9 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
|
|||||||
|
|
||||||
indexer = new Indexer(*(this->dbService));
|
indexer = new Indexer(*(this->dbService));
|
||||||
indexer->setParent(this);
|
indexer->setParent(this);
|
||||||
|
|
||||||
|
tagManager = new TagManager(*(this->dbService));
|
||||||
|
|
||||||
connectSignals();
|
connectSignals();
|
||||||
ui->treeResultsList->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
ui->treeResultsList->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||||
ui->tabWidget->setCurrentIndex(0);
|
ui->tabWidget->setCurrentIndex(0);
|
||||||
@ -1008,6 +1014,65 @@ void MainWindow::createSearchResultMenu(QMenu &menu, const QFileInfo &fileInfo)
|
|||||||
this->ui->comboPreviewFiles->setCurrentText(fileInfo.absoluteFilePath());
|
this->ui->comboPreviewFiles->setCurrentText(fileInfo.absoluteFilePath());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMenu *tagMenu = menu.addMenu("Tag file with: ");
|
||||||
|
QVector<QString> allTags = this->dbService->getTags();
|
||||||
|
QHash<QString, bool> fileTags;
|
||||||
|
|
||||||
|
QString path = fileInfo.absoluteFilePath();
|
||||||
|
|
||||||
|
for(const QString &fileTag : this->dbService->getTagsForPath(path))
|
||||||
|
{
|
||||||
|
fileTags[fileTag] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const QString &tag : allTags)
|
||||||
|
{
|
||||||
|
QCheckBox *checkBox = new QCheckBox(tagMenu);
|
||||||
|
QWidgetAction *checkableAction = new QWidgetAction(tagMenu);
|
||||||
|
checkableAction->setDefaultWidget(checkBox);
|
||||||
|
checkBox->setText(tag);
|
||||||
|
checkBox->setChecked(fileTags.contains(tag));
|
||||||
|
tagMenu->addAction(checkableAction);
|
||||||
|
|
||||||
|
connect(checkBox, &QCheckBox::stateChanged, this,
|
||||||
|
[this, checkBox, path]
|
||||||
|
{
|
||||||
|
QVector<QString> currentTags = this->dbService->getTagsForPath(path);
|
||||||
|
QString checkBoxText = checkBox->text();
|
||||||
|
if(checkBox->isChecked())
|
||||||
|
{
|
||||||
|
if(!this->tagManager->addTagsToPath(path, {checkBoxText}))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "Error while adding tag",
|
||||||
|
"An error occured while trying to add the tag");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!this->tagManager->removeTagsForPath(path, {checkBoxText}))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "Error while removing tag",
|
||||||
|
"An error occured while trying to remove the tag");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tagMenu->addAction("Add new tags", this,
|
||||||
|
[this, path]
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
QString text =
|
||||||
|
QInputDialog::getText(this, tr("Enter new tags"), tr("New tags (comma separated):"),
|
||||||
|
QLineEdit::Normal, "", &ok);
|
||||||
|
|
||||||
|
if(ok && !this->tagManager->addTagsToPath(path, text, ','))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "Error while trying to add tags",
|
||||||
|
"An error occured while trying to add tags");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::openDocument(QString path, int num)
|
void MainWindow::openDocument(QString path, int num)
|
||||||
@ -1062,6 +1127,7 @@ MainWindow::~MainWindow()
|
|||||||
delete this->dbService;
|
delete this->dbService;
|
||||||
delete this->dbFactory;
|
delete this->dbFactory;
|
||||||
delete this->indexer;
|
delete this->indexer;
|
||||||
|
delete this->tagManager;
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "../shared/indexsyncer.h"
|
#include "../shared/indexsyncer.h"
|
||||||
#include "previewcoordinator.h"
|
#include "previewcoordinator.h"
|
||||||
#include "indexer.h"
|
#include "indexer.h"
|
||||||
|
#include "tagmanager.h"
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
@ -39,6 +40,9 @@ class MainWindow : public QMainWindow
|
|||||||
QFutureWatcher<QVector<SearchResult>> searchWatcher;
|
QFutureWatcher<QVector<SearchResult>> searchWatcher;
|
||||||
LooqsQuery contentSearchQuery;
|
LooqsQuery contentSearchQuery;
|
||||||
QVector<QString> searchHistory;
|
QVector<QString> searchHistory;
|
||||||
|
|
||||||
|
TagManager *tagManager;
|
||||||
|
|
||||||
int currentSearchHistoryIndex = 0;
|
int currentSearchHistoryIndex = 0;
|
||||||
QString currentSavedSearchText;
|
QString currentSavedSearchText;
|
||||||
bool previewDirty = false;
|
bool previewDirty = false;
|
||||||
|
@ -285,6 +285,10 @@ LooqsQuery LooqsQuery::build(QString expression, TokenType loneWordsTokenType, b
|
|||||||
{
|
{
|
||||||
tokenType = FILTER_CONTENT_PAGE;
|
tokenType = FILTER_CONTENT_PAGE;
|
||||||
}
|
}
|
||||||
|
else if(filtername == "t" || filtername == "tag")
|
||||||
|
{
|
||||||
|
tokenType = FILTER_TAG_ASSIGNED;
|
||||||
|
}
|
||||||
// TODO: given this is not really a "filter", this feels slightly misplaced here
|
// TODO: given this is not really a "filter", this feels slightly misplaced here
|
||||||
else if(filtername == "sort")
|
else if(filtername == "sort")
|
||||||
{
|
{
|
||||||
|
@ -60,6 +60,7 @@ SOURCES += sqlitesearch.cpp \
|
|||||||
processor.cpp \
|
processor.cpp \
|
||||||
sandboxedprocessor.cpp \
|
sandboxedprocessor.cpp \
|
||||||
sqlitedbservice.cpp \
|
sqlitedbservice.cpp \
|
||||||
|
tagmanager.cpp \
|
||||||
tagstripperprocessor.cpp \
|
tagstripperprocessor.cpp \
|
||||||
utils.cpp \
|
utils.cpp \
|
||||||
../submodules/exile.h/exile.c \
|
../submodules/exile.h/exile.c \
|
||||||
@ -93,6 +94,7 @@ HEADERS += sqlitesearch.h \
|
|||||||
savefileresult.h \
|
savefileresult.h \
|
||||||
searchresult.h \
|
searchresult.h \
|
||||||
sqlitedbservice.h \
|
sqlitedbservice.h \
|
||||||
|
tagmanager.h \
|
||||||
tagstripperprocessor.h \
|
tagstripperprocessor.h \
|
||||||
token.h \
|
token.h \
|
||||||
common.h \
|
common.h \
|
||||||
|
@ -104,6 +104,91 @@ unsigned int SqliteDbService::getFiles(QVector<FileData> &results, QString wildC
|
|||||||
return processedRows;
|
return processedRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QString> SqliteDbService::getTags()
|
||||||
|
{
|
||||||
|
QVector<QString> result;
|
||||||
|
auto query = QSqlQuery(dbFactory->forCurrentThread());
|
||||||
|
query.prepare("SELECT name FROM tag ORDER by name ASC");
|
||||||
|
query.setForwardOnly(true);
|
||||||
|
if(!query.exec())
|
||||||
|
{
|
||||||
|
throw LooqsGeneralException("Error while trying to retrieve tags from database: " + query.lastError().text());
|
||||||
|
}
|
||||||
|
while(query.next())
|
||||||
|
{
|
||||||
|
QString tagname = query.value(0).toString();
|
||||||
|
result.append(tagname);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QString> SqliteDbService::getTagsForPath(QString path)
|
||||||
|
{
|
||||||
|
QVector<QString> result;
|
||||||
|
auto query = QSqlQuery(dbFactory->forCurrentThread());
|
||||||
|
query.prepare("SELECT name FROM tag INNER JOIN filetag ON tag.id = filetag.tagid INNER JOIN file ON filetag.fileid "
|
||||||
|
"= file.id WHERE file.path = ? ORDER BY name ASC");
|
||||||
|
query.addBindValue(path);
|
||||||
|
query.setForwardOnly(true);
|
||||||
|
if(!query.exec())
|
||||||
|
{
|
||||||
|
throw LooqsGeneralException("Error while trying to retrieve tags from database: " + query.lastError().text());
|
||||||
|
}
|
||||||
|
while(query.next())
|
||||||
|
{
|
||||||
|
QString tagname = query.value(0).toString();
|
||||||
|
result.append(tagname);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqliteDbService::setTags(QString path, const QSet<QString> &tags)
|
||||||
|
{
|
||||||
|
QSqlDatabase db = dbFactory->forCurrentThread();
|
||||||
|
if(!db.transaction())
|
||||||
|
{
|
||||||
|
Logger::error() << "Failed to open transaction for " << path << " : " << db.lastError() << Qt::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery deletionQuery = QSqlQuery(db);
|
||||||
|
deletionQuery.prepare("DELETE FROM filetag WHERE fileid = (SELECT id FROM file WHERE path = ?)");
|
||||||
|
deletionQuery.addBindValue(path);
|
||||||
|
if(!deletionQuery.exec())
|
||||||
|
{
|
||||||
|
db.rollback();
|
||||||
|
Logger::error() << "Failed to delete existing tags " << deletionQuery.lastError() << Qt::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const QString &tag : tags)
|
||||||
|
{
|
||||||
|
QSqlQuery tagQuery = QSqlQuery(db);
|
||||||
|
tagQuery.prepare("INSERT OR IGNORE INTO tag (name) VALUES(?)");
|
||||||
|
tagQuery.addBindValue(tag.toLower());
|
||||||
|
if(!tagQuery.exec())
|
||||||
|
{
|
||||||
|
db.rollback();
|
||||||
|
Logger::error() << "Failed to insert tag " << tagQuery.lastError() << Qt::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QSqlQuery fileTagQuery(db);
|
||||||
|
fileTagQuery.prepare(
|
||||||
|
"INSERT INTO filetag(fileid, tagid) VALUES((SELECT id FROM file WHERE path = ?), (SELECT id "
|
||||||
|
"FROM tag WHERE name = ?))");
|
||||||
|
fileTagQuery.bindValue(0, path);
|
||||||
|
fileTagQuery.bindValue(1, tag);
|
||||||
|
if(!fileTagQuery.exec())
|
||||||
|
{
|
||||||
|
db.rollback();
|
||||||
|
Logger::error() << "Failed to assign tag to file" << Qt::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SqliteDbService::insertToFTS(bool useTrigrams, QSqlDatabase &db, int fileid, QVector<PageData> &pageData)
|
bool SqliteDbService::insertToFTS(bool useTrigrams, QSqlDatabase &db, int fileid, QVector<PageData> &pageData)
|
||||||
{
|
{
|
||||||
QString ftsInsertStatement;
|
QString ftsInsertStatement;
|
||||||
@ -248,6 +333,8 @@ bool SqliteDbService::addTag(QString tag, const QVector<QString> &paths)
|
|||||||
QSqlQuery tagQuery(db);
|
QSqlQuery tagQuery(db);
|
||||||
QSqlQuery fileTagQuery(db);
|
QSqlQuery fileTagQuery(db);
|
||||||
|
|
||||||
|
tag = tag.toLower();
|
||||||
|
|
||||||
tagQuery.prepare("INSERT OR IGNORE INTO tag (name) VALUES(?)");
|
tagQuery.prepare("INSERT OR IGNORE INTO tag (name) VALUES(?)");
|
||||||
tagQuery.addBindValue(tag);
|
tagQuery.addBindValue(tag);
|
||||||
|
|
||||||
|
@ -23,13 +23,19 @@ class SqliteDbService
|
|||||||
public:
|
public:
|
||||||
SqliteDbService(DatabaseFactory &dbFactory);
|
SqliteDbService(DatabaseFactory &dbFactory);
|
||||||
SaveFileResult saveFile(QFileInfo fileInfo, QVector<PageData> &pageData, bool pathsOnly);
|
SaveFileResult saveFile(QFileInfo fileInfo, QVector<PageData> &pageData, bool pathsOnly);
|
||||||
unsigned int getFiles(QVector<FileData> &results, QString wildCardPattern, int offset, int limit);
|
|
||||||
bool deleteFile(QString path);
|
bool deleteFile(QString path);
|
||||||
bool fileExistsInDatabase(QString path);
|
bool fileExistsInDatabase(QString path);
|
||||||
bool fileExistsInDatabase(QString path, qint64 mtime);
|
bool fileExistsInDatabase(QString path, qint64 mtime);
|
||||||
bool fileExistsInDatabase(QString path, qint64 mtime, QChar filetype);
|
bool fileExistsInDatabase(QString path, qint64 mtime, QChar filetype);
|
||||||
|
unsigned int getFiles(QVector<FileData> &results, QString wildCardPattern, int offset, int limit);
|
||||||
|
|
||||||
bool addTag(QString tag, QString path);
|
bool addTag(QString tag, QString path);
|
||||||
bool addTag(QString tag, const QVector<QString> &paths);
|
bool addTag(QString tag, const QVector<QString> &paths);
|
||||||
|
QVector<QString> getTags();
|
||||||
|
QVector<QString> getTagsForPath(QString path);
|
||||||
|
bool setTags(QString path, const QSet<QString> &tags);
|
||||||
|
|
||||||
QVector<SearchResult> search(const LooqsQuery &query);
|
QVector<SearchResult> search(const LooqsQuery &query);
|
||||||
|
|
||||||
std::optional<QChar> queryFileType(QString absPath);
|
std::optional<QChar> queryFileType(QString absPath);
|
||||||
|
@ -143,6 +143,11 @@ QPair<QString, QVector<QString>> SqliteSearch::createSql(const Token &token)
|
|||||||
{
|
{
|
||||||
return {" fts MATCH ? ", {escapeFtsArgument(value)}};
|
return {" fts MATCH ? ", {escapeFtsArgument(value)}};
|
||||||
}
|
}
|
||||||
|
if(token.type == FILTER_TAG_ASSIGNED)
|
||||||
|
{
|
||||||
|
return {" file.id IN (SELECT fileid FROM filetag WHERE tagid = (SELECT id FROM tag WHERE name = ?)) ",
|
||||||
|
{value.toLower()}};
|
||||||
|
}
|
||||||
throw LooqsGeneralException("Unknown token passed (should not happen)");
|
throw LooqsGeneralException("Unknown token passed (should not happen)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,18 +184,18 @@ QSqlQuery SqliteSearch::makeSqlQuery(const LooqsQuery &query)
|
|||||||
}
|
}
|
||||||
QString whereSqlTrigram = whereSql;
|
QString whereSqlTrigram = whereSql;
|
||||||
whereSqlTrigram.replace("fts MATCH", "fts_trigram MATCH"); // A bit dirty...
|
whereSqlTrigram.replace("fts MATCH", "fts_trigram MATCH"); // A bit dirty...
|
||||||
prepSql =
|
prepSql = "SELECT DISTINCT path, page, mtime, size, filetype FROM ("
|
||||||
"SELECT DISTINCT path, page, mtime, size, filetype FROM ("
|
"SELECT file.path AS path, content.page AS page, file.mtime AS mtime, file.size AS size, "
|
||||||
"SELECT file.path AS path, content.page AS page, file.mtime AS mtime, file.size AS size, "
|
"file.filetype AS filetype, 0 AS prio, fts.rank AS rank FROM file INNER JOIN content ON file.id = "
|
||||||
"file.filetype AS filetype, 0 AS prio, fts.rank AS rank FROM file INNER JOIN content ON file.id = "
|
"content.fileid "
|
||||||
"content.fileid "
|
"INNER JOIN fts ON content.ftsid = fts.ROWID WHERE 1=1 AND " +
|
||||||
"INNER JOIN fts ON content.ftsid = fts.ROWID WHERE 1=1 AND " +
|
whereSql +
|
||||||
whereSql +
|
"UNION ALL SELECT file.path AS path, content.page AS page, file.mtime AS mtime, file.size AS size, "
|
||||||
"UNION ALL SELECT file.path AS path, content.page AS page, file.mtime AS mtime, file.size AS size, "
|
"file.filetype AS filetype, 1 as prio, fts_trigram.rank AS rank FROM file INNER JOIN content ON "
|
||||||
"file.filetype AS filetype, 1 as prio, fts_trigram.rank AS rank FROM file INNER JOIN content ON file.id = "
|
"file.id = "
|
||||||
"content.fileid " +
|
"content.fileid " +
|
||||||
"INNER JOIN fts_trigram ON content.fts_trigramid = fts_trigram.ROWID WHERE 1=1 AND " + whereSqlTrigram +
|
"INNER JOIN fts_trigram ON content.fts_trigramid = fts_trigram.ROWID WHERE 1=1 AND " +
|
||||||
" ) " + sortSql;
|
whereSqlTrigram + " ) " + sortSql;
|
||||||
++bindIterations;
|
++bindIterations;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
41
shared/tagmanager.cpp
Normal file
41
shared/tagmanager.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "tagmanager.h"
|
||||||
|
|
||||||
|
TagManager::TagManager(SqliteDbService &dbService)
|
||||||
|
{
|
||||||
|
this->dbService = &dbService;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TagManager::addTagsToPath(QString path, const QSet<QString> &tags)
|
||||||
|
{
|
||||||
|
QVector<QString> currentTags = this->dbService->getTagsForPath(path);
|
||||||
|
for(const QString &tag : tags)
|
||||||
|
{
|
||||||
|
currentTags.append(tag.toLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> newTags{currentTags.begin(), currentTags.end()};
|
||||||
|
return this->dbService->setTags(path, newTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TagManager::removeTagsForPath(QString path, const QSet<QString> &tags)
|
||||||
|
{
|
||||||
|
QVector<QString> currentTags = this->dbService->getTagsForPath(path);
|
||||||
|
for(const QString &tag : tags)
|
||||||
|
{
|
||||||
|
currentTags.removeAll(tag);
|
||||||
|
}
|
||||||
|
QSet<QString> newTags{currentTags.begin(), currentTags.end()};
|
||||||
|
return this->dbService->setTags(path, newTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TagManager::addTagsToPath(QString path, QString tagstring, QChar delim)
|
||||||
|
{
|
||||||
|
auto splitted = tagstring.split(delim);
|
||||||
|
|
||||||
|
return addTagsToPath(path, QSet<QString>{splitted.begin(), splitted.end()});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TagManager::addPathsToTag(QString tag, const QVector<QString> &paths)
|
||||||
|
{
|
||||||
|
return this->dbService->addTag(tag, paths);
|
||||||
|
}
|
26
shared/tagmanager.h
Normal file
26
shared/tagmanager.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef TAGMANAGER_H
|
||||||
|
#define TAGMANAGER_H
|
||||||
|
#include "sqlitedbservice.h"
|
||||||
|
|
||||||
|
class TagManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SqliteDbService *dbService = nullptr;
|
||||||
|
bool ensurePathOkay(QString inpath);
|
||||||
|
|
||||||
|
public:
|
||||||
|
TagManager(SqliteDbService &dbService);
|
||||||
|
|
||||||
|
bool addTagsToPath(QString path, const QSet<QString> &tags);
|
||||||
|
bool addTagsToPath(QString path, QString tagstring, QChar delim);
|
||||||
|
|
||||||
|
bool addPathsToTag(QString tag, const QVector<QString> &paths);
|
||||||
|
bool removeTagsForPath(QString path, const QSet<QString> &tags);
|
||||||
|
|
||||||
|
bool deleteTag(QString tag);
|
||||||
|
|
||||||
|
QVector<QString> getTags(QString path);
|
||||||
|
QVector<QString> getPaths(QString tag);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TAGMANAGER_H
|
@ -22,6 +22,7 @@ enum TokenType
|
|||||||
FILTER_CONTENT = 512,
|
FILTER_CONTENT = 512,
|
||||||
FILTER_CONTENT_CONTAINS,
|
FILTER_CONTENT_CONTAINS,
|
||||||
FILTER_CONTENT_PAGE,
|
FILTER_CONTENT_PAGE,
|
||||||
|
FILTER_TAG_ASSIGNED,
|
||||||
LIMIT = 1024
|
LIMIT = 1024
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user