kio_recoll: html/dir switching

This commit is contained in:
dockes 2008-12-04 11:49:59 +00:00
parent 0b649ae8d6
commit 3e311495be
10 changed files with 120 additions and 68 deletions

View File

@ -5,60 +5,86 @@
</head> </head>
<body> <body>
<a href="recoll:///search.html">Recoll search</a> <a href="recoll:///search.html">Recoll search</a>
<h3>Recoll kio slave</h3> <h2>Recoll kio slave</h2>
<p>Use this module to perform Recoll searches from any program with <p>Use this module to perform Recoll searches from any program with
a KIO interface (sort of...). <b>kio_recoll</b> is primarily a KIO interface. <b>kio_recoll</b> is primarily
designed and tested with <b>konqueror</b>, and you will designed and tested with <b>konqueror</b>, and you will
undoubtedly get even more surprising effects with other tools.</p> undoubtedly get even more surprising effects with other tools.</p>
<p>The module can work in two different modes, depending on the form <p>The module can work in two modes:</p>
of the URLS that it is given:</p> <ul>
<li><b>Html interface</b>: this is what you get into when you just type <li><b>Html interface</b>, close to a simplified QT Recoll
recoll: or recoll:/ in the address bar, and then click the initial interface.</li>
icon.</li> <li><b>File manager interface</b>, which presents results as
<li><b>File/Directory interface</b>: which you enter when you pass directory entries</li>
an URL ending with a '/'</li> </ul>
<p>Please note that this module is still in its infancy and that it <p>The module is still in its infancy. You will undoubtedly obtain
is still more a toy than anything else. The semantics of the strange effects from time to time. If you have any remarks or
KIO slaves interface is still a bit unstable between KDE releases, ideas about improving kio_recoll, or observe an interesting and
you will certainly get surprising effects from time to time.</p> reproducible sequence, please <a href="mailto:jfd@recoll.org">
report it</a>.</p>
<h4>HTML interface</h4> <p>The module is particularly unhelpful with search hits inside
email folders, which Konqueror has no way to access.</p>
<p>This works more or less like the Recoll QT GUI, much
simplified. The <h3>HTML interface</h3>
<p>This works more or less like the Recoll QT GUI, much simplified. The
<a href="http://www.recoll.org/usermanual/rcl.search.lang.html"> <a href="http://www.recoll.org/usermanual/rcl.search.lang.html">
Recoll manual</a> describes the queries that can be Recoll manual</a> describes the queries that can be performed.</p>
performed.</p>
<p>Most pages in the interface should quite self-explanatory.</p> <p>Most pages in the interface should quite self-explanatory.</p>
<h4>File interface</h4> <p>You normally enter this interface by entering "recoll:" or
"recoll:/" in the Konqueror URL entry, and following the "search"
link.<br> In most circumstances, entering a link like
"recoll:/some search terms" will also yield an HTML result
list.</p>
<p>kio_recoll enters this mode when it receives an URL ending with
a&nbsp;'/', ie:</p> <h3>File manager interface</h3>
<blockquote><i>recoll:///xapian recoll ext:.html/</i></blockquote>
<p>Search results are returned as directory entries. No search
result details (samples, relevance etc.) are available, but this
interface allows multiple selections and copies, usage inside any
KDE open dialog, etc.</p>
<p>The <i>path</i> part of the URI is taken as a Recoll query <p>The <i>path</i> part of the URI is taken as a Recoll query
language string and executed. The results are displayed as language string and executed. The results are displayed as
directory entries.</p> directory entries.</p>
<p>This works fine with normal documents, very badly with message <p>To avoid swamping the interface with thousands of results, the
inside folders, which Konqueror has no way to access.</p> result count is limited to 100 by default. You can change this value
by setting the <code>kio_max_direntries</code> parameter in your recoll
configuration file (typically ~/.recoll/recoll.conf)</p>
<p>As there is no provision to page directory listings, the number <p>There are several ways to enter this interface:</p>
of results returned is limited to a fixed value, 100 by default, <ul>
which you can change by setting the kio_max_direntries in your <li>Using "recollf" as protocol name instead of "recoll". This is
Recoll configuration file (usually ~/.recoll). probably the easiest option inside open dialogs.</li>
<a href="http://www.recoll.org/usermanual/rcl.install.config.html">
More information about Recoll configuration.</a></p>
<p>This interface is very limited, but allows performing multiple <li>Using an URL ending with a&nbsp;'/', ie:
selection, copies, and other file operations on the results, which <blockquote><i>recoll:/red apples ext:.html/</i></blockquote></li>
may be useful in some cases (or not :))</p>
<p><a href="recoll:///welcome">Recoll Search</a></p> <li>Switching from the HTML interface in konqueror by clicking the
"Directory&nbsp;view" link at the top of the page. When doing
this, the directory content will be synchronized with the
current HTML result page. This offers a way to display results
in the file manager beyond the <code>kio_max_direntries</code>
limit. <em>In most cases, you will have to reload the file
manager page to obtain this synchronization</em>.</li>
<li>Users who will want to use the file manager view most of the
time can set the <code>RECOLL_KIO_ALWAYS_DIR</code> environment
variable or the <code>kio_always_dir</code> recoll.conf variable
to&nbsp;1. The HTML interface will then only be accessible
through the search link in the top "recoll:" view.</li>
</ul>
<p><a href="recoll:///search.html">Recoll Search</a></p>
</body> </body>
</html> </html>

