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>
<body>
<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
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
undoubtedly get even more surprising effects with other tools.</p>
<p>The module can work in two different modes, depending on the form
of the URLS that it is given:</p>
<li><b>Html interface</b>: this is what you get into when you just type
recoll: or recoll:/ in the address bar, and then click the initial
icon.</li>
<li><b>File/Directory interface</b>: which you enter when you pass
an URL ending with a '/'</li>
<p>The module can work in two modes:</p>
<ul>
<li><b>Html interface</b>, close to a simplified QT Recoll
interface.</li>
<li><b>File manager interface</b>, which presents results as
directory entries</li>
</ul>
<p>Please note that this module is still in its infancy and that it
is still more a toy than anything else. The semantics of the
KIO slaves interface is still a bit unstable between KDE releases,
you will certainly get surprising effects from time to time.</p>
<p>The module is still in its infancy. You will undoubtedly obtain
strange effects from time to time. If you have any remarks or
ideas about improving kio_recoll, or observe an interesting and
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">
Recoll manual</a> describes the queries that can be
performed.</p>
Recoll manual</a> describes the queries that can be performed.</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>
<blockquote><i>recoll:///xapian recoll ext:.html/</i></blockquote>
<h3>File manager interface</h3>
<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
language string and executed. The results are displayed as
directory entries.</p>
<p>This works fine with normal documents, very badly with message
inside folders, which Konqueror has no way to access.</p>
<p>To avoid swamping the interface with thousands of results, the
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
of results returned is limited to a fixed value, 100 by default,
which you can change by setting the kio_max_direntries in your
Recoll configuration file (usually ~/.recoll).
<a href="http://www.recoll.org/usermanual/rcl.install.config.html">
More information about Recoll configuration.</a></p>
<p>There are several ways to enter this interface:</p>
<ul>
<li>Using "recollf" as protocol name instead of "recoll". This is
probably the easiest option inside open dialogs.</li>
<p>This interface is very limited, but allows performing multiple
selection, copies, and other file operations on the results, which
may be useful in some cases (or not :))</p>
<li>Using an URL ending with a&nbsp;'/', ie:
<blockquote><i>recoll:/red apples ext:.html/</i></blockquote></li>
<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>
</html>

View File

