1 /***************************************************************************
2 Copyright 2006 David Nolden <david.nolden.kdevelop@art-master.de>
3 ***************************************************************************/
5 /***************************************************************************
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. *
12 ***************************************************************************/
14 #include "patchesmanager.h"
15 #include <boost/archive/polymorphic_xml_oarchive.hpp>
16 #include <boost/archive/polymorphic_xml_iarchive.hpp>
18 #include <QPersistentModelIndex>
23 #include <kmimetype.h>
24 #include <kmimetypetrader.h>
25 #include <kopenwithdialog.h>
26 #include <k3process.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
51 //krazy:excludeall=doublequote_chars
53 QString
userNameFromSession( const SessionPointer
& session
) {
54 UserPointer::Locked lu
= userFromSession( session
);
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() )
66 for ( QStringList::const_iterator it
= patterns
.begin(); it
!= patterns
.end(); ++it
) {
68 int i
= s
.lastIndexOf( "*." );
71 return s
.mid( i
+ 1 );
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
) ) );
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() ) );
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
) {
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
* ) ) );
204 void PatchesManager::slotEditPatch() {
205 showEditDialog( selectedPatch(), true );
208 void PatchesManager::slotRemovePatch() {
209 LocalPatchSourcePointer p
= selectedPatch();
212 m_config
.patchSources
.remove( p
);
214 guiUpdatePatchesList();
217 void PatchesManager::slotCloseManagement() {}
219 void PatchesManager::slotManagementFinished( int /*result*/ ) {
223 LocalPatchSourcePointer
PatchesManager::selectedPatch() {
225 return LocalPatchSourcePointer();
226 QModelIndex i
= m_managePatches
.patchesList
->currentIndex();
228 i
= m_patchesModel
->index( i
.row(), 0 );
231 QVariant v
= m_patchesModel
->data( i
, Qt::UserRole
);
232 if ( v
.canConvert
<LocalPatchSourcePointer
>() ) {
233 return v
.value
<LocalPatchSourcePointer
>();
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() {
245 QAction
* act
= qobject_cast
<QAction
*>( sender() );
249 QVariant v
= act
->data();
250 if ( !v
.canConvert
<LocalPatchSourcePointer
>() )
251 throw "cannot convert to patch-source";
253 LocalPatchSourcePointer::Locked lpatch
= v
.value
<LocalPatchSourcePointer
>();
255 throw "could not lock patch-source";
257 UserPointer::Locked user
= lpatch
->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();
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() {
277 QAction
* act
= qobject_cast
<QAction
*>( sender() );
281 QVariant v
= act
->data();
282 if ( !v
.canConvert
<LocalPatchSourcePointer
>() )
283 throw "cannot convert to patch-source";
285 LocalPatchSourcePointer::Locked lpatch
= v
.value
<LocalPatchSourcePointer
>();
287 throw "could not lock patch-source";
289 UserPointer::Locked user
= lpatch
->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();
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() {
309 QAction
* act
= qobject_cast
<QAction
*>( sender() );
313 QVariant v
= act
->data();
314 if ( !v
.canConvert
<LocalPatchSourcePointer
>() )
315 throw "cannot convert to patch-source";
317 LocalPatchSourcePointer::Locked lpatch
= v
.value
<LocalPatchSourcePointer
>();
319 throw "could not lock patch-source";
321 UserPointer::Locked user
= lpatch
->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();
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() {
341 QAction
* act
= qobject_cast
<QAction
*>( sender() );
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
;
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() );
370 int PatchesManager::receiveMessage( PatchMessage
* msg
) {
372 SafeSharedPtr
<PatchRequestMessage
>::Locked request
= msg
->info().replyToMessage().cast
<PatchRequestMessage
>();
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
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
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
);
389 throw QString( "Error in kdelibs: could not create mime-type" ); ///according to the documentation this should never happen, but it does
392 if( msg
->info().session().cast
<FakeSession
>() )
395 userName
= userNameFromSession( msg
->info().session() );
396 QString fileName
= ~patchInfo
->name
;
397 if( !patchInfo
->filename
.empty() )
398 fileName
+= "_" + QFileInfo(~patchInfo
->filename
).fileName();
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" );
436 p
->apply( false, filePath
.toLocalFile() );
438 guiUpdatePatchesList();
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();
452 case PatchRequestData::View
: {
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" );
461 // df->showDiff( str );
464 // log( "no diff-interface available!", Error );
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!
482 proc
.startDetached();
484 log( "no application was chosen for opening " + fileName
, Warning
);
487 throw QString( "open-with dialog was closed" );
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" );
504 tempFileName = tempFile.name();
505 log( "name of the temp-file: " + tempFileName, Debug );
506 QDataStream* stream = tempFile.dataStream();
508 *stream << msg->data();
509 if ( tempFile.status() != 0 )
510 throw QString( "writing to the temporary file failed" );
512 throw QString( "could not get stream" );
516 if ( !mime->isValid() )
517 throw "service-type is not valid, file: " + tempFileName;
521 #ifdef USE_KOPENWITHDLG
523 auto_ptr<KOpenWithDlg> d = new KOpenWithDlg( ~patchInfo->type, "" );
525 if ( d->exec() == QDialog::Accepted ) {
528 throw QString( "open-with dialog was closed" );
533 KService::Ptr service = KMimeTypeTrader::self() ->preferredService( ~patchInfo->type , "KPlugins/ReadOnlyPlugin" );
535 throw QString( "could not get a service that can handle " + ~patchInfo->type );
537 app = service->exec();
541 if ( !app.isEmpty() ) {
545 proc << tempFileName;
546 proc.startDetached();
548 log( "no application was chosen for opening " + tempFileName, Warning );
553 } catch ( const QString
& str
) {
554 log( QString( "dispatch of patch-message from %1 failed: " ).arg( userFromSession( msg
->info().session() ) ) + str
, Error
);
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
);
564 if ( ident
== l
->identity() ) {
568 log( "could not lock patch-source", Warning
);
574 LocalPatchSourcePointer
PatchesManager::merge( const QString
& name
, const QList
<LocalPatchSourcePointer
>& patches
) {
576 LocalPatchSourcePointer::Locked lp
= new LocalPatchSource();
578 foreach( LocalPatchSourcePointer patch
, patches
) {
579 LocalPatchSourcePointer::Locked l
= patch
;
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
;
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 );
617 lp
->filename
= ~TeamworkFolderManager::teamworkRelative( file
, "patches" );
618 lp
->type
= "text/x-diff";
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
);
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
) {
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" );
652 m_teamwork
->addMessageToList( msg
);
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" );
663 log( "sending patch " + ~patch
->name
+ " to " + userNameFromSession( msg
->info().session() ) );
664 globalMessageSendHelper().sendReply
<PatchMessage
>( msg
, ( LocalPatchSourcePointer
) patch
, m_teamwork
->logger() );
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
);
673 int PatchesManager::receiveMessage( PatchesListMessage
* msg
) {
674 ///Give the list to the GUI or whoever was waiting for it
675 m_teamwork
->handlePatchesList( msg
);
679 int PatchesManager::receiveMessage( PatchesManagerMessage
* msg
) {
680 if ( !msg
->isDerived() ) {
681 switch ( msg
->message() ) {
682 case PatchesManagerMessage::None
:
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();
691 log( QString( "sending patches-list to " ) + pl
->name().c_str() );
693 log( "sent patches-list through anonymous session" );
697 log( QString( "could not answer a patches-list-request" ) );
703 log( QString( "could not handle a PatchesManagerMessage of real type " ) + msg
->name() );
708 void PatchesManager::guiUpdatePatchesList() {
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 );
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
);
731 m_patchesModel
->setData( m_patchesModel
->index( 0, 0 ), "lock failed", Qt::DisplayRole
);
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() {
751 QAction
* act
= qobject_cast
<QAction
*>( sender() );
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
>();
761 throw "cannot lock/cast message";
763 LocalPatchSourcePointer::Locked lpatch
= patchFromIdentity( msg
->patchIdentity() );
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() {
776 QAction
* act
= qobject_cast
<QAction
*>( sender() );
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
>();
787 throw "cannot lock/cast message";
789 LocalPatchSourcePointer::Locked lpatch
= patchFromIdentity( msg
->patchIdentity() );
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() {
807 QAction
* act
= qobject_cast
<QAction
*>( sender() );
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
>();
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*/ ) {
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*/ ) {
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() {
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
) {
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
;
884 log( "determineState(..) could not lock patch", Error
);
885 return LocalPatchSource::Unknown
;
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 );
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)) ;
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" ) )
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";
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
;
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" ) ) {
943 proc
.setEnvironment( "FILE", fileUrl
.toLocalFile() );
945 //proc << "--dry-run" << "-s" << "-f" << applyParams;
946 QString cmd
= ~lpatch
->patchTool(true) + " --dry-run -s -f " + applyParams
;
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() {
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() {
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