Don't keep compiling/run if something failed.
[kdevelopdvcssupport.git] / plugins / teamwork / patchesmanager.cpp
blob81f7fcfcfc61b6bdcbddd323782fd55a3f78aedf
1 /***************************************************************************
2 Copyright 2006 David Nolden <david.nolden.kdevelop@art-master.de>
3 ***************************************************************************/
5 /***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 ***************************************************************************/
14 #include "patchesmanager.h"
15 #include <boost/archive/polymorphic_xml_oarchive.hpp>
16 #include <boost/archive/polymorphic_xml_iarchive.hpp>
18 #include <QPersistentModelIndex>
19 #include <QMenu>
20 #include <QFile>
21 #include <QTimer>
23 #include <kmimetype.h>
24 #include <kmimetypetrader.h>
25 #include <kopenwithdialog.h>
26 #include <k3process.h>
27 #include <kprocess.h>
28 #include <kdialog.h>
30 #include <interfaces/idocumentcontroller.h>
31 #include <interfaces/iplugincontroller.h>
33 #include "lib/network/sessioninterface.h"
34 #include "lib/network/messagetypeset.h"
35 #include "lib/network/messagesendhelper.h"
37 #include "kdevteamworkplugin.h"
38 #include "kdevteamwork_user.h"
39 #include "ui_kdevteamwork_interface.h"
40 #include "collaborationmanager.h"
41 #include "messagemanager.h"
42 #include "kdevteamwork_client.h"
43 #include "kdevteamwork_helpers.h"
44 #include "teamworkfoldermanager.h"
45 #include "editpatch.h"
47 #include "serializationutils.h"
49 /* Exclude this file from doublequote_chars check as krazy doesn't understand
50 std::string*/
51 //krazy:excludeall=doublequote_chars
53 QString userNameFromSession( const SessionPointer& session ) {
54 UserPointer::Locked lu = userFromSession( session );
55 if ( lu ) {
56 return ~lu->name();
57 } else {
58 return "unlockable-user";
62 ///Tries to extract the default-extension from a pattern-list. The returned extension includes the dot.
63 QString getDefaultExtension( const QStringList& patterns ) {
64 if ( patterns.isEmpty() )
65 return "";
66 for ( QStringList::const_iterator it = patterns.begin(); it != patterns.end(); ++it ) {
67 QString s = *it;
68 int i = s.lastIndexOf( "*." );
69 if ( i == -1 )
70 continue;
71 return s.mid( i + 1 );
73 return "";
77 ///currently, KOpenWithDlg crashes.
78 //#define USE_KOPENWITHDLG
80 REGISTER_MESSAGE( PatchesManagerMessage )
81 REGISTER_MESSAGE( PatchesListMessage )
82 EASY_IMPLEMENT_MESSAGE( PatchRequestMessage )
83 EASY_IMPLEMENT_MESSAGE( PatchMessage )
85 Q_DECLARE_METATYPE( QPersistentModelIndex )
86 Q_DECLARE_METATYPE( LocalPatchSourcePointer )
87 Q_DECLARE_METATYPE( MessagePointer )
89 PatchesManager::PatchesManager( KDevTeamwork* tw ) : QObject( tw ), m_teamwork( tw ), dispatcher_( *this ), m_manageDlg( 0 ) {
91 connect( m_teamwork, SIGNAL( init() ), this, SLOT( init() ) );
93 qRegisterMetaType<PatchesManagerMessagePointer>( "PatchesManagerMessagePointer" );
94 qRegisterMetaType<LocalPatchSourcePointer>( "LocalPatchSourcePointer" );
96 m_showPatchAction = new QAction( i18n("Show"), this );
97 connect( m_showPatchAction, SIGNAL( triggered() ), this, SLOT( slotShowPatch() ) );
98 m_showPatchAction->setToolTip( i18n("Request and show the selected file.") );
100 m_showPatchInfoAction = new QAction( i18n("Show Info"), this );
101 connect( m_showPatchInfoAction, SIGNAL( triggered() ), this, SLOT( slotShowPatchInfo() ) );
102 m_showPatchInfoAction->setToolTip( i18n("Show Information about the selected file.") );
104 m_downloadPatchAction = new QAction( i18n("Download Item"), this );
105 connect( m_downloadPatchAction, SIGNAL( triggered() ), this, SLOT( slotDownloadPatch() ) );
106 m_downloadPatchAction->setToolTip( i18n("Download the item and store it into the list of local items.") );
108 m_applyPatchAction = new QAction( i18n("Apply Locally"), this );
109 connect( m_applyPatchAction, SIGNAL( triggered() ), this, SLOT( slotApplyPatch() ) );
110 m_applyPatchAction->setToolTip( i18n("Apply the item/patch to the local workspace/project and store it into the list of local patches.") );
112 m_allowPatchAction = new QAction( i18n("Allow"), this );
113 connect( m_allowPatchAction, SIGNAL( triggered() ), this, SLOT( slotAllowPatch() ) );
114 m_allowPatchAction->setToolTip( i18n("Send the requested patch to the user.") );
116 m_denyPatchAction = new QAction( i18n("Deny"), this );
117 connect( m_denyPatchAction, SIGNAL( triggered() ), this, SLOT( slotDenyPatch() ) );
118 m_denyPatchAction->setToolTip( i18n("Do not send the requested patch to the user.") );
120 m_showRequestedPatchAction = new QAction( i18n("Show Patch-Info"), this );
121 connect( m_showRequestedPatchAction, SIGNAL( triggered() ), this, SLOT( slotShowRequestedPatch() ) );
122 m_showRequestedPatchAction->setToolTip( i18n("Show the requested patch.") );
124 m_updateTimer = new QTimer( this );
125 m_updateTimer->setSingleShot( true );
126 connect( m_updateTimer, SIGNAL(timeout()), this, SLOT(guiUpdatePatchesList() ) );
127 connect( m_teamwork, SIGNAL( deInit() ), this, SLOT( save() ) );
130 void PatchesManager::init() {
131 connect( m_teamwork->widgets().managePatches, SIGNAL( pressed() ), this, SLOT( slotManagePatches() ) );
132 connect( m_teamwork, SIGNAL( updateConnection( TeamworkClientPointer ) ), this, SLOT( slotUpdateConnection( TeamworkClientPointer ) ) );
133 load();
136 void PatchesManager::slotManagePatches() {
137 m_manageDlg = new KDialog( m_teamwork->widget() );
138 m_manageDlg->setButtons( KDialog::Close );
139 m_manageDlg->setCaption( i18n("Manage Patches") );
140 m_managePatches.setupUi( m_manageDlg->mainWidget() );
141 m_managePatches.patchesList->setEditTriggers( QAbstractItemView::NoEditTriggers );
142 connect( m_managePatches.edit, SIGNAL( pressed() ), this, SLOT( slotEditPatch() ) );
143 connect( m_managePatches.add, SIGNAL( pressed() ), this, SLOT( slotAddPatch() ) );
144 connect( m_managePatches.remove, SIGNAL( pressed() ), this, SLOT( slotRemovePatch() ) );
145 connect( m_manageDlg, SIGNAL( finished() ), this, SLOT( slotCloseManagement() ) );
146 m_manageDlg->show();
147 m_patchesModel = new QStandardItemModel( 0, 1, m_managePatches.patchesList );
148 m_managePatches.patchesList->setModel( m_patchesModel );
149 guiUpdatePatchesList();
152 void PatchesManager::slotUpdateConnection( TeamworkClientPointer newClient ) {
153 if ( newClient ) {
154 log( "slotUpdateConnection", Debug );
155 connect( newClient.unsafe(), SIGNAL( signalDispatchMessage( PatchesManagerMessagePointer ) ), this, SLOT( processMessage( PatchesManagerMessagePointer ) ), Qt::QueuedConnection );
159 void PatchesManager::log( const QString& str, LogLevel level ) {
160 m_teamwork->log( "patchesmanager: " + str, level );
163 void PatchesManager::addPatch( const LocalPatchSourcePointer& patch ) {
164 if( !hasPatch( patch ) ) {
165 m_config.addPatch( patch );
166 guiUpdatePatchesList();
170 void PatchesManager::editStateChanged( EditPatch* ) {
171 m_updateTimer->start( 100 );
174 void PatchesManager::editDialogClosed( EditPatch* dialog ) {
175 m_editing.removeAll( dialog );
176 m_updateTimer->start( 100 );
179 bool PatchesManager::hasPatch( const LocalPatchSourcePointer& patch ) {
180 return m_config.hasPatch( patch );
183 void PatchesManager::slotAddPatch() {
184 showEditDialog( new LocalPatchSource(), true );
187 void PatchesManager::showPatchInfo( const LocalPatchSourcePointer & patch, bool local ) {
188 showEditDialog( patch, local );
191 EditPatchPointer PatchesManager::showEditDialog( const LocalPatchSourcePointer & patch, bool local ) {
192 if( !patch ) return 0;
193 for( QList<EditPatchPointer>::iterator it = m_editing.begin(); it != m_editing.end(); ++it ) {
194 if( (*it)->patch() == patch ) return *it;
196 EditPatchPointer p = new EditPatch( this, patch, local );
198 connect( p.data(), SIGNAL(dialogClosed( EditPatch* )), this, SLOT( editDialogClosed( EditPatch* ) ) );
199 connect( p.data(), SIGNAL(stateChanged( EditPatch* )), this, SLOT( editStateChanged( EditPatch* ) ) );
200 m_editing << p;
201 return p;
204 void PatchesManager::slotEditPatch() {
205 showEditDialog( selectedPatch(), true );
208 void PatchesManager::slotRemovePatch() {
209 LocalPatchSourcePointer p = selectedPatch();
211 if ( p )
212 m_config.patchSources.remove( p );
214 guiUpdatePatchesList();
217 void PatchesManager::slotCloseManagement() {}
219 void PatchesManager::slotManagementFinished( int /*result*/ ) {
220 m_manageDlg = 0;
223 LocalPatchSourcePointer PatchesManager::selectedPatch() {
224 if ( !m_manageDlg )
225 return LocalPatchSourcePointer();
226 QModelIndex i = m_managePatches.patchesList->currentIndex();
227 if ( i.isValid() )
228 i = m_patchesModel->index( i.row(), 0 );
230 if ( i.isValid() ) {
231 QVariant v = m_patchesModel->data( i, Qt::UserRole );
232 if ( v.canConvert<LocalPatchSourcePointer>() ) {
233 return v.value<LocalPatchSourcePointer>();
234 } else {
235 log( "bad data in patches-list" );
239 return LocalPatchSourcePointer();
242 ///The following three functions are nearly the same, maybe they should be put together
243 void PatchesManager::slotApplyPatch() {
244 try {
245 QAction * act = qobject_cast<QAction*>( sender() );
246 if ( !act )
247 throw "no action";
249 QVariant v = act->data();
250 if ( !v.canConvert<LocalPatchSourcePointer>() )
251 throw "cannot convert to patch-source";
253 LocalPatchSourcePointer::Locked lpatch = v.value<LocalPatchSourcePointer>();
254 if ( !lpatch )
255 throw "could not lock patch-source";
257 UserPointer::Locked user = lpatch->user();
258 if ( !user )
259 throw "the patch has no associated user";
260 if ( !user->online() )
261 throw "the user is not online";
263 SessionPointer::Locked lsession = user->online().session();
264 if ( !lsession )
265 throw "the session could not be locked";
267 SafeSharedPtr<PatchRequestMessage>::Locked mp = new PatchRequestMessage( globalMessageTypeSet(), lpatch, m_teamwork, PatchRequestData::Apply );
268 lsession->send( mp );
269 m_teamwork->addMessageToList( ( PatchRequestMessage* ) mp );
270 } catch ( const char * str ) {
271 log( QString( "error in slotApplyPatch: " ) + str, Error );
275 void PatchesManager::slotDownloadPatch() {
276 try {
277 QAction * act = qobject_cast<QAction*>( sender() );
278 if ( !act )
279 throw "no action";
281 QVariant v = act->data();
282 if ( !v.canConvert<LocalPatchSourcePointer>() )
283 throw "cannot convert to patch-source";
285 LocalPatchSourcePointer::Locked lpatch = v.value<LocalPatchSourcePointer>();
286 if ( !lpatch )
287 throw "could not lock patch-source";
289 UserPointer::Locked user = lpatch->user();
290 if ( !user )
291 throw "the patch has no associated user";
292 if ( !user->online() )
293 throw "the user is not online";
295 SessionPointer session = user->online().session();
296 if ( !session )
297 throw "the session could not be acquired";
299 MessagePointer::Locked mp = new PatchRequestMessage( globalMessageTypeSet(), lpatch, m_teamwork, PatchRequestData::Download );
300 session.unsafe() ->send( mp );
301 m_teamwork->addMessageToList( mp );
302 } catch ( const char * str ) {
303 log( QString( "error in slotDownloadPatch: " ) + str, Error );
307 void PatchesManager::slotShowPatch() {
308 try {
309 QAction * act = qobject_cast<QAction*>( sender() );
310 if ( !act )
311 throw "no action";
313 QVariant v = act->data();
314 if ( !v.canConvert<LocalPatchSourcePointer>() )
315 throw "cannot convert to patch-source";
317 LocalPatchSourcePointer::Locked lpatch = v.value<LocalPatchSourcePointer>();
318 if ( !lpatch )
319 throw "could not lock patch-source";
321 UserPointer::Locked user = lpatch->user();
322 if ( !user )
323 throw "the patch has no associated user";
324 if ( !user->online() )
325 throw "the user is not online";
327 SessionPointer session = user->online().session();
328 if ( !session )
329 throw "the session could not be acquired";
331 MessagePointer::Locked mp = new PatchRequestMessage( globalMessageTypeSet(), lpatch, m_teamwork );
332 session.unsafe() ->send( mp );
333 m_teamwork->addMessageToList( mp );
334 } catch ( const char * str ) {
335 log( QString( "error in slotShowPatch: " ) + str, Error );
339 void PatchesManager::slotShowPatchInfo() {
340 try {
341 QAction * act = qobject_cast<QAction*>( sender() );
342 if ( !act )
343 throw "no action";
345 QVariant v = act->data();
346 if ( !v.canConvert<LocalPatchSourcePointer>() )
347 throw "cannot convert to patch-source";
349 showPatchInfo( v.value<LocalPatchSourcePointer>(), false );
350 } catch ( const char * str ) {
351 log( QString( "error in slotShowPatchInfo: " ) + str, Error );
355 void PatchesManager::processMessage( PatchesManagerMessagePointer msg ) {
356 log( "dispatching message", Debug );
357 PatchesManagerMessagePointer::Locked l = msg;
358 if ( l ) {
359 dispatcher_( l );
360 } else {
361 log( "PatchesManager could not lock a message-pointer", Error );
365 int PatchesManager::receiveMessage( MessageInterface* msg ) {
366 m_teamwork->log( QString( "PatchesManager got unknown message of type " ) + msg->name() );
367 return 0;
370 int PatchesManager::receiveMessage( PatchMessage* msg ) {
371 try {
372 SafeSharedPtr<PatchRequestMessage>::Locked request = msg->info().replyToMessage().cast<PatchRequestMessage>();
373 if ( !request )
374 throw QString( "got unrequested patch-message, or could not lock patch-request" );
376 LocalPatchSourcePointer::Locked patchInfo = request->request(); ///For security, information like apply-command etc. are cached
377 if ( !patchInfo )
378 throw QString( "could not get or lock patch-information" );
380 LocalPatchSourcePointer::Locked patchInfoNew = msg->patch(); ///For security, information like apply-command etc. are cached
381 if ( !patchInfo )
382 throw QString( "could not get or lock patch-information" );
384 if ( !( patchInfoNew->identity() == request->patchIdentity() ) )
385 throw QString( "identity of received patch does not match the identity of the corresponding request" );
387 KMimeType::Ptr mime = KMimeType::mimeType( ~patchInfo->type );
388 if ( !mime )
389 throw QString( "Error in kdelibs: could not create mime-type" ); ///according to the documentation this should never happen, but it does
391 QString userName;
392 if( msg->info().session().cast<FakeSession>() )
393 userName = "local";
394 else
395 userName = userNameFromSession( msg->info().session() );
396 QString fileName = ~patchInfo->name;
397 if( !patchInfo->filename.empty() )
398 fileName += "_" + QFileInfo(~patchInfo->filename).fileName();
399 else
400 fileName += getDefaultExtension( mime->patterns() );
402 log( QString( "creating file for patch: %1" ).arg( fileName ), Debug );
404 KUrl filePath = TeamworkFolderManager::createUniqueFile( "patches/"+userName, fileName );
407 ///@todo make this work with remove files
408 QFile file( filePath.toLocalFile() );
410 file.open( QIODevice::WriteOnly );
411 if ( !file.isOpen() )
412 throw QString( "could not open %1" ).arg( filePath.prettyUrl() );
414 file.write( msg->data() );
415 log( QString( "writing patch of size %1 to %2" ).arg( msg->data().size() ).arg( filePath.prettyUrl() ), Debug );
417 if ( request->requestType() == PatchRequestData::View )
418 TeamworkFolderManager::registerTempItem( filePath );
420 switch ( request->requestType() ) {
421 case PatchRequestData::Apply: {
422 ///Apply the patch to the local tree
423 if ( patchFromIdentity( patchInfo->identity() ) )
424 throw QString( "there already is another local patch with identity \"%1\"" ).arg( ~patchInfo->identity().desc() );
426 //LocalPatchSourcePointer::Locked newPatchInfo( patchInfo );//new LocalPatchSource( *patchInfo ) );
427 if( !hasPatch( patchInfo ) ) {
428 ///@todo make work with remote files
429 patchInfo->setFileName( ~filePath.toLocalFile() );
430 addPatch( patchInfo);
433 EditPatch* p = showEditDialog( patchInfo, true );
434 if( !p ) throw QString( "cannot edit received patch" );
435 ///@todo ...
436 p->apply( false, filePath.toLocalFile() );
438 guiUpdatePatchesList();
440 break;
441 case PatchRequestData::Download: {
442 if ( patchFromIdentity( patchInfo->identity() ) )
443 throw QString( "there already is another local patch with identity \"%1\"" ).arg( ~patchInfo->identity().desc() );
444 ///Store the patch locally
445 LocalPatchSourcePointer::Locked newPatchInfo( new LocalPatchSource( *patchInfo ) );
446 ///@todo make work with remote files
447 newPatchInfo->setFileName( ~filePath.toLocalFile() );
448 m_config.patchSources.push_back( newPatchInfo );
449 guiUpdatePatchesList();
451 break;
452 case PatchRequestData::View: {
453 ///Show the patch
455 ///@todo where has KDevDiffFrontend gone? When will it be back?
456 // if ( patchInfo->type == "text/x-diff" ) {
457 // QString str = msg->data();
459 // KDevDiffFrontend *df = KDevTeamworkPlugin::staticCore()->pluginController()->pluginForExtension<KDevDiffFrontend>( "KDevelop/DiffFrontend" );
460 // if ( df ) {
461 // df->showDiff( str );
462 // return 1;
463 // } else {
464 // log( "no diff-interface available!", Error );
465 // }
466 // }
468 if( !KDevTeamworkPlugin::staticDocumentController()->openDocument( filePath, KTextEditor::Cursor()) ) {
469 log( QString( "could not open %1 with the document-controller" ).arg( filePath.prettyUrl() ), Warning );
471 auto_ptr<KOpenWithDialog> d( new KOpenWithDialog( ~patchInfo->type, "" ) );
473 if ( d->exec() == QDialog::Accepted ) {
474 QString app = d->text();
475 if ( !app.isEmpty() ) {
476 ///@todo How to run this in a better way, so that .desktop-files are accepted etc.?
477 // Use KRun, of course!
478 KProcess proc;
479 proc << app;
481 proc << fileName;
482 proc.startDetached();
483 } else {
484 log( "no application was chosen for opening " + fileName, Warning );
486 } else {
487 throw QString( "open-with dialog was closed" );
489 d.reset(0);
491 /* ///Open with KRun instead
492 if( KRun::runUrl( KUrl(filePath), ~patchInfo->type, KDevTeamworkPlugin::staticCore()->uiController()->activeMainWindow() ) == 0 ) {
493 log( QString( "Failed to open %1 with an application" ).arg( filePath ), Warning );
496 ///Find an application that is able to open the selected mime-type
498 QString tempFileName;
499 KTempFile tempFile( QString(), getDefaultExtension( mime->patterns() ) );
501 if ( tempFile.status() != 0 ) {
502 throw QString( "could not create temporary file" );
503 } else {
504 tempFileName = tempFile.name();
505 log( "name of the temp-file: " + tempFileName, Debug );
506 QDataStream* stream = tempFile.dataStream();
507 if ( stream ) {
508 *stream << msg->data();
509 if ( tempFile.status() != 0 )
510 throw QString( "writing to the temporary file failed" );
511 } else {
512 throw QString( "could not get stream" );
516 if ( !mime->isValid() )
517 throw "service-type is not valid, file: " + tempFileName;
519 QString app;
521 #ifdef USE_KOPENWITHDLG
523 auto_ptr<KOpenWithDlg> d = new KOpenWithDlg( ~patchInfo->type, "" );
525 if ( d->exec() == QDialog::Accepted ) {
526 app = d->text();
527 } else {
528 throw QString( "open-with dialog was closed" );
530 d = 0;
531 #else
533 KService::Ptr service = KMimeTypeTrader::self() ->preferredService( ~patchInfo->type , "KPlugins/ReadOnlyPlugin" );
534 if ( !service )
535 throw QString( "could not get a service that can handle " + ~patchInfo->type );
537 app = service->exec();
539 #endif
541 if ( !app.isEmpty() ) {
542 KProcess proc;
543 proc << app;
545 proc << tempFileName;
546 proc.startDetached();
547 } else {
548 log( "no application was chosen for opening " + tempFileName, Warning );
551 break;
553 } catch ( const QString & str ) {
554 log( QString( "dispatch of patch-message from %1 failed: " ).arg( userFromSession( msg->info().session() ) ) + str , Error );
557 return 0;
560 LocalPatchSourcePointer::Locked PatchesManager::patchFromIdentity( const LocalPatchSource::Identity& ident ) {
561 for ( list<LocalPatchSourcePointer>::iterator it = m_config.patchSources.begin(); it != m_config.patchSources.end(); ++it ) {
562 LocalPatchSourcePointer::Locked l = ( *it );
563 if ( l ) {
564 if ( ident == l->identity() ) {
565 return l;
567 } else {
568 log( "could not lock patch-source", Warning );
571 return 0;
574 LocalPatchSourcePointer PatchesManager::merge( const QString& name, const QList<LocalPatchSourcePointer>& patches ) {
575 try {
576 LocalPatchSourcePointer::Locked lp = new LocalPatchSource();
578 foreach( LocalPatchSourcePointer patch, patches ) {
579 LocalPatchSourcePointer::Locked l = patch;
580 if ( !l )
581 throw "could not lock patch-source";
582 if ( l->type != "text/x-diff" )
583 throw "a mime-type is not text/x-diff, only that types can be merged";
584 if(l->userIdentity) lp->userIdentity = l->userIdentity;
588 QString user = "local";
589 if( lp->userIdentity )
590 user = ~lp->userIdentity.name();
592 KUrl file = TeamworkFolderManager::createUniqueFile( "patches/"+user, name+".diff" );
594 ///@todo make this work with remove Urls
595 QFile target( file.toLocalFile() );
596 if ( !target.open( QIODevice::WriteOnly ) )
597 throw QString( "could not open file %1" ).arg( file.prettyUrl() );
599 foreach( LocalPatchSourcePointer patch, patches ) {
600 LocalPatchSourcePointer::Locked l = patch;
601 if ( !l )
602 throw "could not lock patch-source";
606 KUrl u = TeamworkFolderManager::teamworkAbsolute( ~l->filename, "patches" );
607 ///@todo make this work with remove Urls
608 QFile f( u.toLocalFile() );
609 if ( !f.open( QIODevice::ReadOnly ) )
610 throw QString( "could not open file %1" ).arg( u.prettyUrl() );
612 target.write( f.readAll() );
613 target.write( "\n", 1 );
616 lp->name = ~name;
617 lp->filename = ~TeamworkFolderManager::teamworkRelative( file, "patches" );
618 lp->type = "text/x-diff";
620 return lp;
621 } catch ( const char * str ) {
622 log( QString( "error while trying to merge patches: %1" ).arg( str ) , Error );
623 } catch ( const QString & str ) {
624 log( QString( "error while trying to merge patches: %1" ).arg( str ) , Error );
626 return 0;
630 int PatchesManager::receiveMessage( PatchRequestMessage* msg ) {
631 ///Send the patch to the target.
632 LocalPatchSourcePointer::Locked patch = patchFromIdentity( msg->patchIdentity() );
634 bool overrideAccess = false;
635 if( msg->info().session().cast<FakeSession>() ) overrideAccess = true;
637 LocalPatchSourcePointer::Locked lpatch = patch;
638 if ( patch && lpatch ) {
639 if( !overrideAccess ) {
640 switch ( lpatch->access ) {
641 case Public:
642 break;
643 case ConnectedOnly: {
644 if ( !m_teamwork->collaborationManager() ->isCollaborating( userFromSession( msg->info().session() ) ) ) {
645 log( "not sending patch " + ~patch->name + " to " + userNameFromSession( msg->info().session() ) + " because the user is not collaborating" );
646 globalMessageSendHelper().sendReply<KDevSystemMessage>( msg, KDevSystemMessage::ActionFailed, "access only for collaborating users" );
647 return 1;
650 break;
651 case Ask:
652 m_teamwork->addMessageToList( msg );
653 return 1;
654 break;
655 default: {
656 log( "not sending patch " + ~patch->name + " to " + userNameFromSession( msg->info().session() ) + " because the patch is private" );
657 globalMessageSendHelper().sendReply<KDevSystemMessage>( msg, KDevSystemMessage::ActionFailed, "the patch is private" );
658 return 1;
660 break;
663 log( "sending patch " + ~patch->name + " to " + userNameFromSession( msg->info().session() ) );
664 globalMessageSendHelper().sendReply<PatchMessage>( msg, ( LocalPatchSourcePointer ) patch, m_teamwork->logger() );
665 } else {
666 globalMessageSendHelper().sendReply<KDevSystemMessage>( msg, KDevSystemMessage::ActionFailed, "no fitting patch available, or patch could not be locked" );
667 log( "got a patch-request, but the requested patch could not be found or could not be locked", Warning );
670 return 1;
673 int PatchesManager::receiveMessage( PatchesListMessage* msg ) {
674 ///Give the list to the GUI or whoever was waiting for it
675 m_teamwork->handlePatchesList( msg );
676 return 0;
679 int PatchesManager::receiveMessage( PatchesManagerMessage* msg ) {
680 if ( !msg->isDerived() ) {
681 switch ( msg->message() ) {
682 case PatchesManagerMessage::None:
683 break;
684 case PatchesManagerMessage::GetPatchesList: {
685 SessionPointer::Locked l = msg->info().session();
686 if ( l && l->isRunning() ) {
687 l->send( new PatchesListMessage( globalMessageTypeSet(), m_config.patchSources ) );
689 UserPointer::Locked pl = l->safeUser();
690 if ( pl ) {
691 log( QString( "sending patches-list to " ) + pl->name().c_str() );
692 } else {
693 log( "sent patches-list through anonymous session" );
696 } else {
697 log( QString( "could not answer a patches-list-request" ) );
700 break;
702 } else {
703 log( QString( "could not handle a PatchesManagerMessage of real type " ) + msg->name() );
705 return 0;
708 void PatchesManager::guiUpdatePatchesList() {
709 if ( !m_manageDlg )
710 return ;
712 m_patchesModel->clear();
713 m_patchesModel->insertColumns( 0, 5 );
715 m_patchesModel->setHeaderData( 0, Qt::Horizontal, QString( "Name" ) );
716 m_patchesModel->setHeaderData( 1, Qt::Horizontal, QString( "File/Command" ) );
717 m_patchesModel->setHeaderData( 2, Qt::Horizontal, QString( "Access" ) );
718 m_patchesModel->setHeaderData( 3, Qt::Horizontal, QString( "Type" ) );
719 m_patchesModel->setHeaderData( 4, Qt::Horizontal, QString( "State" ) );
721 for ( std::list<LocalPatchSourcePointer>::iterator it = m_config.patchSources.begin(); it != m_config.patchSources.end(); ++it ) {
722 LocalPatchSourcePointer::Locked l = *it;
723 m_patchesModel->insertRow( 0 );
724 if ( l ) {
725 m_patchesModel->setData( m_patchesModel->index( 0, 0 ), ~l->name, Qt::DisplayRole );
726 m_patchesModel->setData( m_patchesModel->index( 0, 1 ), ~( l->filename + l->command ), Qt::DisplayRole );
727 m_patchesModel->setData( m_patchesModel->index( 0, 2 ), ~l->accessAsString(), Qt::DisplayRole );
728 m_patchesModel->setData( m_patchesModel->index( 0, 3 ), ~l->type, Qt::DisplayRole );
729 m_patchesModel->setData( m_patchesModel->index( 0, 4 ), ~l->stateAsString(), Qt::DisplayRole );
730 } else {
731 m_patchesModel->setData( m_patchesModel->index( 0, 0 ), "lock failed", Qt::DisplayRole );
733 QVariant v;
734 v.setValue( *it );
736 m_patchesModel->setData( m_patchesModel->index( 0, 0 ), v, Qt::UserRole );
739 m_patchesModel->sort( 4 );
742 PatchesManager::~PatchesManager() {
745 QIcon LocalPatchSource::getIcon( IconCache& icons ) {
746 return icons( "patch" );
749 void PatchesManager::slotShowRequestedPatch() {
750 try {
751 QAction * act = qobject_cast<QAction*>( sender() );
752 if ( !act )
753 throw "no action";
755 QVariant userVar = act->data();
756 if ( !userVar.canConvert<MessagePointer>() )
757 throw "cannot convert to message-pointer";
759 SafeSharedPtr<PatchRequestMessage>::Locked msg = userVar.value<MessagePointer>().cast<PatchRequestMessage>();
760 if ( !msg )
761 throw "cannot lock/cast message";
763 LocalPatchSourcePointer::Locked lpatch = patchFromIdentity( msg->patchIdentity() );
764 if ( !lpatch )
765 throw "could not find the requested patch";
767 showEditDialog( lpatch, false );
769 } catch ( const char * str ) {
770 log( QString( "slotShowRequestedPatch failed: " ) + str );
774 void PatchesManager::slotAllowPatch() {
775 try {
776 QAction * act = qobject_cast<QAction*>( sender() );
777 if ( !act )
778 throw "no action";
780 QVariant userVar = act->data();
782 if ( !userVar.canConvert<MessagePointer>() )
783 throw "cannot convert to message-pointer";
785 SafeSharedPtr<PatchRequestMessage>::Locked msg = userVar.value<MessagePointer>().cast<PatchRequestMessage>();
786 if ( !msg )
787 throw "cannot lock/cast message";
789 LocalPatchSourcePointer::Locked lpatch = patchFromIdentity( msg->patchIdentity() );
790 if ( !lpatch )
791 throw "could not find the requested patch";
793 log( "sending patch " + ~lpatch->name + " to " + userNameFromSession( msg->info().session() ) );
794 globalMessageSendHelper().sendReply<PatchMessage>( msg, ( LocalPatchSourcePointer ) lpatch, m_teamwork->logger() );
796 msg->setStatus( PatchRequestData::Accepted );
798 m_teamwork->messageManager() ->updateMessage( msg.data() );
799 } catch ( const char * str ) {
800 log( QString( "slotAllowPatch failed: " ) + str );
805 void PatchesManager::slotDenyPatch() {
806 try {
807 QAction * act = qobject_cast<QAction*>( sender() );
808 if ( !act )
809 throw "no action";
811 QVariant v = act->data();
813 if ( v.canConvert<MessagePointer>() )
814 throw "cannot convert to message-pointer";
816 SafeSharedPtr<PatchRequestMessage>::Locked msg = v.value<MessagePointer>().cast<PatchRequestMessage>();
817 if ( !msg )
818 throw "cannot lock/cast message";
820 log( "denying patch to " + userNameFromSession( msg->info().session() ) );
821 globalMessageSendHelper().sendReply<KDevSystemMessage>( msg, KDevSystemMessage::ActionFailed, "access denied" );
823 msg->setStatus( PatchRequestData::Denied );
825 m_teamwork->messageManager() ->updateMessage( msg.data() );
826 } catch ( const char * str ) {
827 log( QString( "slotAllowPatch failed: " ) + str );
831 void PatchesManager::restorePartialProjectSession( const QDomElement* /*el*/ ) {
832 /*try {
833 xmlDeserializeFromElementItem( el, "PatchesManager", NVP( m_config ) );
834 } catch ( const QString & str ) {
835 log( "could not restore the patch-information: " + str, Error );
839 void PatchesManager::savePartialProjectSession( QDomElement* /*el*/ ) {
840 /*try {
841 xmlSerializeToElementItem( el, "PatchesManager", NVP( m_config ) );
842 } catch ( const QString & str ) {
843 log( "could not save the patch-information: " + str, Error );
847 KDevTeamwork* PatchesManager::teamwork() {
848 return m_teamwork;
851 void PatchesManager::fillDeveloperActions( const QModelIndex& index, QMenu* menu ) {
852 QVariant v = index.model() ->data( index, Qt::UserRole );
854 m_showPatchInfoAction->setData( v );
855 m_showPatchAction->setData( v );
856 m_downloadPatchAction->setData( v );
857 m_applyPatchAction->setData( v );
859 menu->addAction( m_showPatchInfoAction );
860 menu->addAction( m_showPatchAction );
861 menu->addAction( m_downloadPatchAction );
862 menu->addAction( m_applyPatchAction );
865 void PatchRequestData::fillContextMenu( QMenu* menu, KDevTeamwork* teamwork ) {
866 QVariant v;
868 v.setValue( MessagePointer( selfMessage() ) );
869 teamwork->patchesManager() ->m_denyPatchAction->setData( v );
870 menu->addAction( teamwork->patchesManager() ->m_denyPatchAction );
872 teamwork->patchesManager() ->m_allowPatchAction->setData( v );
873 menu->addAction( teamwork->patchesManager() ->m_allowPatchAction );
875 teamwork->patchesManager() ->m_showRequestedPatchAction->setData( v );
876 menu->addAction( teamwork->patchesManager() ->m_showRequestedPatchAction );
879 QStringList splitArgs( const QString& str );
881 LocalPatchSource::State PatchesManager::determineState( const LocalPatchSourcePointer& patch ) {
882 LocalPatchSourcePointer::Locked lpatch = patch;
883 if( !lpatch ) {
884 log( "determineState(..) could not lock patch", Error );
885 return LocalPatchSource::Unknown;
887 try {
888 if( lpatch->type != "text/x-diff" )
889 throw "state can only be determined for files of type \"text/x-diff\"";
890 if( lpatch->filename.empty() )
891 throw "state can only be determined for file-patches";
893 /* KUrl fileUrl = projectDir();
894 if( (~lpatch->filename).startsWith( fileUrl.toLocalFile() ) )
895 fileUrl = KUrl( ~lpatch->filename );
896 else
897 fileUrl.addPath( ~lpatch->filename );*/
898 KUrl fileUrl = ~lpatch->filename;
900 if( lpatch->patchTool() != "patch" || lpatch->patchTool(true) != "patch" ) throw QString( "cannot determine state with other tool than patch: \"%1\" \"%2\"").arg(~lpatch->patchTool()).arg(~lpatch->patchTool(true)) ;
903 K3Process proc;
904 ///@todo does not work with remove directories
905 proc.setWorkingDirectory( TeamworkFolderManager::workspaceDirectory().toLocalFile() );
906 // proc << ~lpatch->patchTool();
907 bool hadFile = false;
908 QString applyParams = ~lpatch->patchParams(false);
909 if( applyParams.contains( "$FILE" ) )
910 hadFile = true;
911 //applyParams.replace( "$FILE", fileUrl.toLocalFile() );
912 proc.setEnvironment( "FILE", fileUrl.toLocalFile() );
913 //proc << "--dry-run" << "-s" << "-f";
914 QString cmd = ~lpatch->patchTool() + " --dry-run " + applyParams + " -s -f";
915 if( !hadFile ) {
916 //proc << "-i" << fileUrl.toLocalFile();
917 cmd += " -i " + fileUrl.toLocalFile();
919 proc << splitArgs( cmd );
921 log( "determineState(...) calling " + cmd, Debug );
923 if( !proc.start( K3Process::Block ) ) throw "could not start process";
924 if( !proc.normalExit() ) throw "process did not exit normally";
925 log( QString( "exit-status: %1").arg( proc.exitStatus() ), Debug );
926 if( proc.exitStatus() == 0 ) {
927 lpatch->state = LocalPatchSource::NotApplied;
928 return LocalPatchSource::NotApplied;
933 K3Process proc;
934 ///@todo does not work with remove directories
935 proc.setWorkingDirectory( TeamworkFolderManager::workspaceDirectory().toLocalFile() );
936 //proc << ~lpatch->patchTool(true);
938 bool hadFile = false;
939 QString applyParams = ~lpatch->patchParams(true);
940 if( applyParams.contains( "$FILE" ) ) {
941 hadFile = true;
943 proc.setEnvironment( "FILE", fileUrl.toLocalFile() );
945 //proc << "--dry-run" << "-s" << "-f" << applyParams;
946 QString cmd = ~lpatch->patchTool(true) + " --dry-run -s -f " + applyParams;
947 if( !hadFile ) {
948 //proc << "-i" << fileUrl.toLocalFile();
949 cmd += " -i " + fileUrl.toLocalFile();
951 proc << splitArgs( cmd );
952 log( "determineState(...) calling " + cmd, Debug );
954 if( !proc.start( K3Process::Block ) ) throw "could not start patch-process";
955 if( !proc.normalExit() ) throw "process did not exit normally";
956 log( QString( "exit-status: %1").arg( proc.exitStatus() ), Debug );
957 if( proc.exitStatus() == 0 ) {
958 lpatch->state = LocalPatchSource::Applied;
959 return LocalPatchSource::Applied;
962 } catch( const QString& str ) {
963 log( "Error in determineState: " + str, Error );
964 } catch( const char* str ) {
965 log( QString("Error in determineState: ") + str, Error );
967 lpatch->state = LocalPatchSource::Unknown;
968 return LocalPatchSource::Unknown;
971 void PatchesManager::save() {
972 try {
973 KUrl fileName = TeamworkFolderManager::teamworkAbsolute( "patches.database" );
974 ///@todo does not work with remote files
975 if( !fileName.isLocalFile() ) throw QString( "file is not a local Url: %1" ).arg( fileName.prettyUrl() );
977 std::ofstream file(fileName.toLocalFile().toLocal8Bit(), ios_base::out | ios_base::binary );
978 if( !file.good() ) throw "could not open " + fileName.prettyUrl() + " for writing";
979 boost::archive::polymorphic_xml_oarchive arch( file );
980 arch << NVP(m_config);
981 } catch ( std::exception & exc ) {
982 log( QString("save(): exception occurred while serialization: %1").arg( exc.what() ), Error );
983 } catch( const char* str ) {
984 log( QString("save(): %1").arg( str ), Error );
985 } catch( const QString& str ) {
986 log( QString( "save(): %1").arg( str ), Error );
990 void PatchesManager::load() {
991 try {
992 KUrl fileName = TeamworkFolderManager::teamworkAbsolute( "patches.database" );
993 ///@todo does not work with remote files
994 if( !fileName.isLocalFile() ) throw QString( "file is not a local Url: %1" ).arg( fileName.prettyUrl() );
996 std::ifstream file(fileName.toLocalFile().toLocal8Bit(), ios_base::binary );
997 if( !file.good() ) throw "could not open " + fileName.prettyUrl() + " for reading";
998 boost::archive::polymorphic_xml_iarchive arch( file );
999 arch >> NVP(m_config);
1000 } catch ( std::exception & exc ) {
1001 log( QString("load(): exception occurred while serialization: %1").arg( exc.what() ), Error );
1002 } catch( const char* str ) {
1003 log( QString("load(): %1").arg( str ), Error );
1004 } catch( const QString& str ) {
1005 log( QString( "load(): %1").arg( str ), Error );
1007 guiUpdatePatchesList();
1010 #include "patchesmanager.moc"
1012 // kate: space-indent on; indent-width 2; tab-width 2; replace-tabs on