Index: kioslaves/tags/kio_tags.cpp =================================================================== --- kioslaves/tags/kio_tags.cpp (revision 879917) +++ kioslaves/tags/kio_tags.cpp (revision 879918) @@ -27,11 +27,15 @@ #include <kio/job.h> #include <KUser> #include <KDebug> +#include <kio/netaccess.h> #include <QtCore/QEventLoop> #include <Soprano/Vocabulary/Xesam> #include <Soprano/Vocabulary/NAO> +#include <Soprano/QueryResultIterator> +#include <Soprano/Model> +#include <Soprano/Node> using namespace KIO; @@ -39,11 +43,13 @@ namespace { KIO::UDSEntry createUDSEntryForTag( const Nepomuk::Tag& tag ) { + kDebug() << tag.resourceUri() << tag.genericLabel() << tag.exists(); // create a virtual folder stat thingi KIO::UDSEntry uds; uds.insert( KIO::UDSEntry::UDS_NAME, tag.genericLabel() ); uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, i18n( "Things tagged with '%1'", uds.stringValue( KIO::UDSEntry::UDS_NAME ) ) ); uds.insert( KIO::UDSEntry::UDS_CREATION_TIME, tag.property( Soprano::Vocabulary::NAO::created() ).toDateTime().toTime_t() ); + uds.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, tag.property( Soprano::Vocabulary::NAO::lastModified() ).toDateTime().toTime_t() ); uds.insert( KIO::UDSEntry::UDS_ACCESS, 0700 ); uds.insert( KIO::UDSEntry::UDS_USER, KUser().loginName() ); uds.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR ); @@ -73,37 +79,30 @@ void Nepomuk::TagsProtocol::listDir( const KUrl& url ) { - kDebug(); + kDebug() << url; - // FIXME: always list all tags except the ones already in the path. These tags (subfolders) - // then allow to browse "Things tagged with 'A', 'B', and 'C'" and such - - QString path = url.path(); - if ( path.isEmpty() || path == "/" ) { - listTags(); - } - else { - QString tag; - if ( splitUrl( url, tag, path ) ) { - if ( path.isEmpty() && !tag.isEmpty() ) { - listTag( tag ); - } - else { - ForwardingSlaveBase::listDir( url ); - } + QString path; + QStringList tags; + if ( splitUrl( url, tags, path ) ) { + if ( path.isEmpty() ) { + listTags( tags ); } + else { + ForwardingSlaveBase::listDir( url ); + } } } void Nepomuk::TagsProtocol::mkdir( const KUrl &url, int permissions ) { - kDebug(); + kDebug() << url; - QString tag, path; - if ( splitUrl( url, tag, path ) ) { - if ( path.isEmpty() && !tag.isEmpty() ) { - Tag( tag ).setLabel( tag ); + QString path; + QStringList tags; + if ( splitUrl( url, tags, path ) ) { + if ( path.isEmpty() && !tags.isEmpty() ) { + Tag( tags.last() ).setLabel( tags.last() ); finished(); } else { @@ -115,7 +114,7 @@ void Nepomuk::TagsProtocol::get( const KUrl& url ) { - kDebug(); + kDebug() << url; ForwardingSlaveBase::get( url ); } @@ -133,17 +132,21 @@ void Nepomuk::TagsProtocol::copy( const KUrl& src, const KUrl& dest, int permissions, KIO::JobFlags flags ) { - kDebug(); - QString tag, path; - if ( splitUrl( src, tag, path ) ) { - QString newTag, newPath; - if ( splitUrl( dest, newTag, newPath ) ) { - if ( newTag.isEmpty() ) { + kDebug() << src << dest; + QString path; + QStringList tags; + if ( splitUrl( src, tags, path ) ) { + QString newPath; + QStringList newTags; + if ( splitUrl( dest, newTags, newPath ) ) { + if ( newTags.isEmpty() ) { ForwardingSlaveBase::copy( src, dest, permissions, flags ); } else { Resource res( path ); - res.addTag( Tag( newTag ) ); + foreach( const QString& tag, newTags ) { + res.addTag( Tag( tag ) ); + } finished(); } } @@ -153,17 +156,19 @@ void Nepomuk::TagsProtocol::rename( const KUrl& src, const KUrl& dest, KIO::JobFlags flags ) { - kDebug(); - QString tag, path; - if ( splitUrl( src, tag, path ) ) { + kDebug() << src << dest; + QString path; + QStringList tags; + if ( splitUrl( src, tags, path ) ) { if ( path.isEmpty() ) { - QString newTag, newPath; - if ( splitUrl( dest, newTag, newPath ) ) { - if ( !newTag.isEmpty() ) { + QString newPath; + QStringList newTags; + if ( splitUrl( dest, newTags, newPath ) ) { + if ( path.isEmpty() && !newTags.isEmpty() ) { // rename tag - Tag theTag( tag ); - theTag.setLabel( newTag ); - theTag.setIdentifiers( QStringList( newTag ) ); + Tag theTag( tags.last() ); + theTag.setLabel( newTags.last() ); + theTag.setIdentifiers( QStringList( newTags.last() ) ); finished(); } else { @@ -180,25 +185,35 @@ void Nepomuk::TagsProtocol::del( const KUrl& url, bool isfile ) { - kDebug(); - QString tag, path; - if ( splitUrl( url, tag, path ) ) { - if ( tag.isEmpty() ) { + kDebug() << url << isfile; + QString path; + QStringList tags; + if ( splitUrl( url, tags, path ) ) { + if ( tags.isEmpty() ) { ForwardingSlaveBase::del( url, isfile ); } else { if ( path.isEmpty() ) { // delete the tag altogether - Tag( tag ).remove(); + Tag( tags.last() ).remove(); finished(); } else { - // "untag" the file - Resource res( path ); - QList<Tag> tags = res.tags(); - tags.removeAll( Tag( tag ) ); - res.setTags( tags ); - finished(); + // "untag" the resource + KUrl targetUrl; + if ( rewriteUrl( url, targetUrl ) ) { + Resource res( targetUrl ); + if ( res.isValid() ) { + QList<Tag> currentTags = res.tags(); + foreach( const QString& tag, tags ) { + currentTags.removeAll( Tag( tag ) ); + } + res.setTags( currentTags ); + finished(); + return; + } + } + error( ERR_MALFORMED_URL, url.url() ); } } } @@ -207,14 +222,14 @@ void Nepomuk::TagsProtocol::mimetype( const KUrl& url ) { - kDebug(); - QString tag; + kDebug() << url; + QStringList tags; QString path = url.path(); if ( path.isEmpty() || path == "/" ) { mimeType( "inode/directory" ); finished(); } - else if ( splitUrl( url, tag, path ) ) { + else if ( splitUrl( url, tags, path ) ) { if ( path.isEmpty() ) { mimeType( "inode/directory" ); finished(); @@ -228,7 +243,7 @@ void Nepomuk::TagsProtocol::stat( const KUrl& url ) { - kDebug(); + kDebug() << url; QString path = url.path(); if ( path.isEmpty() || path == "/" ) { // @@ -243,15 +258,15 @@ finished(); } else { - QString tag; - if ( splitUrl( url, tag, path ) ) { + QStringList tags; + if ( splitUrl( url, tags, path ) ) { if ( path.isEmpty() ) { - statEntry( createUDSEntryForTag( Tag( tag ) ) ); + statEntry( createUDSEntryForTag( tags.last() ) ); } else { KUrl newUrl; rewriteUrl( url, newUrl ); - statEntry( statFile( newUrl ) ); + statEntry( statResource( newUrl ) ); } } finished(); @@ -261,20 +276,18 @@ bool Nepomuk::TagsProtocol::rewriteUrl( const KUrl& url, KUrl& newURL ) { - kDebug(); - QString tag, path; - if ( splitUrl( url, tag, path, false ) ) { - if ( !tag.isEmpty() ) { + kDebug() << url; + QString path; + QStringList tags; + if ( splitUrl( url, tags, path, false ) ) { + if ( !tags.isEmpty() ) { // HACK - // FIXME: This is no solution, especially because path is not unique! - Tag t( tag ); + // FIXME: Create a correct sparql query + Tag t( tags.last() ); foreach( Resource res, t.tagOf() ) { - if ( res.hasProperty( Soprano::Vocabulary::Xesam::url().toString() ) ) { - QString p = res.property( Soprano::Vocabulary::Xesam::url().toString() ).toStringList().first(); - if ( p.endsWith( path ) ) { - newURL = p; - return true; - } + if ( res.genericLabel() == path ) { + newURL = res.resourceUri(); + return true; } } } @@ -288,26 +301,33 @@ } -bool Nepomuk::TagsProtocol::splitUrl( const KUrl& url, QString& tag, QString& path, bool signalError ) +bool Nepomuk::TagsProtocol::splitUrl( const KUrl& url, QStringList& tags, QString& fileName, bool signalError ) { - kDebug(); - path = url.path().mid( 1 ); - int pos = path.indexOf( '/' ); - if ( pos > 0 ) { - tag = path.left( pos ); - path = path.mid( pos ); - if ( path.endsWith( "/" ) ) - path.truncate( path.length()-1 ); - return true; + kDebug() << url; + fileName = url.fileName( KUrl::ObeyTrailingSlash ); + QString dir = url.directory( KUrl::ObeyTrailingSlash ); + tags = dir.split( '/', QString::SkipEmptyParts ); + + // we need to check if there is a tag called fileName. I don't think there is another way + if ( !fileName.isEmpty() ) { + if ( ResourceManager::instance()->mainModel()->executeQuery( QString( "ask where { ?r a <%1> . { ?r <%2> %3 . } UNION { ?r <%4> %3 . } }" ) + .arg( Soprano::Vocabulary::NAO::Tag().toString() ) + .arg( Soprano::Vocabulary::NAO::prefLabel().toString() ) + .arg( Soprano::Node( Soprano::LiteralValue( fileName ) ).toN3() ) + .arg( Soprano::Vocabulary::NAO::identifier().toString() ), + Soprano::Query::QueryLanguageSparql ).boolValue() ) { + tags << fileName; + fileName = QString(); + } } - else if ( !path.isEmpty() ) { - tag = path; - path = QString(); + + if ( fileName.isEmpty() || !tags.isEmpty() ) { + kDebug() << url << tags << fileName; return true; } else { if ( signalError ) { - error( ERR_MALFORMED_URL, i18n( "Unable to handle URL %1" ).arg( url.path() ) ); + error( ERR_MALFORMED_URL, i18n( "Unable to handle URL %1", url.url() ) ); } return false; @@ -315,111 +335,130 @@ } - -void Nepomuk::TagsProtocol::listTags() +void Nepomuk::TagsProtocol::listTags( const QStringList& tagLabels ) { - kDebug(); - QList<Tag> tags = Tag::allTags(); + kDebug() << tagLabels; -// totalSize( tags.count() ); + // list tagged things + QList<Tag> tags; + if ( !tagLabels.isEmpty() ) { + QString query = "select ?r where { "; + foreach( const QString& tag, tagLabels ) { + tags << Tag( tag ); + query += QString( "?r <%1> <%2> . " ) + .arg( Soprano::Vocabulary::NAO::hasTag().toString() ) + .arg( QString::fromAscii( tags.last().resourceUri().toEncoded() ) ); + } + query += '}'; + kDebug() << query; + Soprano::QueryResultIterator it = ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql ); + while ( it.next() ) { + listEntry( statResource( it.binding( 0 ).uri() ), false ); + } + } - for ( QList<Tag>::iterator it = tags.begin(); - it != tags.end(); ++it ) { - listEntry( createUDSEntryForTag( *it ), false ); + + // list sub-folders + QList<Tag> allTags = Tag::allTags(); + for ( QList<Tag>::iterator it = allTags.begin(); + it != allTags.end(); ++it ) { + if ( !tags.contains( *it ) ) { + kDebug() << "listing subfolder" << it->resourceUri() << it->genericLabel() << it->exists(); + listEntry( createUDSEntryForTag( *it ), false ); + } + else { + kDebug() << "NOT listing subfolder" << it->resourceUri() << it->genericLabel() << it->exists(); + } } + listEntry( KIO::UDSEntry(), true ); finished(); } -void Nepomuk::TagsProtocol::listTag( const QString& tag ) +KIO::UDSEntry Nepomuk::TagsProtocol::statResource( const QUrl& uri ) { - kDebug() << ": " << tag; - m_currentTag = tag; + kDebug() << uri; - Tag theTag( tag ); - QList<Resource> taggedOnes = theTag.tagOf(); + KIO::UDSEntry uds; - // FIXME: use the search io slave code for resource listing - // FIXME: why can't we just use ForwardingSlaveBase to list result files? Probably the same reason as in the search io slave? - for ( QList<Resource>::const_iterator it = taggedOnes.constBegin(); - it != taggedOnes.constEnd(); ++it ) { - const Resource& res = *it; - if ( res.hasProperty( Soprano::Vocabulary::Xesam::url().toString() ) ) { - listEntry( statFile( KUrl( res.property( Soprano::Vocabulary::Xesam::url().toString() ).toStringList().first() ) ), false ); + Nepomuk::Resource res( uri ); + KUrl url = res.property( Soprano::Vocabulary::Xesam::url() ).toString(); + if ( url.isEmpty() ) { + url = uri; + } + bool isFile = false; + if ( !url.isEmpty() && url.scheme() != "akonadi" ) { // do not stat akonadi resouces here, way too slow, even hangs if akonadi is not running + kDebug() << "listing file" << url; + if ( KIO::StatJob* job = KIO::stat( url, KIO::HideProgressInfo ) ) { + job->setAutoDelete( false ); + if ( KIO::NetAccess::synchronousRun( job, 0 ) ) { + uds = job->statResult(); + if ( url.isLocalFile() ) { + uds.insert( KIO::UDSEntry::UDS_LOCAL_PATH, url.toLocalFile() ); + } + isFile = true; + } + else { + kDebug() << "failed to stat" << url; + } + delete job; } } - listEntry( KIO::UDSEntry(), true ); - finished(); -} + // + // The nepomuk resource listing is the same as in the nepomuk kio slave. + // So either only depend on that or let the nepomuk kio slave fail on each + // stat. + // + if ( !isFile ) { + kDebug() << "listing resource" << uri; + QString label = res.genericLabel(); + QString name = label; -KIO::UDSEntry Nepomuk::TagsProtocol::statFile( const KUrl& url ) -{ - m_currentUrl = url; - m_currentUDS.clear(); + // make sure name is not the URI (which is the fallback of genericLabel() and will lead to crashes in KDirModel) + if ( name.contains( '/' ) ) { + name = name.section( '/', -1 ); + if ( name.isEmpty() ) + name = res.resourceUri().fragment(); + if ( name.isEmpty() ) + name = res.resourceUri().toString().replace( '/', '_' ); + } - KIO::StatJob* job = KIO::stat( m_currentUrl, KIO::HideProgressInfo ); - connect( job, SIGNAL( result(KJob *) ), - this, SLOT( slotResult(KJob *) ) ); - connect( job, SIGNAL( redirection(KIO::Job *, const KUrl &) ), - this, SLOT( slotRedirection(KIO::Job *, const KUrl &) ) ); - - m_eventLoop.exec(); - - return m_currentUDS; -} - - -// taken from ForwardingSlaveBase -void Nepomuk::TagsProtocol::slotResult( KJob* job ) -{ - kDebug(); - if ( job->error() != 0 ) { - error( job->error(), job->errorText() ); - } - else { - KIO::StatJob* stat_job = qobject_cast<KIO::StatJob*>(job); - if ( stat_job != 0 ) { - // FIXME: The name is not unique here! This is a problem especially for - // rewriteUrl since then the URL is also not unique - m_currentUDS = stat_job->statResult(); - m_currentUDS.insert( KIO::UDSEntry::UDS_LOCAL_PATH, m_currentUrl.path() ); - m_currentUDS.insert( KIO::UDSEntry::UDS_TARGET_URL, m_currentUrl.url() ); + uds.insert( KIO::UDSEntry::UDS_NAME, name ); + uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, label ); + uds.insert( KIO::UDSEntry::UDS_CREATION_TIME, res.property( Soprano::Vocabulary::NAO::created() ).toDateTime().toTime_t() ); + uds.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, res.property( Soprano::Vocabulary::NAO::lastModified() ).toDateTime().toTime_t() ); + uds.insert( KIO::UDSEntry::UDS_ACCESS, 0700 ); + uds.insert( KIO::UDSEntry::UDS_USER, KUser().loginName() ); + uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, "application/x-nepomuk-resource" ); + QString icon = res.genericIcon(); + if ( !icon.isEmpty() ) { + uds.insert( KIO::UDSEntry::UDS_ICON_NAME, icon ); } } - m_eventLoop.exit(); -} + uds.insert( KIO::UDSEntry::UDS_TARGET_URL, uri.toString() ); + // + // FIXME: make sure we have no duplicate names + // -// taken from ForwardingSlaveBase -void Nepomuk::TagsProtocol::slotRedirection( KIO::Job *job, const KUrl& url ) -{ - kDebug(); - redirection(url); - - // We've been redirected stop everything. - job->kill( KJob::Quietly ); - finished(); - - m_eventLoop.exit(); + return uds; } - extern "C" { KDE_EXPORT int kdemain( int argc, char **argv ) { - KAboutData about( "kio_tags", 0, ki18n("kio_tags"), 0); - KCmdLineArgs::init( &about ); + // necessary to use other kio slaves + KComponentData( "kio_nepomuksearch" ); + QCoreApplication app( argc, argv ); - KApplication app; - kDebug(7102) << "Starting tags slave " << getpid(); if (argc != 4) { Index: kioslaves/tags/kio_tags.h =================================================================== --- kioslaves/tags/kio_tags.h (revision 879917) +++ kioslaves/tags/kio_tags.h (revision 879918) @@ -84,21 +84,10 @@ */ bool rewriteUrl( const KUrl& url, KUrl& newURL ); - private Q_SLOTS: - void slotRedirection( KIO::Job *job, const KUrl& url ); - void slotResult( KJob* job ); - private: - bool splitUrl( const KUrl& url, QString& tag, QString& path, bool signalError = true ); - void listTags(); - void listTag( const QString& tag ); - KIO::UDSEntry statFile( const KUrl& url ); - - QString m_currentTag; - KUrl m_currentUrl; - KIO::UDSEntry m_currentUDS; - - QEventLoop m_eventLoop; + bool splitUrl( const KUrl& url, QStringList& tags, QString& path, bool signalError = true ); + void listTags( const QStringList& tags = QStringList() ); + KIO::UDSEntry statResource( const QUrl& uri ); }; }