1 | /* |
2 | This file is part of the kcal library. |
3 | |
4 | Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> |
5 | Copyright (c) 2009 Sergio Martins <iamsergio@gmail.com> |
6 | |
7 | This library is free software; you can redistribute it and/or |
8 | modify it under the terms of the GNU Library General Public |
9 | License as published by the Free Software Foundation; either |
10 | version 2 of the License, or (at your option) any later version. |
11 | |
12 | This library 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 GNU |
15 | Library General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Library General Public License |
18 | along with this library; see the file COPYING.LIB. If not, write to |
19 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | Boston, MA 02110-1301, USA. |
21 | */ |
22 | |
23 | #include "resourcelocaldir.h" |
24 | #include "resourcelocaldir_p.h" |
25 | #include "calendarlocal.h" |
26 | #include "incidence.h" |
27 | #include "event.h" |
28 | #include "todo.h" |
29 | #include "journal.h" |
30 | |
31 | #include "kresources/configwidget.h" |
32 | |
33 | #include <kcal/assignmentvisitor.h> |
34 | #include <kcal/comparisonvisitor.h> |
35 | #include <kdebug.h> |
36 | #include <klocalizedstring.h> |
37 | #include <kconfig.h> |
38 | #include <kstandarddirs.h> |
39 | #include <kconfiggroup.h> |
40 | |
41 | #include <QtCore/QString> |
42 | #include <QtCore/QDir> |
43 | #include <QtCore/QFileInfo> |
44 | |
45 | #include <typeinfo> |
46 | #include <stdlib.h> |
47 | |
48 | #include "moc_resourcelocaldir.cpp" |
49 | #include "moc_resourcelocaldir_p.cpp" |
50 | |
51 | using namespace KCal; |
52 | |
53 | ResourceLocalDir::ResourceLocalDir() |
54 | : ResourceCached(), d( new KCal::ResourceLocalDir::Private( this ) ) |
55 | { |
56 | d->init(); |
57 | } |
58 | |
59 | ResourceLocalDir::ResourceLocalDir( const KConfigGroup &group ) |
60 | : ResourceCached( group ), d( new KCal::ResourceLocalDir::Private( this ) ) |
61 | { |
62 | readConfig( group ); |
63 | d->init(); |
64 | } |
65 | |
66 | ResourceLocalDir::ResourceLocalDir( const QString &dirName ) |
67 | : ResourceCached(), d( new KCal::ResourceLocalDir::Private( dirName, this ) ) |
68 | { |
69 | d->init(); |
70 | } |
71 | |
72 | void ResourceLocalDir::readConfig( const KConfigGroup &group ) |
73 | { |
74 | QString url = group.readPathEntry( "CalendarURL" , QString() ); |
75 | d->mURL = KUrl( url ); |
76 | } |
77 | |
78 | void ResourceLocalDir::writeConfig( KConfigGroup &group ) |
79 | { |
80 | kDebug(); |
81 | |
82 | ResourceCalendar::writeConfig( group ); |
83 | |
84 | group.writePathEntry( "CalendarURL" , d->mURL.prettyUrl() ); |
85 | } |
86 | |
87 | //@cond PRIVATE |
88 | void ResourceLocalDir::Private::init() |
89 | { |
90 | mResource->setType( "dir" ); |
91 | |
92 | mResource->setSavePolicy( SaveDelayed ); |
93 | |
94 | connect( &mDirWatch, SIGNAL(dirty(QString)), |
95 | this, SLOT(updateIncidenceInCalendar(QString)) ); |
96 | connect( &mDirWatch, SIGNAL(created(QString)), |
97 | this, SLOT(addIncidenceToCalendar(QString)) ); |
98 | connect( &mDirWatch, SIGNAL(deleted(QString)), |
99 | this, SLOT(deleteIncidenceFromCalendar(QString)) ); |
100 | |
101 | connect ( this, SIGNAL(resourceChanged(ResourceCalendar*)), |
102 | mResource, SIGNAL(resourceChanged(ResourceCalendar*)) ); |
103 | |
104 | mLock = new KABC::Lock( mURL.path() ); |
105 | |
106 | mDirWatch.addDir( mURL.path(), KDirWatch::WatchFiles ); |
107 | mDirWatch.startScan(); |
108 | } |
109 | //@endcond |
110 | |
111 | ResourceLocalDir::~ResourceLocalDir() |
112 | { |
113 | close(); |
114 | |
115 | delete d->mLock; |
116 | delete d; |
117 | } |
118 | |
119 | bool ResourceLocalDir::doOpen() |
120 | { |
121 | QFileInfo dirInfo( d->mURL.path() ); |
122 | return dirInfo.isDir() && dirInfo.isReadable() && |
123 | ( dirInfo.isWritable() || readOnly() ); |
124 | } |
125 | |
126 | bool ResourceLocalDir::doLoad( bool ) |
127 | { |
128 | kDebug(); |
129 | |
130 | calendar()->close(); |
131 | QString dirName = d->mURL.path(); |
132 | |
133 | if ( !( KStandardDirs::exists( dirName ) || KStandardDirs::exists( dirName + '/' ) ) ) { |
134 | kDebug() << "Directory '" << dirName << "' doesn't exist yet. Creating it." ; |
135 | |
136 | // Create the directory. Use 0775 to allow group-writable if the umask |
137 | // allows it (permissions will be 0775 & ~umask). This is desired e.g. for |
138 | // group-shared directories! |
139 | return KStandardDirs::makeDir( dirName, 0775 ); |
140 | } |
141 | |
142 | // The directory exists. Now try to open (the files in) it. |
143 | kDebug() << dirName; |
144 | QFileInfo dirInfo( dirName ); |
145 | if ( !( dirInfo.isDir() && dirInfo.isReadable() && |
146 | ( dirInfo.isWritable() || readOnly() ) ) ) { |
147 | return false; |
148 | } |
149 | |
150 | QDir dir( dirName ); |
151 | const QStringList entries = dir.entryList( QDir::Files | QDir::Readable ); |
152 | |
153 | bool success = true; |
154 | |
155 | foreach ( const QString &entry, entries ) { |
156 | if ( d->isTempFile( entry ) ) { |
157 | continue; // backup or temporary file, ignore it |
158 | } |
159 | |
160 | const QString fileName = dirName + '/' + entry; |
161 | kDebug() << " read '" << fileName << "'" ; |
162 | CalendarLocal cal( calendar()->timeSpec() ); |
163 | if ( !doFileLoad( cal, fileName ) ) { |
164 | success = false; |
165 | } |
166 | } |
167 | |
168 | return success; |
169 | } |
170 | |
171 | bool ResourceLocalDir::doFileLoad( CalendarLocal &cal, const QString &fileName ) |
172 | { |
173 | return d->doFileLoad( cal, fileName, false ); |
174 | } |
175 | |
176 | bool ResourceLocalDir::doSave( bool syncCache ) |
177 | { |
178 | Q_UNUSED( syncCache ); |
179 | Incidence::List list; |
180 | bool success = true; |
181 | |
182 | list = addedIncidences(); |
183 | list += changedIncidences(); |
184 | |
185 | for ( Incidence::List::iterator it = list.begin(); it != list.end(); ++it ) { |
186 | if ( !doSave( *it ) ) { |
187 | success = false; |
188 | } |
189 | } |
190 | |
191 | return success; |
192 | } |
193 | |
194 | bool ResourceLocalDir::doSave( bool, Incidence *incidence ) |
195 | { |
196 | if ( d->mDeletedIncidences.contains( incidence ) ) { |
197 | d->mDeletedIncidences.removeAll( incidence ); |
198 | return true; |
199 | } |
200 | |
201 | d->mDirWatch.stopScan(); // do prohibit the dirty() signal and a following reload() |
202 | |
203 | QString fileName = d->mURL.path() + '/' + incidence->uid(); |
204 | kDebug() << "writing '" << fileName << "'" ; |
205 | |
206 | CalendarLocal cal( calendar()->timeSpec() ); |
207 | cal.addIncidence( incidence->clone() ); |
208 | const bool ret = cal.save( fileName ); |
209 | |
210 | d->mDirWatch.startScan(); |
211 | |
212 | return ret; |
213 | } |
214 | |
215 | KABC::Lock *ResourceLocalDir::lock() |
216 | { |
217 | return d->mLock; |
218 | } |
219 | |
220 | void ResourceLocalDir::reload( const QString &file ) |
221 | { |
222 | Q_UNUSED( file ); |
223 | } |
224 | |
225 | bool ResourceLocalDir::deleteEvent( Event *event ) |
226 | { |
227 | kDebug(); |
228 | if ( d->deleteIncidenceFile( event ) ) { |
229 | if ( calendar()->deleteEvent( event ) ) { |
230 | d->mDeletedIncidences.append( event ); |
231 | return true; |
232 | } else { |
233 | return false; |
234 | } |
235 | } else { |
236 | return false; |
237 | } |
238 | } |
239 | |
240 | void ResourceLocalDir::deleteAllEvents() |
241 | { |
242 | calendar()->deleteAllEvents(); |
243 | } |
244 | |
245 | bool ResourceLocalDir::deleteTodo( Todo *todo ) |
246 | { |
247 | if ( d->deleteIncidenceFile( todo ) ) { |
248 | if ( calendar()->deleteTodo( todo ) ) { |
249 | d->mDeletedIncidences.append( todo ); |
250 | return true; |
251 | } else { |
252 | return false; |
253 | } |
254 | } else { |
255 | return false; |
256 | } |
257 | } |
258 | |
259 | void ResourceLocalDir::deleteAllTodos() |
260 | { |
261 | calendar()->deleteAllTodos(); |
262 | } |
263 | |
264 | bool ResourceLocalDir::deleteJournal( Journal *journal ) |
265 | { |
266 | if ( d->deleteIncidenceFile( journal ) ) { |
267 | if ( calendar()->deleteJournal( journal ) ) { |
268 | d->mDeletedIncidences.append( journal ); |
269 | return true; |
270 | } else { |
271 | return false; |
272 | } |
273 | } else { |
274 | return false; |
275 | } |
276 | } |
277 | |
278 | void ResourceLocalDir::deleteAllJournals() |
279 | { |
280 | calendar()->deleteAllJournals(); |
281 | } |
282 | |
283 | void ResourceLocalDir::dump() const |
284 | { |
285 | ResourceCalendar::dump(); |
286 | kDebug() << " Url:" << d->mURL.url(); |
287 | } |
288 | |
289 | //@cond PRIVATE |
290 | bool ResourceLocalDir::Private::deleteIncidenceFile( Incidence *incidence ) |
291 | { |
292 | QFile file( mURL.path() + '/' + incidence->uid() ); |
293 | if ( !file.exists() ) { |
294 | return true; |
295 | } |
296 | |
297 | mDirWatch.stopScan(); |
298 | bool removed = file.remove(); |
299 | mDirWatch.startScan(); |
300 | return removed; |
301 | } |
302 | |
303 | bool ResourceLocalDir::Private::isTempFile( const QString &fileName ) const |
304 | { |
305 | return |
306 | fileName.contains( QRegExp( "(~|\\.new|\\.tmp)$" ) ) || |
307 | QFileInfo( fileName ).fileName().startsWith( QLatin1String( "qt_temp." ) ) || |
308 | fileName == mURL.path(); |
309 | } |
310 | |
311 | void ResourceLocalDir::Private::addIncidenceToCalendar( const QString &file ) |
312 | { |
313 | |
314 | if ( mResource->isOpen() && |
315 | !isTempFile( file ) && |
316 | !mResource->calendar()->incidence( getUidFromFileName( file ) ) ) { |
317 | |
318 | CalendarLocal cal( mResource->calendar()->timeSpec() ); |
319 | if ( doFileLoad( cal, file, true ) ) { |
320 | emit resourceChanged( mResource ); |
321 | } |
322 | } |
323 | } |
324 | |
325 | void ResourceLocalDir::Private::updateIncidenceInCalendar( const QString &file ) |
326 | { |
327 | if ( mResource->isOpen() && !isTempFile( file ) ) { |
328 | CalendarLocal cal( mResource->calendar()->timeSpec() ); |
329 | if ( doFileLoad( cal, file, true ) ) { |
330 | emit resourceChanged( mResource ); |
331 | } |
332 | } |
333 | } |
334 | |
335 | QString ResourceLocalDir::Private::getUidFromFileName( const QString &fileName ) |
336 | { |
337 | return QFileInfo( fileName ).fileName(); |
338 | } |
339 | |
340 | void ResourceLocalDir::Private::deleteIncidenceFromCalendar( const QString &file ) |
341 | { |
342 | |
343 | if ( mResource->isOpen() && !isTempFile( file ) ) { |
344 | Incidence *inc = mResource->calendar()->incidence( getUidFromFileName( file ) ); |
345 | |
346 | if ( inc ) { |
347 | mResource->calendar()->deleteIncidence( inc ); |
348 | emit resourceChanged( mResource ); |
349 | } |
350 | } |
351 | } |
352 | |
353 | bool ResourceLocalDir::Private::doFileLoad( CalendarLocal &cal, |
354 | const QString &fileName, |
355 | const bool replace ) |
356 | { |
357 | if ( !cal.load( fileName ) ) { |
358 | return false; |
359 | } |
360 | Incidence::List incidences = cal.rawIncidences(); |
361 | Incidence::List::ConstIterator it; |
362 | Incidence *inc; |
363 | ComparisonVisitor compVisitor; |
364 | AssignmentVisitor assVisitor; |
365 | for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) { |
366 | Incidence *i = *it; |
367 | if ( i ) { |
368 | // should we replace, and does the incidence exist in calendar? |
369 | if ( replace && ( inc = mResource->calendar()->incidence( i->uid() ) ) ) { |
370 | if ( compVisitor.compare( i, inc ) ) { |
371 | // no need to do anything |
372 | return false; |
373 | } else { |
374 | inc->startUpdates(); |
375 | |
376 | bool assignResult = assVisitor.assign( inc, i ); |
377 | |
378 | if ( assignResult ) { |
379 | if ( !inc->relatedToUid().isEmpty() ) { |
380 | QString uid = inc->relatedToUid(); |
381 | inc->setRelatedTo( mResource->calendar()->incidence( uid ) ); |
382 | } |
383 | inc->updated(); |
384 | inc->endUpdates(); |
385 | } else { |
386 | inc->endUpdates(); |
387 | kWarning() << "Incidence (uid=" << inc->uid() |
388 | << ", summary=" << inc->summary() |
389 | << ") changed type. Replacing it." ; |
390 | |
391 | mResource->calendar()->deleteIncidence( inc ); |
392 | delete inc; |
393 | mResource->calendar()->addIncidence( i->clone() ); |
394 | } |
395 | } |
396 | } else { |
397 | mResource->calendar()->addIncidence( i->clone() ); |
398 | } |
399 | } |
400 | } |
401 | return true; |
402 | } |
403 | //@endcond |
404 | |