View File

@ -1,5 +1,5 @@
#ifndef lint #ifndef lint
static char rcsid[] = "@(#$Id: dirif.cpp,v 1.9 2008-12-03 17:04:20 dockes Exp $ (C) 2008 J.F.Dockes"; static char rcsid[] = "@(#$Id: dirif.cpp,v 1.10 2008-12-04 11:49:58 dockes Exp $ (C) 2008 J.F.Dockes";
#endif #endif
/* /*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -141,7 +141,7 @@ static void createRootEntry(KIO::UDSEntry& entry)
static void createGoHomeEntry(KIO::UDSEntry& entry) static void createGoHomeEntry(KIO::UDSEntry& entry)
{ {
entry.clear(); entry.clear();
entry.insert(KIO::UDSEntry::UDS_NAME, "search"); entry.insert(KIO::UDSEntry::UDS_NAME, "search.html");
entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, "Recoll search (click me)"); entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, "Recoll search (click me)");
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
entry.insert(KIO::UDSEntry::UDS_TARGET_URL, "recoll:///search.html"); entry.insert(KIO::UDSEntry::UDS_TARGET_URL, "recoll:///search.html");
@ -173,6 +173,7 @@ void RecollProtocol::stat(const KUrl & url)
UrlIngester ingest(this, url); UrlIngester ingest(this, url);
KIO::UDSEntry entry; KIO::UDSEntry entry;
entry.insert(KIO::UDSEntry::UDS_TARGET_URL, url.url());
UrlIngester::RootEntryType rettp; UrlIngester::RootEntryType rettp;
QueryDesc qd; QueryDesc qd;
int num; int num;
@ -194,7 +195,8 @@ void RecollProtocol::stat(const KUrl & url)
} else if (ingest.isResult(&qd, &num)) { } else if (ingest.isResult(&qd, &num)) {
if (syncSearch(qd)) { if (syncSearch(qd)) {
Rcl::Doc doc; Rcl::Doc doc;
if (num >= 0 && !m_source.isNull() && m_source->getDoc(num, doc)) { if (num >= 0 && !m_source.isNull() &&
m_source->getDoc(num, doc)) {
entry = resultToUDSEntry(doc, num); entry = resultToUDSEntry(doc, num);
} else { } else {
error(ERR_DOES_NOT_EXIST, ""); error(ERR_DOES_NOT_EXIST, "");
@ -214,14 +216,15 @@ void RecollProtocol::stat(const KUrl & url)
// Another approach would be to use different protocol names // Another approach would be to use different protocol names
// to avoid any possibility of mixups // to avoid any possibility of mixups
if (m_alwaysdir || ingest.alwaysDir() || ingest.endSlashQuery()) { if (m_alwaysdir || ingest.alwaysDir() || ingest.endSlashQuery()) {
kDebug() << "Directory type";
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700); entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700);
entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory"); entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory");
entry.insert(KIO::UDSEntry::UDS_NAME, qd.query); entry.insert(KIO::UDSEntry::UDS_NAME, qd.query);
entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, time(0));
entry.insert( KIO::UDSEntry::UDS_CREATION_TIME, time(0));
} }
} }
entry.insert(KIO::UDSEntry::UDS_TARGET_URL, url.url());
statEntry(entry); statEntry(entry);
finished(); finished();
} }
@ -253,7 +256,6 @@ void RecollProtocol::listDir(const KUrl& url)
return; return;
default: default:
error(ERR_CANNOT_ENTER_DIRECTORY, ""); error(ERR_CANNOT_ENTER_DIRECTORY, "");
finished();
return; return;
} }
} else if (ingest.isQuery(&qd)) { } else if (ingest.isQuery(&qd)) {
@ -266,6 +268,7 @@ void RecollProtocol::listDir(const KUrl& url)
return; return;
} }
if (!syncSearch(qd)) { if (!syncSearch(qd)) {
// syncSearch did the error thing
return; return;
} }
// Fallthrough to actually listing the directory // Fallthrough to actually listing the directory
@ -283,8 +286,18 @@ void RecollProtocol::listDir(const KUrl& url)
numentries = 100; numentries = 100;
} }
// If the html pager is set, begin display at the current page. This
// allows paging the dir display by switching between both modes
int first = 0;
if (m_pager.pageNumber() > 0) {
first = m_pager.pageNumber() * m_pager.pageSize();
}
vector<ResListEntry> page; vector<ResListEntry> page;
int pagelen = m_source->getSeqSlice(0, numentries, page); int pagelen = m_source->getSeqSlice(first, numentries, page);
if (pagelen < 0) {
error(ERR_SLAVE_DEFINED, "Internal error");
return;
}
UDSEntryList entries; UDSEntryList entries;
for (int i = 0; i < pagelen; i++) { for (int i = 0; i < pagelen; i++) {
entries.append(resultToUDSEntry(page[i].doc, i)); entries.append(resultToUDSEntry(page[i].doc, i));

View File

@ -1,5 +1,5 @@
#ifndef lint #ifndef lint
static char rcsid[] = "@(#$Id: htmlif.cpp,v 1.6 2008-12-03 17:04:20 dockes Exp $ (C) 2005 J.F.Dockes"; static char rcsid[] = "@(#$Id: htmlif.cpp,v 1.7 2008-12-04 11:49:59 dockes Exp $ (C) 2005 J.F.Dockes";
#endif #endif
/* /*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -57,7 +57,7 @@ string RecollProtocol::makeQueryUrl(int page, bool isdet)
{ {
char buf[100]; char buf[100];
sprintf(buf, "recoll://search/query?q=%s&qtp=%s&p=%d", sprintf(buf, "recoll://search/query?q=%s&qtp=%s&p=%d",
(const char*)m_query.query.toUtf8(), url_encode((const char*)m_query.query.toUtf8()).c_str(),
(const char*)m_query.opt.toUtf8(), (const char*)m_query.opt.toUtf8(),
page); page);
string ret(buf); string ret(buf);
@ -87,7 +87,14 @@ const string& RecollKioPager::parFormat()
string RecollKioPager::pageTop() string RecollKioPager::pageTop()
{ {
return "<p align=\"center\"><a href=\"recoll:///search.html\">New Search</a></p>"; return "<p align=\"center\">"
"<a href=\"recoll:///search.html\">New Search</a>"
#if KDE_IS_VERSION(4,1,0)
" &nbsp;&nbsp;&nbsp;<a href=\"recoll:///" +
url_encode(string(m_parent->m_query.query.toUtf8())) +
"/\">Directory view</a> (you may need to reload the page)"
#endif
"</p>";
} }
string RecollKioPager::nextUrl() string RecollKioPager::nextUrl()
@ -183,11 +190,10 @@ void RecollProtocol::htmlDoSearch(const QueryDesc& qd)
mimeType("text/html"); mimeType("text/html");
bool samesearch; if (!syncSearch(qd))
if (!syncSearch(qd, &samesearch))
return; return;
if (!samesearch) { // syncSearch/doSearch do the setDocSource when needed
m_pager.setDocSource(m_source); if (m_pager.pageNumber() < 0) {
m_pager.resultPageNext(); m_pager.resultPageNext();
} }
if (qd.isDetReq) { if (qd.isDetReq) {

View File

@ -1,5 +1,5 @@
#ifndef lint #ifndef lint
static char rcsid[] = "@(#$Id: kio_recoll.cpp,v 1.21 2008-12-03 17:04:20 dockes Exp $ (C) 2005 J.F.Dockes"; static char rcsid[] = "@(#$Id: kio_recoll.cpp,v 1.22 2008-12-04 11:49:59 dockes Exp $ (C) 2005 J.F.Dockes";
#endif #endif
/* /*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -164,7 +164,8 @@ UrlIngester::UrlIngester(RecollProtocol *p, const KUrl& url)
m_query.page = 0; m_query.page = 0;
} }
} else { } else {
if (url.host().compare("search")) { kDebug() << "host" << url.host() << "path" << url.path();
if (url.host().compare("search") || url.path().compare("/query")) {
return; return;
} }
m_type = UIMT_QUERY; m_type = UIMT_QUERY;
@ -187,6 +188,7 @@ UrlIngester::UrlIngester(RecollProtocol *p, const KUrl& url)
if (m_query.query.startsWith("/")) if (m_query.query.startsWith("/"))
m_query.query.remove(0,1); m_query.query.remove(0,1);
if (m_query.query.endsWith("/")) { if (m_query.query.endsWith("/")) {
kDebug() << "Ends with /";
m_slashend = true; m_slashend = true;
m_query.query.chop(1); m_query.query.chop(1);
} else { } else {
@ -195,7 +197,7 @@ UrlIngester::UrlIngester(RecollProtocol *p, const KUrl& url)
return; return;
} }
bool RecollProtocol::syncSearch(const QueryDesc &qd, bool *same) bool RecollProtocol::syncSearch(const QueryDesc &qd)
{ {
kDebug(); kDebug();
if (!m_initok || !maybeOpenDb(m_reason)) { if (!m_initok || !maybeOpenDb(m_reason)) {
@ -204,12 +206,8 @@ bool RecollProtocol::syncSearch(const QueryDesc &qd, bool *same)
return false; return false;
} }
if (qd.sameQuery(m_query)) { if (qd.sameQuery(m_query)) {
if (same)
*same = true;
return true; return true;
} }
if (same)
*same = false;
// doSearch() calls error() if appropriate. // doSearch() calls error() if appropriate.
return doSearch(qd); return doSearch(qd);
} }
@ -331,6 +329,9 @@ bool RecollProtocol::doSearch(const QueryDesc& qd)
return false; return false;
} }
m_source = RefCntr<DocSequence>(src); m_source = RefCntr<DocSequence>(src);
// Reset pager in all cases. Costs nothing, stays at page -1 initially
// htmldosearch will fetch the first page if needed.
m_pager.setDocSource(m_source);
return true; return true;
} }

View File

@ -1,5 +1,5 @@
#ifndef _RECOLL_H #ifndef _RECOLL_H
/* @(#$Id: kio_recoll.h,v 1.12 2008-12-03 17:04:20 dockes Exp $ (C) 2005 J.F.Dockes */ /* @(#$Id: kio_recoll.h,v 1.13 2008-12-04 11:49:59 dockes Exp $ (C) 2005 J.F.Dockes */
#define _RECOLL_H #define _RECOLL_H
/* /*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -153,7 +153,7 @@ class RecollProtocol : public KIO::SlaveBase {
void searchPage(); void searchPage();
void queryDetails(); void queryDetails();
string makeQueryUrl(int page, bool isdet = false); string makeQueryUrl(int page, bool isdet = false);
bool syncSearch(const QueryDesc& qd, bool *same = 0); bool syncSearch(const QueryDesc& qd);
void htmlDoSearch(const QueryDesc& qd); void htmlDoSearch(const QueryDesc& qd);
bool isRecollResult(const KUrl &url, int *num, QString* q); bool isRecollResult(const KUrl &url, int *num, QString* q);

Binary file not shown.

View File

@ -1,5 +1,5 @@
#ifndef lint #ifndef lint
static char rcsid[] = "@(#$Id: reslistpager.cpp,v 1.4 2008-12-02 13:14:01 dockes Exp $ (C) 2007 J.F.Dockes"; static char rcsid[] = "@(#$Id: reslistpager.cpp,v 1.5 2008-12-04 11:49:59 dockes Exp $ (C) 2007 J.F.Dockes";
#endif #endif
#include <stdio.h> #include <stdio.h>
@ -30,7 +30,7 @@ Prefs prefs = {true, true};
void ResListPager::resultPageNext() void ResListPager::resultPageNext()
{ {
if (m_docSource.isNull()) { if (m_docSource.isNull()) {
LOGDEB(("ResListPager::displayPage: null source\n")); LOGDEB(("ResListPager::resultPageNext: null source\n"));
return; return;
} }
@ -75,6 +75,10 @@ void ResListPager::displayPage()
LOGDEB(("ResListPager::displayPage: null source\n")); LOGDEB(("ResListPager::displayPage: null source\n"));
return; return;
} }
if (m_winfirst < 0) {
LOGDEB(("ResListPager::displayPage: sequence error: winfirst < 0\n"));
return;
}
string chunk; string chunk;
// Display list header // Display list header

View File

@ -1,6 +1,6 @@
#ifndef _reslistpager_h_included_ #ifndef _reslistpager_h_included_
#define _reslistpager_h_included_ #define _reslistpager_h_included_
/* @(#$Id: reslistpager.h,v 1.2 2008-11-20 13:10:23 dockes Exp $ (C) 2007 J.F.Dockes */ /* @(#$Id: reslistpager.h,v 1.3 2008-12-04 11:49:59 dockes Exp $ (C) 2007 J.F.Dockes */
#include <vector> #include <vector>
using std::vector; using std::vector;
@ -32,6 +32,7 @@ public:
return -1; return -1;
return m_winfirst / m_pagesize; return m_winfirst / m_pagesize;
} }
virtual int pageSize() const {return m_pagesize;}
void pageNext(); void pageNext();
bool hasNext() {return m_hasNext;} bool hasNext() {return m_hasNext;}
bool hasPrev() {return m_winfirst > 0;} bool hasPrev() {return m_winfirst > 0;}
@ -60,12 +61,11 @@ public:
virtual string nextUrl(); virtual string nextUrl();
virtual string prevUrl(); virtual string prevUrl();
virtual string pageTop() {return string();} virtual string pageTop() {return string();}
private: private:
// First docnum (from docseq) in current page // First docnum (from docseq) in current page
int m_winfirst; int m_winfirst;
RefCntr<DocSequence> m_docSource; RefCntr<DocSequence> m_docSource;
int m_pagesize; const int m_pagesize;
bool m_hasNext; bool m_hasNext;
vector<ResListEntry> m_respage; vector<ResListEntry> m_respage;