@ -1,5 +1,5 @@
#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
/*
* 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)
{
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_FILE_TYPE, S_IFREG);
entry.insert(KIO::UDSEntry::UDS_TARGET_URL, "recoll:///search.html");
@ -173,6 +173,7 @@ void RecollProtocol::stat(const KUrl & url)
UrlIngester ingest(this, url);
KIO::UDSEntry entry;
entry.insert(KIO::UDSEntry::UDS_TARGET_URL, url.url());
UrlIngester::RootEntryType rettp;
QueryDesc qd;
int num;
@ -194,7 +195,8 @@ void RecollProtocol::stat(const KUrl & url)
} else if (ingest.isResult(&qd, &num)) {
if (syncSearch(qd)) {
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);
} else {
error(ERR_DOES_NOT_EXIST, "");
@ -214,14 +216,15 @@ void RecollProtocol::stat(const KUrl & url)
// Another approach would be to use different protocol names
// to avoid any possibility of mixups
if (m_alwaysdir || ingest.alwaysDir() || ingest.endSlashQuery()) {
kDebug() << "Directory type";
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700);
entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory");
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);
finished();
}
@ -253,7 +256,6 @@ void RecollProtocol::listDir(const KUrl& url)
return;
default:
error(ERR_CANNOT_ENTER_DIRECTORY, "");
finished();
return;
}
} else if (ingest.isQuery(&qd)) {
@ -266,6 +268,7 @@ void RecollProtocol::listDir(const KUrl& url)
return;
}
if (!syncSearch(qd)) {
// syncSearch did the error thing
return;
}
// Fallthrough to actually listing the directory
@ -283,8 +286,18 @@ void RecollProtocol::listDir(const KUrl& url)
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;
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;
for (int i = 0; i < pagelen; i++) {
entries.append(resultToUDSEntry(page[i].doc, i));

View File

@ -1,5 +1,5 @@
#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
/*
* 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];
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(),
page);
string ret(buf);
@ -87,7 +87,14 @@ const string& RecollKioPager::parFormat()
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()
@ -183,11 +190,10 @@ void RecollProtocol::htmlDoSearch(const QueryDesc& qd)
mimeType("text/html");
bool samesearch;
if (!syncSearch(qd, &samesearch))
if (!syncSearch(qd))
return;
if (!samesearch) {
m_pager.setDocSource(m_source);
// syncSearch/doSearch do the setDocSource when needed
if (m_pager.pageNumber() < 0) {
m_pager.resultPageNext();
}
if (qd.isDetReq) {

View File

@ -1,5 +1,5 @@
#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
/*
* 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;
}
} else {
if (url.host().compare("search")) {
kDebug() << "host" << url.host() << "path" << url.path();
if (url.host().compare("search") || url.path().compare("/query")) {
return;
}
m_type = UIMT_QUERY;
@ -187,6 +188,7 @@ UrlIngester::UrlIngester(RecollProtocol *p, const KUrl& url)
if (m_query.query.startsWith("/"))
m_query.query.remove(0,1);
if (m_query.query.endsWith("/")) {
kDebug() << "Ends with /";
m_slashend = true;
m_query.query.chop(1);
} else {
@ -195,7 +197,7 @@ UrlIngester::UrlIngester(RecollProtocol *p, const KUrl& url)
return;
}
bool RecollProtocol::syncSearch(const QueryDesc &qd, bool *same)
bool RecollProtocol::syncSearch(const QueryDesc &qd)
{
kDebug();
if (!m_initok || !maybeOpenDb(m_reason)) {
@ -204,12 +206,8 @@ bool RecollProtocol::syncSearch(const QueryDesc &qd, bool *same)
return false;
}
if (qd.sameQuery(m_query)) {
if (same)
*same = true;
return true;
}
if (same)
*same = false;
// doSearch() calls error() if appropriate.
return doSearch(qd);
}
@ -331,6 +329,9 @@ bool RecollProtocol::doSearch(const QueryDesc& qd)
return false;
}
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;
}

View File

@ -1,5 +1,5 @@
#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
/*
* This program is free software; you can redistribute it and/or modify
@ -153,7 +153,7 @@ class RecollProtocol : public KIO::SlaveBase {
void searchPage();
void queryDetails();
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);
bool isRecollResult(const KUrl &url, int *num, QString* q);

Binary file not shown.

View File

@ -1,5 +1,5 @@
#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
#include <stdio.h>
@ -30,7 +30,7 @@ Prefs prefs = {true, true};
void ResListPager::resultPageNext()
{
if (m_docSource.isNull()) {
LOGDEB(("ResListPager::displayPage: null source\n"));
LOGDEB(("ResListPager::resultPageNext: null source\n"));
return;
}
@ -75,6 +75,10 @@ void ResListPager::displayPage()
LOGDEB(("ResListPager::displayPage: null source\n"));
return;
}
if (m_winfirst < 0) {
LOGDEB(("ResListPager::displayPage: sequence error: winfirst < 0\n"));
return;
}
string chunk;
// Display list header

View File

@ -1,6 +1,6 @@
#ifndef _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>
using std::vector;
@ -32,6 +32,7 @@ public:
return -1;
return m_winfirst / m_pagesize;
}
virtual int pageSize() const {return m_pagesize;}
void pageNext();
bool hasNext() {return m_hasNext;}
bool hasPrev() {return m_winfirst > 0;}
@ -60,12 +61,11 @@ public:
virtual string nextUrl();
virtual string prevUrl();
virtual string pageTop() {return string();}
private:
// First docnum (from docseq) in current page
int m_winfirst;
RefCntr<DocSequence> m_docSource;
int m_pagesize;
const int m_pagesize;
bool m_hasNext;
vector<ResListEntry> m_respage;

View File

@ -1,5 +1,5 @@
#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
/*
* 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;
for (pos = db.positionlist_begin(docid, qterm);
pos != db.positionlist_end(docid, qterm); pos++) {
unsigned int ipos = *pos;
if (ipos < baseTextPosition) // Not in text body
int ipos = *pos;
if (ipos < int(baseTextPosition)) // Not in text body
continue;
LOGABS(("makeAbstract: [%s] at %d occurrences %d maxoccs %d\n",
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 sto = ipos+m_db->m_synthAbsWordCtxLen;
for (unsigned int ii = sta; ii <= sto; ii++) {
if (ii == ipos)
if (ii == (unsigned int)ipos)
sparseDoc[ii] = qterm;
else
sparseDoc[ii] = emptys;
@ -1366,6 +1366,8 @@ bool Db::stemExpand(const string &lang, const string &term,
LOGDEB1(("Db::stemExpand: Got %d from %s\n",
more.size(), it->c_str()));
result.insert(result.end(), more.begin(), more.end());
if (result.size() >= (unsigned int)max)
break;
}
LOGDEB1(("Db:::stemExpand: final count %d \n", result.size()));
return true;

View File

@ -16,7 +16,7 @@
*/
#ifndef _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 <list>
@ -51,7 +51,7 @@ extern list<string> path_dirglob(const string &dir,
const string pattern);
/// Encode according to rfc 1738
extern string url_encode(const string url,
string::size_type offs);
string::size_type offs = 0);
extern bool printableUrl(const string &fcharset,
const string &in, string &out);