KCal Library
resourcelocaldir.cpp
00001 /* 00002 This file is part of the kcal library. 00003 00004 Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> 00005 Copyright (c) 2009 Sergio Martins <iamsergio@gmail.com> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00022 00023 #include "resourcelocaldir.h" 00024 #include "resourcelocaldir_p.h" 00025 #include "calendarlocal.h" 00026 #include "incidence.h" 00027 #include "event.h" 00028 #include "todo.h" 00029 #include "journal.h" 00030 00031 #include "kresources/configwidget.h" 00032 00033 #include <kcal/assignmentvisitor.h> 00034 #include <kcal/comparisonvisitor.h> 00035 #include <kdebug.h> 00036 #include <klocale.h> 00037 #include <kconfig.h> 00038 #include <kstandarddirs.h> 00039 #include <kconfiggroup.h> 00040 00041 #include <QtCore/QString> 00042 #include <QtCore/QDir> 00043 #include <QtCore/QFileInfo> 00044 00045 #include <typeinfo> 00046 #include <stdlib.h> 00047 00048 #include "resourcelocaldir.moc" 00049 #include "resourcelocaldir_p.moc" 00050 00051 using namespace KCal; 00052 00053 ResourceLocalDir::ResourceLocalDir() 00054 : ResourceCached(), d( new KCal::ResourceLocalDir::Private( this ) ) 00055 { 00056 d->init(); 00057 } 00058 00059 ResourceLocalDir::ResourceLocalDir( const KConfigGroup &group ) 00060 : ResourceCached( group ), d( new KCal::ResourceLocalDir::Private( this ) ) 00061 { 00062 readConfig( group ); 00063 d->init(); 00064 } 00065 00066 ResourceLocalDir::ResourceLocalDir( const QString &dirName ) 00067 : ResourceCached(), d( new KCal::ResourceLocalDir::Private( dirName, this ) ) 00068 { 00069 d->init(); 00070 } 00071 00072 void ResourceLocalDir::readConfig( const KConfigGroup &group ) 00073 { 00074 QString url = group.readPathEntry( "CalendarURL", QString() ); 00075 d->mURL = KUrl( url ); 00076 } 00077 00078 void ResourceLocalDir::writeConfig( KConfigGroup &group ) 00079 { 00080 kDebug(); 00081 00082 ResourceCalendar::writeConfig( group ); 00083 00084 group.writePathEntry( "CalendarURL", d->mURL.prettyUrl() ); 00085 } 00086 00087 //@cond PRIVATE 00088 void ResourceLocalDir::Private::init() 00089 { 00090 mResource->setType( "dir" ); 00091 00092 mResource->setSavePolicy( SaveDelayed ); 00093 00094 connect( &mDirWatch, SIGNAL( dirty( const QString & ) ), 00095 this, SLOT( updateIncidenceInCalendar( const QString & ) ) ); 00096 connect( &mDirWatch, SIGNAL( created( const QString & ) ), 00097 this, SLOT( addIncidenceToCalendar( const QString & ) ) ); 00098 connect( &mDirWatch, SIGNAL( deleted( const QString & ) ), 00099 this, SLOT( deleteIncidenceFromCalendar( const QString & ) ) ); 00100 00101 connect ( this, SIGNAL(resourceChanged( ResourceCalendar *)), 00102 mResource, SIGNAL(resourceChanged( ResourceCalendar *)) ); 00103 00104 mLock = new KABC::Lock( mURL.path() ); 00105 00106 mDirWatch.addDir( mURL.path(), KDirWatch::WatchFiles ); 00107 mDirWatch.startScan(); 00108 } 00109 //@endcond 00110 00111 ResourceLocalDir::~ResourceLocalDir() 00112 { 00113 close(); 00114 00115 delete d->mLock; 00116 delete d; 00117 } 00118 00119 bool ResourceLocalDir::doOpen() 00120 { 00121 QFileInfo dirInfo( d->mURL.path() ); 00122 return dirInfo.isDir() && dirInfo.isReadable() && 00123 ( dirInfo.isWritable() || readOnly() ); 00124 } 00125 00126 bool ResourceLocalDir::doLoad( bool ) 00127 { 00128 kDebug(); 00129 00130 calendar()->close(); 00131 QString dirName = d->mURL.path(); 00132 00133 if ( !( KStandardDirs::exists( dirName ) || KStandardDirs::exists( dirName + '/' ) ) ) { 00134 kDebug() << "Directory '" << dirName << "' doesn't exist yet. Creating it."; 00135 00136 // Create the directory. Use 0775 to allow group-writable if the umask 00137 // allows it (permissions will be 0775 & ~umask). This is desired e.g. for 00138 // group-shared directories! 00139 return KStandardDirs::makeDir( dirName, 0775 ); 00140 } 00141 00142 // The directory exists. Now try to open (the files in) it. 00143 kDebug() << dirName; 00144 QFileInfo dirInfo( dirName ); 00145 if ( !( dirInfo.isDir() && dirInfo.isReadable() && 00146 ( dirInfo.isWritable() || readOnly() ) ) ) { 00147 return false; 00148 } 00149 00150 QDir dir( dirName ); 00151 const QStringList entries = dir.entryList( QDir::Files | QDir::Readable ); 00152 00153 bool success = true; 00154 00155 foreach ( const QString &entry, entries ) { 00156 if ( d->isTempFile( entry ) ) { 00157 continue; // backup or temporary file, ignore it 00158 } 00159 00160 const QString fileName = dirName + '/' + entry; 00161 kDebug() << " read '" << fileName << "'"; 00162 CalendarLocal cal( calendar()->timeSpec() ); 00163 if ( !doFileLoad( cal, fileName ) ) { 00164 success = false; 00165 } 00166 } 00167 00168 return success; 00169 } 00170 00171 bool ResourceLocalDir::doFileLoad( CalendarLocal &cal, const QString &fileName ) 00172 { 00173 return d->doFileLoad( cal, fileName, false ); 00174 } 00175 00176 bool ResourceLocalDir::doSave( bool syncCache ) 00177 { 00178 Q_UNUSED( syncCache ); 00179 Incidence::List list; 00180 bool success = true; 00181 00182 list = addedIncidences(); 00183 list += changedIncidences(); 00184 00185 for ( Incidence::List::iterator it = list.begin(); it != list.end(); ++it ) { 00186 if ( !doSave( *it ) ) { 00187 success = false; 00188 } 00189 } 00190 00191 return success; 00192 } 00193 00194 bool ResourceLocalDir::doSave( bool, Incidence *incidence ) 00195 { 00196 if ( d->mDeletedIncidences.contains( incidence ) ) { 00197 d->mDeletedIncidences.removeAll( incidence ); 00198 return true; 00199 } 00200 00201 d->mDirWatch.stopScan(); // do prohibit the dirty() signal and a following reload() 00202 00203 QString fileName = d->mURL.path() + '/' + incidence->uid(); 00204 kDebug() << "writing '" << fileName << "'"; 00205 00206 CalendarLocal cal( calendar()->timeSpec() ); 00207 cal.addIncidence( incidence->clone() ); 00208 const bool ret = cal.save( fileName ); 00209 00210 d->mDirWatch.startScan(); 00211 00212 return ret; 00213 } 00214 00215 KABC::Lock *ResourceLocalDir::lock() 00216 { 00217 return d->mLock; 00218 } 00219 00220 void ResourceLocalDir::reload( const QString &file ) 00221 { 00222 Q_UNUSED( file ); 00223 } 00224 00225 bool ResourceLocalDir::deleteEvent( Event *event ) 00226 { 00227 kDebug(); 00228 if ( d->deleteIncidenceFile( event ) ) { 00229 if ( calendar()->deleteEvent( event ) ) { 00230 d->mDeletedIncidences.append( event ); 00231 return true; 00232 } else { 00233 return false; 00234 } 00235 } else { 00236 return false; 00237 } 00238 } 00239 00240 void ResourceLocalDir::deleteAllEvents() 00241 { 00242 calendar()->deleteAllEvents(); 00243 } 00244 00245 bool ResourceLocalDir::deleteTodo( Todo *todo ) 00246 { 00247 if ( d->deleteIncidenceFile( todo ) ) { 00248 if ( calendar()->deleteTodo( todo ) ) { 00249 d->mDeletedIncidences.append( todo ); 00250 return true; 00251 } else { 00252 return false; 00253 } 00254 } else { 00255 return false; 00256 } 00257 } 00258 00259 void ResourceLocalDir::deleteAllTodos() 00260 { 00261 calendar()->deleteAllTodos(); 00262 } 00263 00264 bool ResourceLocalDir::deleteJournal( Journal *journal ) 00265 { 00266 if ( d->deleteIncidenceFile( journal ) ) { 00267 if ( calendar()->deleteJournal( journal ) ) { 00268 d->mDeletedIncidences.append( journal ); 00269 return true; 00270 } else { 00271 return false; 00272 } 00273 } else { 00274 return false; 00275 } 00276 } 00277 00278 void ResourceLocalDir::deleteAllJournals() 00279 { 00280 calendar()->deleteAllJournals(); 00281 } 00282 00283 void ResourceLocalDir::dump() const 00284 { 00285 ResourceCalendar::dump(); 00286 kDebug() << " Url:" << d->mURL.url(); 00287 } 00288 00289 //@cond PRIVATE 00290 bool ResourceLocalDir::Private::deleteIncidenceFile( Incidence *incidence ) 00291 { 00292 QFile file( mURL.path() + '/' + incidence->uid() ); 00293 if ( !file.exists() ) { 00294 return true; 00295 } 00296 00297 mDirWatch.stopScan(); 00298 bool removed = file.remove(); 00299 mDirWatch.startScan(); 00300 return removed; 00301 } 00302 00303 bool ResourceLocalDir::Private::isTempFile( const QString &fileName ) const 00304 { 00305 return 00306 fileName.contains( QRegExp( "(~|\\.new|\\.tmp)$" ) ) || 00307 QFileInfo( fileName ).fileName().startsWith( QLatin1String( "qt_temp." ) ) || 00308 fileName == mURL.path(); 00309 } 00310 00311 void ResourceLocalDir::Private::addIncidenceToCalendar( const QString &file ) 00312 { 00313 00314 if ( mResource->isOpen() && 00315 !isTempFile( file ) && 00316 !mResource->calendar()->incidence( getUidFromFileName( file ) ) ) { 00317 00318 CalendarLocal cal( mResource->calendar()->timeSpec() ); 00319 if ( doFileLoad( cal, file, true ) ) { 00320 emit resourceChanged( mResource ); 00321 } 00322 } 00323 } 00324 00325 void ResourceLocalDir::Private::updateIncidenceInCalendar( const QString &file ) 00326 { 00327 if ( mResource->isOpen() && !isTempFile( file ) ) { 00328 CalendarLocal cal( mResource->calendar()->timeSpec() ); 00329 if ( doFileLoad( cal, file, true ) ) { 00330 emit resourceChanged( mResource ); 00331 } 00332 } 00333 } 00334 00335 QString ResourceLocalDir::Private::getUidFromFileName( const QString &fileName ) 00336 { 00337 return QFileInfo( fileName ).fileName(); 00338 } 00339 00340 void ResourceLocalDir::Private::deleteIncidenceFromCalendar( const QString &file ) 00341 { 00342 00343 if ( mResource->isOpen() && !isTempFile( file ) ) { 00344 Incidence *inc = mResource->calendar()->incidence( getUidFromFileName( file ) ); 00345 00346 if ( inc ) { 00347 mResource->calendar()->deleteIncidence( inc ); 00348 emit resourceChanged( mResource ); 00349 } 00350 } 00351 } 00352 00353 bool ResourceLocalDir::Private::doFileLoad( CalendarLocal &cal, 00354 const QString &fileName, 00355 const bool replace ) 00356 { 00357 if ( !cal.load( fileName ) ) { 00358 return false; 00359 } 00360 Incidence::List incidences = cal.rawIncidences(); 00361 Incidence::List::ConstIterator it; 00362 Incidence *inc; 00363 ComparisonVisitor compVisitor; 00364 AssignmentVisitor assVisitor; 00365 for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) { 00366 Incidence *i = *it; 00367 if ( i ) { 00368 // should we replace, and does the incidence exist in calendar? 00369 if ( replace && ( inc = mResource->calendar()->incidence( i->uid() ) ) ) { 00370 if ( compVisitor.compare( i, inc ) ) { 00371 // no need to do anything 00372 return false; 00373 } else { 00374 inc->startUpdates(); 00375 00376 bool assignResult = assVisitor.assign( inc, i ); 00377 00378 if ( assignResult ) { 00379 if ( !inc->relatedToUid().isEmpty() ) { 00380 QString uid = inc->relatedToUid(); 00381 inc->setRelatedTo( mResource->calendar()->incidence( uid ) ); 00382 } 00383 inc->updated(); 00384 inc->endUpdates(); 00385 } else { 00386 inc->endUpdates(); 00387 kWarning() << "Incidence (uid=" << inc->uid() 00388 << ", summary=" << inc->summary() 00389 << ") changed type. Replacing it."; 00390 00391 mResource->calendar()->deleteIncidence( inc ); 00392 delete inc; 00393 mResource->calendar()->addIncidence( i->clone() ); 00394 } 00395 } 00396 } else { 00397 mResource->calendar()->addIncidence( i->clone() ); 00398 } 00399 } 00400 } 00401 return true; 00402 } 00403 //@endcond