diff options
Diffstat (limited to 'installerbuilder/libinstaller/qinstallercomponent.cpp')
-rw-r--r-- | installerbuilder/libinstaller/qinstallercomponent.cpp | 1103 |
1 files changed, 1103 insertions, 0 deletions
diff --git a/installerbuilder/libinstaller/qinstallercomponent.cpp b/installerbuilder/libinstaller/qinstallercomponent.cpp new file mode 100644 index 000000000..78fc94f84 --- /dev/null +++ b/installerbuilder/libinstaller/qinstallercomponent.cpp @@ -0,0 +1,1103 @@ +/************************************************************************** +** +** This file is part of Qt SDK** +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).* +** +** Contact: Nokia Corporation qt-info@nokia.com** +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception version +** 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you are unsure which license is appropriate for your use, please contact +** (qt-info@nokia.com). +** +**************************************************************************/ +#include "qinstallercomponent.h" +#include "qinstallerglobal.h" +#include "messageboxhandler.h" + +#include "common/errors.h" + +#include "lib7z_facade.h" + +#include <KDUpdater/Update> +#include <KDUpdater/UpdateOperation> +#include <KDUpdater/UpdateOperationFactory> + +#include <QApplication> +#include <QDesktopServices> +#include <QDir> +#include <QDirIterator> +#include <QHash> +#include <QMessageBox> +#include <QtCore/QPair> +#include <QScriptEngine> +#include <QScriptValue> +#include <QTextStream> +#include <QTranslator> +#include <QUiLoader> + +#include <memory> + +#include "common/utils.h" +#include "fsengineclient.h" + +using namespace QInstaller; + +/* +TRANSLATOR QInstaller::Component +*/ + +/*! + \class QInstaller::Component + Component describes a component within the installer. +*/ + +class QInstaller::Component::Private +{ + QInstaller::Component* const q; +public: + Private( Installer* installer, QInstaller::Component* qq ) + : q( qq ), + m_installer( installer ), + m_parent( 0 ), + m_offsetInInstaller( 0 ), + autoCreateOperations( true ), + operationsCreated( false ), + removeBeforeUpdate( true ), + enabled( true ), + isCheckedFromUpdater( false ), + m_newlyInstalled ( false ), + operationsCreatedSuccessfully( true ), + minimumProgressOperation(0), + m_licenseOperation(0) + { + } + + static QMap< const Component*, Qt::CheckState > cachedCheckStates; + + Installer *m_installer; + QHash<QString,QString> m_vars; + QList< Component* > m_components; + QList< KDUpdater::UpdateOperation* > operations; + + QList< QPair< QString, bool > > pathesForUninstallation; + + QMap< QString, QWidget* > userInterfaces; + + QUrl repositoryUrl; + QStringList downloadableArchives; + QStringList stopProcessForUpdateRequests; + + Component* m_parent; + + // filled before intaller runs + qint64 m_offsetInInstaller; + + bool autoCreateOperations; + bool operationsCreated; + + bool removeBeforeUpdate; + + bool enabled; + bool isCheckedFromUpdater; + + bool m_newlyInstalled; + + bool operationsCreatedSuccessfully; + + QScriptEngine scriptEngine; + QScriptValue scriptComponent; + + QHash< QString, bool > unexistingScriptMethods; + + void init(); + void setSelectedOnComponentList(const QList<Component*> &componentList, + bool selected, RunModes runMode, SelectMode selectMode); + KDUpdater::UpdateOperation* minimumProgressOperation; + + // < display name, < file name, file content > > + QHash<QString, QPair<QString, QString> > m_licenses; + KDUpdater::UpdateOperation *m_licenseOperation; +}; + +QMap< const Component*, Qt::CheckState > Component::Private::cachedCheckStates; + +void Component::Private::init() +{ + // register translation stuff + scriptEngine.installTranslatorFunctions(); + + // register QMessageBox::StandardButton enum in the script connection + registerMessageBox( &scriptEngine ); + + // register QDesktopServices in the script cennoction + QScriptValue desktopServices = scriptEngine.newArray(); + desktopServices.setProperty( QLatin1String( "DesktopLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::DesktopLocation ) ) ); + desktopServices.setProperty( QLatin1String( "DocumentsLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::DocumentsLocation ) ) ); + desktopServices.setProperty( QLatin1String( "FontsLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::FontsLocation ) ) ); + desktopServices.setProperty( QLatin1String( "ApplicationsLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::ApplicationsLocation ) ) ); + desktopServices.setProperty( QLatin1String( "MusicLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::MusicLocation ) ) ); + desktopServices.setProperty( QLatin1String( "MoviesLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::MoviesLocation ) ) ); + desktopServices.setProperty( QLatin1String( "PicturesLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::PicturesLocation ) ) ); + desktopServices.setProperty( QLatin1String( "TempLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::TempLocation ) ) ); + desktopServices.setProperty( QLatin1String( "HomeLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::HomeLocation ) ) ); + desktopServices.setProperty( QLatin1String( "DataLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::DataLocation ) ) ); + desktopServices.setProperty( QLatin1String( "CacheLocation" ), scriptEngine.newVariant( static_cast< int >( QDesktopServices::CacheLocation ) ) ); + + desktopServices.setProperty( QLatin1String( "openUrl" ), scriptEngine.newFunction( qDesktopServicesOpenUrl ) ); + desktopServices.setProperty( QLatin1String( "displayName" ), scriptEngine.newFunction( qDesktopServicesDisplayName ) ); + desktopServices.setProperty( QLatin1String( "storageLocation" ), scriptEngine.newFunction( qDesktopServicesStorageLocation ) ); + scriptEngine.globalObject().setProperty( QLatin1String( "QDesktopServices" ), desktopServices ); + + // register ::WizardPage enum in the script connection + QScriptValue qinstaller = scriptEngine.newArray(); + qinstaller.setProperty( QLatin1String( "Introduction" ), scriptEngine.newVariant( static_cast< int >( Installer::Introduction ) ) ); + qinstaller.setProperty( QLatin1String( "LicenseCheck" ), scriptEngine.newVariant( static_cast< int >( Installer::LicenseCheck ) ) ); + qinstaller.setProperty( QLatin1String( "TargetDirectory" ), scriptEngine.newVariant( static_cast< int >( Installer::TargetDirectory ) ) ); + qinstaller.setProperty( QLatin1String( "ComponentSelection" ), scriptEngine.newVariant( static_cast< int >( Installer::ComponentSelection ) ) ); + qinstaller.setProperty( QLatin1String( "StartMenuSelection" ), scriptEngine.newVariant( static_cast< int >( Installer::StartMenuSelection ) ) ); + qinstaller.setProperty( QLatin1String( "ReadyForInstallation" ), scriptEngine.newVariant( static_cast< int >( Installer::ReadyForInstallation ) ) ); + qinstaller.setProperty( QLatin1String( "PerformInstallation" ), scriptEngine.newVariant( static_cast< int >( Installer::PerformInstallation ) ) ); + qinstaller.setProperty( QLatin1String( "InstallationFinished" ), scriptEngine.newVariant( static_cast< int >( Installer::InstallationFinished ) ) ); + qinstaller.setProperty( QLatin1String( "End" ), scriptEngine.newVariant( static_cast< int >( Installer::End ) ) ); + + // register ::Status enum in the script connection + qinstaller.setProperty( QLatin1String( "InstallerUnfinished" ), scriptEngine.newVariant( static_cast< int >( Installer::InstallerUnfinished ) ) ); + qinstaller.setProperty( QLatin1String( "InstallerCanceledByUser" ), scriptEngine.newVariant( static_cast< int >( Installer::InstallerCanceledByUser ) ) ); + qinstaller.setProperty( QLatin1String( "InstallerFailed" ), scriptEngine.newVariant( static_cast< int >( Installer::InstallerFailed ) ) ); + qinstaller.setProperty( QLatin1String( "InstallerSucceeded" ), scriptEngine.newVariant( static_cast< int >( Installer::InstallerSucceeded ) ) ); + + scriptEngine.globalObject().setProperty( QLatin1String( "QInstaller" ), qinstaller ); + + scriptEngine.globalObject().setProperty( QLatin1String( "component" ), scriptEngine.newQObject( q ) ); + QScriptValue installerObject = scriptEngine.newQObject( m_installer ); + installerObject.setProperty( QLatin1String( "componentByName" ), scriptEngine.newFunction( qInstallerComponentByName, 1 ) ); + scriptEngine.globalObject().setProperty( QLatin1String( "installer" ), installerObject ); +} + +void Component::Private::setSelectedOnComponentList(const QList<Component*> &componentList, + bool selected, RunModes runMode, SelectMode selectMode) +{ + for( QList< Component* >::const_iterator it = componentList.begin(); it != componentList.end(); ++it ) + { + Component* const comp = *it; + if( !comp->isSelected( runMode ) ) + comp->setSelected( selected, runMode, selectMode ); + } +} + +/*! + Constructor. Creates a new Component inside of \a installer. +*/ +Component::Component(Installer *installer) + : d(new Component::Private( installer, this ) ) +{ + d->init(); +} + +Component::Component( KDUpdater::Update* update, Installer* installer ) + : d( new Private( installer, this ) ) { + Q_ASSERT( update ); + d->init(); + setValue( QLatin1String( "Name" ), update->data( QLatin1String( "Name" ) ).toString() ); + setValue( QLatin1String( "DisplayName" ), update->data( QLatin1String( "DisplayName" ) ).toString() ); + setValue( QLatin1String( "Description" ), update->data( QLatin1String( "Description" ) ).toString() ); + setValue( QLatin1String( "UncompressedSize" ), QString::number( update->uncompressedSize() ) ); + setValue( QLatin1String( "Version" ), update->data( QLatin1String( "Version" ) ).toString() ); + setValue( QLatin1String( "Dependencies" ), update->data( QLatin1String( "Dependencies" ) ).toString() ); + setValue( QLatin1String( "Virtual" ), update->data( QLatin1String( "Virtual" ) ).toString() ); + setValue( QLatin1String( "SortingPriority" ), update->data( QLatin1String( "SortingPriority" ) ).toString() ); + setValue( QLatin1String( "InstallPriority" ), update->data( QLatin1String( "InstallPriority" ) ).toString() ); + setValue( QLatin1String( "AutoSelectOn" ), update->data( QLatin1String( "AutoSelectOn" ) ).toString() ); +} + +/*! + Destroys the Component. +*/ +Component::~Component() +{ + if( parentComponent() != 0 ) + d->m_parent->d->m_components.removeAll( this ); + + if (!d->m_newlyInstalled) + qDeleteAll(d->operations); + + qDeleteAll( d->m_components ); + delete d; +} + +void Component::markAsPerformedInstallation() +{ + d->m_newlyInstalled = true; +} + +/*! + \property Component::removeBeforeUpdate + Specifies wheter this component gets removed by the installer system before it gets updated. + Get this property's value by using %removeBeforeUpdate(), and set it + using %setRemoveBeforeUpdate(). The default value is true. + */ +bool Component::removeBeforeUpdate() const +{ + return d->removeBeforeUpdate; +} + +void Component::setRemoveBeforeUpdate( bool removeBeforeUpdate ) +{ + d->removeBeforeUpdate = removeBeforeUpdate; +} + +QList<Component*> Component::dependees() const +{ + return d->m_installer->dependees( this ); +} + +/* + Returns a key/value based hash of all variables set for this component. + */ +QHash<QString,QString> Component::variables() const +{ + return d->m_vars; +} + +/*! + Returns the value of variable name \a key. If \a key is not known yet, \a defaultValue is returned. + */ +QString Component::value(const QString &key, + const QString &defaultValue) const +{ + return d->m_vars.value(key, defaultValue); +} + +/*! + Sets the value of the variable with \a key to \a value. +*/ +void Component::setValue(const QString &key, const QString &value) +{ + if( d->m_vars[ key ] == value ) + return; + + d->m_vars[ key ] = value; + emit valueChanged( key, value ); +} + +/*! + Returnst the installer this component belongs to. +*/ +Installer* Component::installer() const +{ + return d->m_installer; +} + +/*! + Returns the parent of this component. If this component is com.nokia.sdk.qt, its + parent is com.nokia.sdk, as far as this exists. + */ +Component* Component::parentComponent( RunModes runMode ) const +{ + if ( runMode == UpdaterMode ) + return 0; + else + return d->m_parent; +} + +/*! + Appends \a component as a child of this component. If \a component already has a parent, + it is removed from the previous parent. + */ +void Component::appendComponent( Component* component ) +{ + d->m_components.append( component ); + if( component->parentComponent() != 0 ) + component->d->m_parent->d->m_components.removeAll( component ); + component->d->m_parent = this; +} + +/*! + Returns a list of child components. If \a recursive is set to true, the returned list + contains not only the direct children, but all ancestors. + */ +QList< Component* > Component::components( bool recursive, RunModes runMode ) const +{ + if ( runMode == UpdaterMode ) + return QList < Component* >(); + if( !recursive ) + return d->m_components; + + QList< Component* > result; + for( QList< Component* >::const_iterator it = d->m_components.begin(); it != d->m_components.end(); ++it ) + { + result.append( *it ); + result += (*it)->components( true ); + } + return result; +} + +/*! + Contains this component's name (unique identifier). + */ +QString Component::name() const +{ + return value( QLatin1String( "Name" ) ); +} + +/*! + Contains this component's display name (as visible to the user). + */ +QString Component::displayName() const +{ + return value( QLatin1String( "DisplayName" ) ); +} + +/*! + Loads the script at \a fileName into this component's script engine. + The installer and all its components as well as other useful stuff are being exported into the script. + Read \link componentscripting Component Scripting \endlink for details. + \throws Error when either the script at \a fileName couldn't be opened, or the QScriptEngine couldn't evaluate the script. + */ +void Component::loadComponentScript( const QString& fileName ) +{ + QFile file( fileName ); + if( !file.open( QIODevice::ReadOnly ) ) + throw Error( QObject::tr( "Could not open the requested script file at %1: %2" ).arg( fileName, file.errorString() ) ); + d->scriptEngine.evaluate( QLatin1String( file.readAll() ), fileName ); + if( d->scriptEngine.hasUncaughtException() ) { + throw Error( QObject::tr( "Exception while loading the component script %1" ) + .arg(uncaughtExceptionString(&(d->scriptEngine)/*, QFileInfo(file).absoluteFilePath()*/)) ); + } + + const QList< Component* > components = d->m_installer->components( true ); + QScriptValue comps = d->scriptEngine.newArray( components.count() ); + for( int i = 0; i < components.count(); ++i ) { + comps.setProperty( i, d->scriptEngine.newQObject( components[ i ] ) ); + } + d->scriptEngine.globalObject().property( QLatin1String( "installer" ) ).setProperty( QLatin1String( "components" ), comps ); + + QScriptValue comp = d->scriptEngine.evaluate( QLatin1String( "Component" ) ); + if( !d->scriptEngine.hasUncaughtException() ) + { + d->scriptComponent = comp; + d->scriptComponent.construct(); + } + + emit loaded(); + languageChanged(); +} + +/*! + \internal + Calls the script method \link retranslateUi() \endlink, if any. This is done whenever a QTranslator file is being loaded. + */ +void Component::languageChanged() +{ + callScriptMethod( QLatin1String( "retranslateUi" ) ); +} + +/*! + Tries to call the method with \a name within the script and returns the result. + If the method doesn't exist, an invalid result is returned. If the method has an uncaught exception, its string + representation is thrown as an Error exception. + \note The method is not called, if the current script context is the same method, to avoid infinite recursion. + */ +QScriptValue Component::callScriptMethod( const QString& methodName, const QScriptValueList& arguments ) +{ + if( !d->unexistingScriptMethods.value( methodName, true ) ) + return QScriptValue(); + + // don't allow such a recursion + if( d->scriptEngine.currentContext()->backtrace().first().startsWith( methodName ) ) + return QScriptValue(); + + QScriptValue method = d->scriptComponent.property( QString::fromLatin1( "prototype" ) ).property( methodName ); + + if( !method.isValid() ) // this marks the method to be called not any longer + d->unexistingScriptMethods[ methodName ] = false; + + const QScriptValue result = method.call( d->scriptComponent, arguments ); + + if( !result.isValid() ) + return result; + + if( d->scriptEngine.hasUncaughtException() ) + throw Error( uncaughtExceptionString(&(d->scriptEngine)/*, name()*/) ); + + return result; +} + +/*! + Loads the translations matching the name filters \a qms inside \a directory. + Only translations with a \link QFileInfo::baseName() baseName \endlink matching the + current locales \link QLocale::name() name \endlink are loaded. + Read \ref componenttranslation for details. + */ +void Component::loadTranslations( const QDir& directory, const QStringList& qms ) +{ + QDirIterator it( directory.path(), qms, QDir::Files ); + while( it.hasNext() ) + { + const QString filename = it.next(); + if( QFileInfo( filename ).baseName().toLower() != QLocale().name().toLower() ) + continue; + + std::auto_ptr<QTranslator> translator( new QTranslator( this ) ); + if( !translator->load( filename ) ) + throw Error( tr( "Could not open the requested translation file at %1" ).arg( filename ) ); + qApp->installTranslator( translator.release() ); + } +} + +/*! + Loads the user interface files matching the name filters \a uis inside \a directory. + The loaded interface can be accessed via userInterfaces by using the class name set in the ui file. + Read \ref componentuserinterfaces for details. + */ +void Component::loadUserInterfaces( const QDir& directory, const QStringList& uis ) +{ + if( QApplication::type() == QApplication::Tty ) + return; + + QDirIterator it( directory.path(), uis, QDir::Files ); + while( it.hasNext() ) + { + QFile file( it.next() ); + if( !file.open( QIODevice::ReadOnly ) ) + throw Error( tr( "Could not open the requested UI file at %1: %2" ).arg( it.fileName(), file.errorString() ) ); + + static QUiLoader loader; + loader.setTranslationEnabled( true ); + loader.setLanguageChangeEnabled( true ); + QWidget* const w = loader.load( &file ); + d->userInterfaces[ w->objectName() ] = w; + } +} + + +void Component::loadLicenses(const QString &directory, const QHash<QString, QVariant> &licenseHash) +{ + QHash<QString, QVariant>::const_iterator it; + for (it = licenseHash.begin(); it != licenseHash.end(); ++it) { + const QString &fileName = it.value().toString(); + QFile file(directory + fileName); + if(!file.open(QIODevice::ReadOnly)) { + throw Error(tr("Could not open the requested license file at %1: %2" ).arg(fileName, + file.errorString())); + } + d->m_licenses.insert(it.key(), qMakePair(fileName, QTextStream(&file).readAll())); + } +} + +/*! + Contains a list of all user interface class names known to this component. + */ +QStringList Component::userInterfaces() const +{ + return d->userInterfaces.keys(); +} + +QHash<QString, QPair<QString, QString> > Component::licenses() const +{ + return d->m_licenses; +} + +/*! + Returns the QWidget created for class \a name. + */ +QWidget* Component::userInterface( const QString& name ) const +{ + return d->userInterfaces.value( name ); +} + +/*! + Creates all operations needed to install this component's \a path. + \a path is a full qualified filename including the component's name. + This metods gets called from Component::createOperationsForArchive. + You can override this method by providing a method with the same name in the component script. + \note If you call this method from a script, it won't call the scripts method with the same name. + \note RSA signature files are omitted by this method. + + The default implemention is recursively creating Copy and Mkdir operations for all files + and folders within \a path. + */ +void Component::createOperationsForPath( const QString& path ) +{ + const QFileInfo fi( path ); + + // don't copy over a signature + if( fi.suffix() == QLatin1String( "sig" ) && QFileInfo( fi.dir(), fi.completeBaseName() ).exists() ) + return; + + // the script can override this method + if( callScriptMethod( QLatin1String( "createOperationsForPath" ), QScriptValueList() << path ).isValid() ) + return; + + static const QString zipPrefix = QString::fromLatin1( "7z://installer://" ); + static const QString prefix = QString::fromLatin1( "installer://" ); + QString target; + if ( path.startsWith( zipPrefix ) ) { // if the path is an archive, remove the archive file name from the target path + target = path.mid( zipPrefix.length() + name().length() + 1 ); // + 1 for the / + const int nextSlash = target.indexOf( QLatin1Char('/') ); + if ( nextSlash != -1 ) + target = target.mid( nextSlash ); + else + target.clear(); + target.prepend(QLatin1String("@TargetDir@")); + } + else + target = QString::fromLatin1( "@TargetDir@%1" ).arg( path.mid( prefix.length() + name().length() ) ); + static const QString copy = QString::fromLatin1( "Copy" ); + static const QString mkdir = QString::fromLatin1( "Mkdir" ); + if( fi.isFile() ) + { + addOperation( copy, fi.filePath(), target ); + } + else if( fi.isDir() ) + { + qApp->processEvents(); + addOperation( mkdir, target ); + QDirIterator it( fi.filePath() ); + while( it.hasNext() ) + createOperationsForPath( it.next() ); + } + +} + +/*! + Creates all operations needed to install this component's \a archive. + This metods gets called from Component::createOperations. + You can override this method by providing a method with the same name in the component script. + \note If you call this method from a script, it won't call the scripts method with the same name. + + The default implementation calls createOperationsForPath for everything contained in the archive. + If \a archive is a compressed archive known to the installer system, an Extract operation is created, instead. + */ +void Component::createOperationsForArchive( const QString& archive ) +{ + // the script can override this method + if( callScriptMethod( QLatin1String( "createOperationsForArchive" ), QScriptValueList() << archive ).isValid() ) + return; + + const QFileInfo fi( QString::fromLatin1( "installer://%1/%2").arg( name(), archive ) ); + const bool isZip = Lib7z::isSupportedArchive( fi.filePath() ); + + if( !isZip ) + createOperationsForPath( fi.filePath() ); + else + // archives get completely extracted per default (if the script isn't doing other stuff) + addOperation( QLatin1String( "Extract" ), fi.filePath(), QLatin1String( "@TargetDir@" ) ); +} + +/*! + Creates all operations needed to install this component. + You can override this method by providing a method with the same name in the component script. + \note If you call this method from a script, it won't call the scripts method with the same name. + + The default implementation calls createOperationsForArchive for all archives in this component. +*/ +void Component::createOperations() +{ + // the script can override this method + if( callScriptMethod( QLatin1String( "createOperations" ) ).isValid() ) + { + d->operationsCreated = true; + return; + } + + const QDir dir( QString::fromLatin1( "installer://%1/" ).arg( name() ) ); + const QStringList archives = dir.entryList(); + for( QStringList::const_iterator it = archives.begin(); it != archives.end(); ++it ) + createOperationsForArchive( *it ); + + d->operationsCreated = true; +} + +/*! + Registers the file or directory at \a path for being removed when this component gets uninstalled. + In case of a directory, this will be recursive. + If \a wipe is set to true, the directory will also be deleted if it contains changes done by the user + after installation. +*/ +void Component::registerPathForUninstallation( const QString& path, bool wipe ) +{ + d->pathesForUninstallation.append( qMakePair( path, wipe ) ); +} + +/*! + Returns the list of pathes previously registered for uninstallation with #registerPathForUninstallation. +*/ +QList< QPair< QString, bool > > Component::pathesForUninstallation() const +{ + return d->pathesForUninstallation; +} + +/*! + Contains the names of all archives known to this component. This does not contain archives added + with #addDownloadableArchive. + */ +QStringList Component::archives() const +{ + return QDir( QString::fromLatin1( "installer://%1/" ).arg( name() ) ).entryList(); +} + +/*! + Adds the archive \a path to this component. This can only be called when this component was downloaded from + an online repository. When adding \a path, it will be downloaded from the repository when the installation starts. + Read \ref sec_repogen for details. + \sa fromOnlineRepository + */ +void Component::addDownloadableArchive( const QString& path ) +{ + Q_ASSERT( isFromOnlineRepository() ); + const QString versionPrefix = value( QLatin1String( "Version" ) ); + verbose() << "addDownloadable " << path << std::endl; + d->downloadableArchives.append( versionPrefix + path ); +} + +/*! + Removes the archive \a path previously added via addDownloadableArchive from this component. This can oly be + called when this component was downloaded from an online repository. Read \ref sec_repogen for details. + */ +void Component::removeDownloadableArchive( const QString& path ) +{ + Q_ASSERT( isFromOnlineRepository() ); + d->downloadableArchives.removeAll( path ); +} + +/*! + Returns the archives to be downloaded from the online repository before installation. + */ +QStringList Component::downloadableArchives() const +{ + return d->downloadableArchives; +} + +/*! + * Adds a request for quitting the process @p process before installing/updating/uninstalling the + * component. + */ +void Component::addStopProcessForUpdateRequest( const QString& process ) +{ + d->stopProcessForUpdateRequests.append( process ); +} + +/*! +* Removes the request for quitting the process @p process again. +*/ +void Component::removeStopProcessForUpdateRequest( const QString& process ) +{ + d->stopProcessForUpdateRequests.removeAll( process ); +} + +/*! +* Convenience: Add/remove request depending on @p requested (add if @p true, remove if @p false). +*/ +void Component::setStopProcessForUpdateRequest( const QString& process, bool requested ) +{ + if ( requested ) + addStopProcessForUpdateRequest( process ); + else + removeStopProcessForUpdateRequest( process ); +} + +/*! + * The list of processes this component needs to be closed before installing/updating/uninstalling + */ +QStringList Component::stopProcessForUpdateRequests() const +{ + return d->stopProcessForUpdateRequests; +} + +/*! + Returns the operations needed to install this component. If autoCreateOperations is true, createOperations + is called, if no operations have been auto-created yet. + */ +QList< KDUpdater::UpdateOperation* > Component::operations() const +{ + if (d->autoCreateOperations && !d->operationsCreated) { + const_cast< Component* >( this )->createOperations(); + + if (!d->minimumProgressOperation) { + d->minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance() + .create(QLatin1String("MinimumProgress")); + d->operations.append(d->minimumProgressOperation); + } + + if (!d->m_licenses.isEmpty()) { + d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance() + .create(QLatin1String("License")); + d->m_licenseOperation->setValue(QLatin1String("installer"), + QVariant::fromValue(d->m_installer)); + + QVariantMap licenses; + const QList<QPair<QString, QString> > values = d->m_licenses.values(); + for (int i = 0; i < values.count(); ++i) + licenses.insert(values.at(i).first, values.at(i).second); + d->m_licenseOperation->setValue(QLatin1String("licenses"), licenses); + d->operations.append(d->m_licenseOperation); + } + } + return d->operations; +} + +/*! + Adds \a operation to the list of operations needed to install this component. + */ +void Component::addOperation( KDUpdater::UpdateOperation* operation ) +{ + d->operations.append( operation ); + if( FSEngineClientHandler::instance()->isActive() ) + operation->setValue( QLatin1String( "admin" ), true ); +} + +/*! + Adds \a operation to the list of operations needed to install this component. \a operation + is executed with elevated rights. + */ +void Component::addElevatedOperation( KDUpdater::UpdateOperation* operation ) +{ + addOperation( operation ); + operation->setValue( QLatin1String( "admin" ), true ); +} + +bool Component::operationsCreatedSuccessfully() const +{ + return d->operationsCreatedSuccessfully; +} + +/*! + Creates and adds an installation operation for \a operation. Add any number of \a parameter1, \a parameter2, \a parameter3, \a parameter4, \a parameter5 and \a parameter6 + The contents of the parameters get variables like "@TargetDir@" replaced with their values, if contained. + \sa installeroperations + */ +bool Component::addOperation( const QString& operation, const QString& parameter1, const QString& parameter2, const QString& parameter3, const QString& parameter4, const QString& parameter5, const QString& parameter6, const QString& parameter7, const QString& parameter8, const QString& parameter9, const QString& parameter10 ) +{ + KDUpdater::UpdateOperation* const op = KDUpdater::UpdateOperationFactory::instance().create( operation ); + if( op == 0 ) + { + const QMessageBox::StandardButton button = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), + QLatin1String( "operationDoesNotExistError" ), tr( "Error" ), + tr( "Error: Operation %1 does not exist" ).arg( operation ), + QMessageBox::Abort | QMessageBox::Ignore ); + if ( button == QMessageBox::Abort ) + { + d->operationsCreatedSuccessfully = false; + } + return false; + } + if ( op->name() == QLatin1String( "Delete" ) ) + op->setValue( QLatin1String( "performUndo" ), false ); + op->setValue( QLatin1String( "installer" ), qVariantFromValue( d->m_installer ) ); + + QStringList arguments; + if( !parameter1.isNull() ) + arguments.append( parameter1 ); + if( !parameter2.isNull() ) + arguments.append( parameter2 ); + if( !parameter3.isNull() ) + arguments.append( parameter3 ); + if( !parameter4.isNull() ) + arguments.append( parameter4 ); + if( !parameter5.isNull() ) + arguments.append( parameter5 ); + if( !parameter6.isNull() ) + arguments.append( parameter6 ); + if( !parameter7.isNull() ) + arguments.append( parameter7 ); + if( !parameter8.isNull() ) + arguments.append( parameter8 ); + if( !parameter9.isNull() ) + arguments.append( parameter9 ); + if( !parameter10.isNull() ) + arguments.append( parameter10 ); + op->setArguments( d->m_installer->replaceVariables( arguments ) ); + + addOperation( op ); + + return true; +} + +/*! + Creates and adds an installation operation for \a operation. Add any number of \a parameter1, \a parameter2, \a parameter3, \a parameter4, \a parameter5 and \a parameter6 + The contents of the parameters get variables like "@TargetDir@" replaced with their values, if contained. \a operation is executed with + elevated rights. + \sa installeroperations + */ +bool Component::addElevatedOperation( const QString& operation, const QString& parameter1, const QString& parameter2, const QString& parameter3, const QString& parameter4, const QString& parameter5, const QString& parameter6, const QString& parameter7, const QString& parameter8, const QString& parameter9, const QString& parameter10 ) +{ + KDUpdater::UpdateOperation* const op = KDUpdater::UpdateOperationFactory::instance().create( operation ); + if( op == 0 ) + return false; + + op->setValue( QLatin1String( "installer" ), qVariantFromValue( d->m_installer ) ); + + QStringList arguments; + if( !parameter1.isNull() ) + arguments.append( parameter1 ); + if( !parameter2.isNull() ) + arguments.append( parameter2 ); + if( !parameter3.isNull() ) + arguments.append( parameter3 ); + if( !parameter4.isNull() ) + arguments.append( parameter4 ); + if( !parameter5.isNull() ) + arguments.append( parameter5 ); + if( !parameter6.isNull() ) + arguments.append( parameter6 ); + if( !parameter7.isNull() ) + arguments.append( parameter7 ); + if( !parameter8.isNull() ) + arguments.append( parameter8 ); + if( !parameter9.isNull() ) + arguments.append( parameter9 ); + if( !parameter10.isNull() ) + arguments.append( parameter10 ); + op->setArguments( d->m_installer->replaceVariables( arguments ) ); + + addElevatedOperation( op ); + + return true; +} + + +/*! + Specifies wheter operations should be automatically created when the installation starts. This would be done by calling #createOperations. + If you set this to false, it's completely up to the component's script to create all operations. + */ +bool Component::autoCreateOperations() const +{ + return d->autoCreateOperations; +} + +void Component::setAutoCreateOperations( bool autoCreateOperations ) +{ + d->autoCreateOperations = autoCreateOperations; +} + +Qt::CheckState Component::checkState( RunModes runMode ) const +{ + if ( runMode == UpdaterMode ) + return d->isCheckedFromUpdater ? Qt::Checked : Qt::Unchecked; + const QMap< const Component*, Qt::CheckState >::const_iterator it = Private::cachedCheckStates.find( this ); + if( it != Private::cachedCheckStates.end() ) + return *it; + const Qt::CheckState state = componentCheckState( this, runMode ); + Private::cachedCheckStates[ this ] = state; + return state; +} + +/*! + \property Component::selected + Specifies wheter this component is selected for installation. + Get this property's value by using %isSelected(), and set it + using %setSelected(). + */ +bool Component::isSelected( RunModes runMode ) const +{ + const Qt::CheckState state = checkState( runMode ); + return state != Qt::Unchecked; +} + +//SelectMode means: +//NormalSelectMode - dependency errors and selectionChanged SIGNAL are emitted +//InitializeComponentTreeSelectMode - no dependency errors(maybe some components are not ready initialized), +// no selectionChanged SIGNAL needed and no recursion +void Component::setSelected(bool selected, RunModes runMode, SelectMode selectMode) +{ + if ( runMode == UpdaterMode ) + { + verbose() << "Update selection" << std::endl; + QStringList missingNames; + d->m_installer->dependencies( this, &missingNames ); + + if ( !missingNames.isEmpty() ) + { + const QString missingPackages = missingNames.join( QLatin1String( " " ) ); + if ( selectMode == NormalSelectMode ) { + MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), + QLatin1String( "DependenciesMissingError" ), tr( "Dependencies Missing" ), + tr( "The following required packages could not be found : %1!" ).arg( missingPackages ), + QMessageBox::Ok ); + } + verbose() << "Error occured missing dependencies" << missingPackages << std::endl; + return; + } else { + verbose() << "No Error occured" << std::endl; + } + const Qt::CheckState previousState = checkState( UpdaterMode ); + const Qt::CheckState newState = selected ? Qt::Checked : Qt::Unchecked; + d->isCheckedFromUpdater = selected; + // we have to select all dependees as well + if ( selected ) + { + verbose() << "Update selected for " << name() <<std::endl; + const QList< Component* > dependees = d->m_installer->missingDependencies( this ); + d->setSelectedOnComponentList(dependees, true, runMode, selectMode); + } + if( !selected ) + { + // if it got deselected, we have to deselect all dependees as well + verbose() << "Update deselected" << name() << std::endl; + const QList< Component* > dependees = d->m_installer->dependees( this ); + d->setSelectedOnComponentList(dependees, false, runMode, selectMode); + } + //we need selectedChanged even it is not in the NormalSelectMode to check the running processes from script side + //for installpart it is working because we are selecting the components in the script as well + //TODO: change this ^ behaviour in scripts and code + if( /*selectMode == NormalSelectMode && */newState != previousState ) + QMetaObject::invokeMethod( this, "selectedChanged", Qt::QueuedConnection, Q_ARG( bool, newState == Qt::Checked ) ); + + } else { + QMap< Component*, Qt::CheckState > previousStates; + const QList< Component* > allComponents = d->m_installer->components( true ); + for( QList< Component* >::const_iterator it = allComponents.begin(); it != allComponents.end(); ++it ) + previousStates[ *it ] = (*it)->checkState(); + + setValue( QString::fromLatin1( "WantedState" ), selected ? QString::fromLatin1( "Installed" ) : QString::fromLatin1( "Uninstalled" ) ); + Private::cachedCheckStates.clear(); + + if ( selected ) + { + verbose() << "Update selected for " << name() << std::endl; + const QList< Component* > dependees = d->m_installer->missingDependencies( this ); + d->setSelectedOnComponentList(dependees, true, runMode, selectMode); + } + + if( !selected ) + { + // if it got deselected, we have to deselect all dependees as well + const QList< Component* > dependees = d->m_installer->dependees( this ); + d->setSelectedOnComponentList(dependees, false, runMode, selectMode); + } + + // and all children + if ( selectMode == NormalSelectMode ) + { + const QList< Component* > children = components( true ); + for( QList< Component* >::const_iterator it = children.begin(); it != children.end(); ++it ) + { + Component* const comp = *it; + comp->setValue( QString::fromLatin1( "WantedState" ), selected ? QString::fromLatin1( "Installed" ) : QString::fromLatin1( "Uninstalled" ) ); + } + + //now all needed components are selected so we can emit the signals + for( QList< Component* >::const_iterator it = allComponents.begin(); it != allComponents.end(); ++it ) + { + const Qt::CheckState newCheckState = (*it)->checkState(); + if( previousStates[ *it ] != newCheckState ) + QMetaObject::invokeMethod( *it, "selectedChanged", Qt::QueuedConnection, Q_ARG( bool, newCheckState == Qt::Checked ) ); + //emit (*it)->selectedChanged( state == Qt::Checked ); + } + } + } +} + +/*! + * Contains this component dependencies. + * Read \ref componentdependencies for details. + */ +QStringList Component::dependencies() const +{ + return value( QLatin1String( "Dependencies" ) ).split( QLatin1Char( ',' ) ); +} + +/*! + * Determines if the component is installed + */ +bool Component::isInstalled() const +{ + return QLatin1String( "Installed" ) == value( QLatin1String( "CurrentState" ) ); +} + +/*! + * Determines if the user wants to install the component + */ +bool Component::installationRequested() const +{ + return ( QLatin1String( "Installed" ) == value( QLatin1String( "WantedState" ) ) && ( !isInstalled() || isSelected( UpdaterMode ) ) ); +} + +/*! + * Determines if the user wants to install the component + */ +bool Component::uninstallationRequested() const +{ + return QLatin1String( "Uninstalled" ) == value( QLatin1String( "WantedState" ) ) && isInstalled(); +} + +/*! + * Determines if the component was installed recently + */ +bool Component::wasInstalled() const +{ + return QLatin1String( "Uninstalled" ) == value( QLatin1String( "PreviousState" ) ) && isInstalled(); +} + +/*! + * Determines if the component was removed recently + */ +bool Component::wasUninstalled() const +{ + return QLatin1String( "Installed" ) == value( QLatin1String( "PreviousState" ) ) && !isInstalled(); +} + +/*! + * Determines if the components installations status can be changed. + */ +bool Component::isEnabled() const +{ + return d->enabled; +} +/*! + * Enables oder disables ability to change the components installations status. + */ +void Component::setEnabled( bool enabled ) +{ + d->enabled = enabled; +} + +/*! + * \property Component::fromOnlineRepository + * Determines wheter this component has been loaded from an online repository. + * Get this property's value by usinng %isFromOnlineRepository. + * \sa addDownloadableArchive + */ +bool Component::isFromOnlineRepository() const +{ + return !repositoryUrl().isEmpty(); +} + +/*! + * Contains the repository Url this component is downloaded from. + * When this component is not downloaded from an online repository, returns an empty #QUrl. + */ +QUrl Component::repositoryUrl() const +{ + return d->repositoryUrl; +} + +/*! + \internal + Sets this components #repositoryUrl. +*/ +void Component::setRepositoryUrl( const QUrl& url ) +{ + d->repositoryUrl = url; +} |