2
Copyright 2008-2010 Sebastian Trueg <trueg@kde.org>
4
This program is free software; you can redistribute it and/or
5
modify it under the terms of the GNU General Public License as
6
published by the Free Software Foundation; either version 2 of
7
the License or (at your option) version 3 or any later version
8
accepted by the membership of KDE e.V. (or its successor approved
9
by the membership of KDE e.V.), which shall act as a proxy
10
defined in Section 14 of version 3 of the license.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program. If not, see <http://www.gnu.org/licenses/>.
21
#include "resourcestat.h"
22
#include "nepomuksearchurltools.h"
24
#include <QtCore/QEventLoop>
25
#include <QtCore/QTimer>
26
#include <QtCore/QFile>
31
#include <kio/udsentry.h>
34
#include <Nepomuk2/Variant>
35
#include <Nepomuk2/Types/Class>
36
#include <Nepomuk2/ResourceManager>
37
#include <Nepomuk2/Query/Query>
38
#include <Nepomuk2/Query/ComparisonTerm>
39
#include <Nepomuk2/Query/ResourceTerm>
40
#include <Nepomuk2/Vocabulary/NFO>
41
#include <Nepomuk2/Vocabulary/NIE>
42
#include <Nepomuk2/Vocabulary/PIMO>
44
#include <Soprano/Vocabulary/RDF>
45
#include <Soprano/Vocabulary/NAO>
46
#include <Soprano/QueryResultIterator>
47
#include <Soprano/NodeIterator>
48
#include <Soprano/Node>
49
#include <Soprano/Model>
51
#include <Solid/Device>
52
#include <Solid/StorageAccess>
55
KUrl Nepomuk2::stripQuery( const KUrl& url )
58
newUrl.setEncodedQuery( QByteArray() );
63
Nepomuk2::Resource Nepomuk2::splitNepomukUrl( const KUrl& url, QString* filename )
66
// let's try to extract the resource from the url in case we listed a tag or
67
// filesystem and need to stat the entries in those virtual folders
69
// pre KDE 4.4 resources had just a single section, in KDE 4.4 we have "/res/<UUID>"
71
if(url.hasQueryItem(QLatin1String("resource"))) {
72
return KUrl(url.queryItemValue(QLatin1String("resource")));
75
const QString urlStr = stripQuery( url ).url();
76
int pos = urlStr.indexOf( '/', urlStr.startsWith( QLatin1String( "nepomuk:/res/" ) ) ? 13 : 9 );
78
KUrl resourceUri = urlStr.left(pos);
80
*filename = urlStr.mid( pos+1 );
84
return stripQuery( url );
90
bool Nepomuk2::isRemovableMediaFile( const Nepomuk2::Resource& res )
92
if ( res.hasProperty( Nepomuk2::Vocabulary::NIE::url() ) ) {
93
KUrl url = res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl();
94
return ( url.protocol() == QLatin1String( "filex" ) );
102
Solid::StorageAccess* Nepomuk2::storageFromUUID( const QString& uuid )
104
QString solidQuery = QString::fromLatin1( "[ StorageVolume.usage=='FileSystem' AND StorageVolume.uuid=='%1' ]" ).arg( uuid.toLower() );
105
QList<Solid::Device> devices = Solid::Device::listFromQuery( solidQuery );
106
kDebug() << uuid << solidQuery << devices.count();
107
if ( !devices.isEmpty() )
108
return devices.first().as<Solid::StorageAccess>();
114
bool Nepomuk2::mountAndWait( Solid::StorageAccess* storage )
118
loop.connect( storage,
119
SIGNAL(accessibilityChanged(bool, QString)),
122
QTimer::singleShot( 20000, &loop, SLOT(quit()) );
127
kDebug() << storage << storage->isAccessible();
129
return storage->isAccessible();
133
KUrl Nepomuk2::determineFilesystemPath( const Nepomuk2::Resource& fsRes )
135
QString uuidQuery = QString::fromLatin1( "select ?uuid where { %1 %2 ?uuid . }" )
136
.arg( Soprano::Node::resourceToN3( fsRes.uri() ),
137
Soprano::Node::resourceToN3( Soprano::Vocabulary::NAO::identifier() ) );
138
Soprano::QueryResultIterator it = Nepomuk2::ResourceManager::instance()->mainModel()->executeQuery( uuidQuery, Soprano::Query::QueryLanguageSparql );
140
Solid::StorageAccess* storage = storageFromUUID( it["uuid"].toString() );
143
( storage->isAccessible() ||
144
mountAndWait( storage ) ) ) {
145
return storage->filePath();
152
QString Nepomuk2::getFileSystemLabelForRemovableMediaFileUrl( const Nepomuk2::Resource& res )
154
QList<Soprano::Node> labelNodes
155
= Nepomuk2::ResourceManager::instance()->mainModel()->executeQuery( QString::fromLatin1( "select ?label where { "
156
"%1 nie:isPartOf ?fs . "
157
"?fs a nfo:Filesystem . "
158
"?fs nao:prefLabel ?label . "
160
.arg( Soprano::Node::resourceToN3( res.uri() ) ),
161
Soprano::Query::QueryLanguageSparql ).iterateBindings( "label" ).allNodes();
163
if ( !labelNodes.isEmpty() )
164
return labelNodes.first().toString();
166
return res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl().host(); // Solid UUID
170
KUrl Nepomuk2::convertRemovableMediaFileUrl( const KUrl& url, bool evenMountIfNecessary )
172
Solid::StorageAccess* storage = Nepomuk2::storageFromUUID( url.host() );
173
kDebug() << url << storage;
175
( storage->isAccessible() ||
176
( evenMountIfNecessary && Nepomuk2::mountAndWait( storage ) ) ) ) {
177
kDebug() << "converted:" << KUrl( storage->filePath() + QLatin1String( "/" ) + url.path() );
178
return QString( storage->filePath() + QLatin1String( "/" ) + url.path() );
186
void Nepomuk2::addGenericNepomukResourceData( const Nepomuk2::Resource& res, KIO::UDSEntry& uds, bool includeMimeType )
189
// Add some random values
191
uds.insert( KIO::UDSEntry::UDS_ACCESS, 0700 );
192
uds.insert( KIO::UDSEntry::UDS_USER, KUser().loginName() );
193
if ( res.hasProperty( Vocabulary::NIE::lastModified() ) ) {
194
// remotely stored files
195
uds.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, res.property( Vocabulary::NIE::lastModified() ).toDateTime().toTime_t() );
198
// all nepomuk resources
199
uds.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, res.property( Soprano::Vocabulary::NAO::lastModified() ).toDateTime().toTime_t() );
200
uds.insert( KIO::UDSEntry::UDS_CREATION_TIME, res.property( Soprano::Vocabulary::NAO::created() ).toDateTime().toTime_t() );
203
if ( res.hasProperty( Vocabulary::NIE::contentSize() ) ) {
204
// remotely stored files
205
uds.insert( KIO::UDSEntry::UDS_SIZE, res.property( Vocabulary::NIE::contentSize() ).toInt() );
210
// Starting with KDE 4.4 we have the pretty UDS_NEPOMUK_URI which makes
211
// everything much cleaner since kio slaves can decide if the resources can be
214
uds.insert( KIO::UDSEntry::UDS_NEPOMUK_URI, KUrl( res.uri() ).url() );
216
if ( includeMimeType ) {
217
// Use nice display types like "Person", "Project" and so on
218
Nepomuk2::Types::Class type( res.type() );
219
if (!type.label().isEmpty())
220
uds.insert( KIO::UDSEntry::UDS_DISPLAY_TYPE, type.label() );
222
QString icon = res.genericIcon();
223
if ( !icon.isEmpty() ) {
224
uds.insert( KIO::UDSEntry::UDS_ICON_NAME, icon );
227
// a fallback icon for nepomuk resources
228
uds.insert( KIO::UDSEntry::UDS_ICON_NAME, QLatin1String( "nepomuk" ) );
231
if ( uds.stringValue( KIO::UDSEntry::UDS_ICON_NAME ) != QLatin1String( "nepomuk" ) )
232
uds.insert( KIO::UDSEntry::UDS_ICON_OVERLAY_NAMES, QLatin1String( "nepomuk" ) );
237
KIO::UDSEntry Nepomuk2::statNepomukResource( const Nepomuk2::Resource& res, bool doNotForward )
240
// We do not have a local file
241
// This is where the magic starts to happen.
242
// This is where we only use Nepomuk properties
246
// we handle files on removable media which are not mounted
248
bool isFileOnRemovableMedium = isRemovableMediaFile( res );
250
// The display name can be anything
252
if ( isFileOnRemovableMedium ) {
253
displayName = i18nc( "%1 is a filename of a file on a removable device, "
254
"%2 is the name of the removable medium which often is something like "
255
"'X GiB Removable Media.",
256
"%1 (on unmounted medium <resource>%2</resource>)",
258
getFileSystemLabelForRemovableMediaFileUrl( res ) );
261
displayName = res.genericLabel();
263
uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, displayName );
265
// UDS_NAME needs to be unique but can be ugly
266
uds.insert( KIO::UDSEntry::UDS_NAME, resourceUriToUdsName( res.uri() ) );
269
// There can still be file resources that have a mimetype but are
270
// stored remotely, thus they do not have a local nie:url
272
// Sadly Strigi's mimetype is not very useful (yet)
273
/* QStringList mimeTypes = res.property( Vocabulary::NIE::mimeType() ).toStringList();
274
if ( !mimeTypes.isEmpty() ) {
275
uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, mimeTypes.first() );
278
if ( !doNotForward && isFileOnRemovableMedium ) {
279
KMimeType::Ptr mt = KMimeType::findByUrl( res.property( Vocabulary::NIE::url() ).toUrl(),
281
false, /* no local file as it is not accessible at the moment */
282
true /* fast mode */ );
284
uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, mt->name() );
288
addGenericNepomukResourceData( res, uds, !uds.contains( KIO::UDSEntry::UDS_MIME_TYPE ) );
290
if ( !doNotForward ) {
291
KUrl reUrl = Nepomuk2::redirectionUrl( res );
292
if ( !reUrl.isEmpty() ) {
293
uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, QLatin1String( "inode/directory" ) );
294
uds.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
302
bool Nepomuk2::willBeRedirected( const Nepomuk2::Resource& res )
304
// here the same canditions as in redirectionUrl need to be listed
305
return( res.hasType( Nepomuk2::Vocabulary::NFO::Folder() ) ||
306
res.hasType( Soprano::Vocabulary::NAO::Tag() ) ||
307
res.hasType( Nepomuk2::Vocabulary::NFO::Filesystem() ) ||
308
!res.hasType( Nepomuk2::Vocabulary::NFO::FileDataObject() ) );
312
KUrl Nepomuk2::redirectionUrl( const Nepomuk2::Resource& res )
314
// list folders by forwarding to the actual folder on disk
315
if ( res.hasType( Nepomuk2::Vocabulary::NFO::Folder() ) ) {
316
return res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl();
319
// list filesystems by forwarding to the mounted path on disk (in case the fs is mounted)
320
else if ( res.hasType( Nepomuk2::Vocabulary::NFO::Filesystem() ) ) {
321
KUrl fsUrl = determineFilesystemPath( res );
322
if ( fsUrl.isValid() ) {
327
// list tags by listing everything tagged with that tag
328
else if ( res.hasType( Soprano::Vocabulary::NAO::Tag() ) ) {
329
Query::ComparisonTerm term( Soprano::Vocabulary::NAO::hasTag(), Query::ResourceTerm( res ), Query::ComparisonTerm::Equal );
330
KUrl url = Query::Query( term ).toSearchUrl( i18n( "Things tagged '%1'", res.genericLabel() ) );
331
url.addQueryItem( QLatin1String( "resource" ), KUrl( res.uri() ).url() );
335
// list everything else besides files by querying things related to the resource in some way
336
// this works for music albums or artists but it would also work for tags
337
else if ( !res.hasType( Nepomuk2::Vocabulary::NFO::FileDataObject() ) ) {
338
Query::ComparisonTerm term( QUrl(), Query::ResourceTerm( res ), Query::ComparisonTerm::Equal );
339
KUrl url = Query::Query( term ).toSearchUrl( res.genericLabel() );
340
url.addQueryItem( QLatin1String( "resource" ), KUrl( res.uri() ).url() );
345
// no forwarding done
353
* Check if the resource represents a local file with an existing nie:url property.
355
bool isLocalFile( const Nepomuk2::Resource& res )
357
if ( res.hasProperty( Nepomuk2::Vocabulary::NIE::url() ) ) {
358
KUrl url = res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl();
359
return ( !url.isEmpty() &&
360
QFile::exists( url.toLocalFile() ) );
368
KUrl Nepomuk2::nepomukToFileUrl( const KUrl& url, bool evenMountIfNecessary )
371
Nepomuk2::Resource res = splitNepomukUrl( url, &filename );
378
if ( isLocalFile( res ) ) {
379
newURL = res.property( Vocabulary::NIE::url() ).toUrl();
381
else if ( isRemovableMediaFile( res ) ) {
382
const KUrl removableMediaUrl = res.property( Nepomuk2::Vocabulary::NIE::url() ).toUrl();
383
newURL = convertRemovableMediaFileUrl( removableMediaUrl, evenMountIfNecessary );
386
if ( newURL.isValid() && !filename.isEmpty() ) {
387
newURL.addPath( filename );
390
kDebug() << url << newURL;