View File

@ -1,5 +1,5 @@
#ifndef lint #ifndef lint
static char rcsid[] = "@(#$Id: rcldb.cpp,v 1.148 2008-10-07 06:44:23 dockes Exp $ (C) 2004 J.F.Dockes"; static char rcsid[] = "@(#$Id: rcldb.cpp,v 1.149 2008-12-04 11:49:59 dockes Exp $ (C) 2004 J.F.Dockes";
#endif #endif
/* /*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -333,8 +333,8 @@ string Db::Native::makeAbstract(Xapian::docid docid, Query *query)
unsigned int occurrences = 0; unsigned int occurrences = 0;
for (pos = db.positionlist_begin(docid, qterm); for (pos = db.positionlist_begin(docid, qterm);
pos != db.positionlist_end(docid, qterm); pos++) { pos != db.positionlist_end(docid, qterm); pos++) {
unsigned int ipos = *pos; int ipos = *pos;
if (ipos < baseTextPosition) // Not in text body if (ipos < int(baseTextPosition)) // Not in text body
continue; continue;
LOGABS(("makeAbstract: [%s] at %d occurrences %d maxoccs %d\n", LOGABS(("makeAbstract: [%s] at %d occurrences %d maxoccs %d\n",
qterm.c_str(), ipos, occurrences, maxoccs)); qterm.c_str(), ipos, occurrences, maxoccs));
@ -344,7 +344,7 @@ string Db::Native::makeAbstract(Xapian::docid docid, Query *query)
unsigned int sta = MAX(0, ipos-m_db->m_synthAbsWordCtxLen); unsigned int sta = MAX(0, ipos-m_db->m_synthAbsWordCtxLen);
unsigned int sto = ipos+m_db->m_synthAbsWordCtxLen; unsigned int sto = ipos+m_db->m_synthAbsWordCtxLen;
for (unsigned int ii = sta; ii <= sto; ii++) { for (unsigned int ii = sta; ii <= sto; ii++) {
if (ii == ipos) if (ii == (unsigned int)ipos)
sparseDoc[ii] = qterm; sparseDoc[ii] = qterm;
else else
sparseDoc[ii] = emptys; sparseDoc[ii] = emptys;
@ -1366,6 +1366,8 @@ bool Db::stemExpand(const string &lang, const string &term,
LOGDEB1(("Db::stemExpand: Got %d from %s\n", LOGDEB1(("Db::stemExpand: Got %d from %s\n",
more.size(), it->c_str())); more.size(), it->c_str()));
result.insert(result.end(), more.begin(), more.end()); result.insert(result.end(), more.begin(), more.end());
if (result.size() >= (unsigned int)max)
break;
} }
LOGDEB1(("Db:::stemExpand: final count %d \n", result.size())); LOGDEB1(("Db:::stemExpand: final count %d \n", result.size()));
return true; return true;

View File

@ -16,7 +16,7 @@
*/ */
#ifndef _PATHUT_H_INCLUDED_ #ifndef _PATHUT_H_INCLUDED_
#define _PATHUT_H_INCLUDED_ #define _PATHUT_H_INCLUDED_
/* @(#$Id: pathut.h,v 1.15 2008-07-01 11:51:51 dockes Exp $ (C) 2004 J.F.Dockes */ /* @(#$Id: pathut.h,v 1.16 2008-12-04 11:49:59 dockes Exp $ (C) 2004 J.F.Dockes */
#include <string> #include <string>
#include <list> #include <list>
@ -51,7 +51,7 @@ extern list<string> path_dirglob(const string &dir,
const string pattern); const string pattern);
/// Encode according to rfc 1738 /// Encode according to rfc 1738
extern string url_encode(const string url, extern string url_encode(const string url,
string::size_type offs); string::size_type offs = 0);
extern bool printableUrl(const string &fcharset, extern bool printableUrl(const string &fcharset,
const string &in, string &out); const string &in, string &out);