File indexing completed on 2024-05-19 04:48:46

0001 /****************************************************************************************
0002  * Copyright (c) 2010 Rick W. Chen <stuffcorpse@archlinux.us>                           *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #define DEBUG_PREFIX "ScriptsConfig"
0018 
0019 #include "ScriptsConfig.h"
0020 
0021 #include "amarokconfig.h"
0022 #include "configdialog/ConfigDialog.h"
0023 #include "core/support/Amarok.h"
0024 #include "core/support/Debug.h"
0025 #include "scripting/scriptmanager/ScriptManager.h"
0026 #include "ScriptSelector.h"
0027 #include "ui_ScriptsConfig.h"
0028 
0029 #include <KMessageBox>
0030 #include <KNS3/DownloadDialog>
0031 #include <KPluginInfo>
0032 #include <KPluginSelector>
0033 #include <QStandardPaths>
0034 #include <KTar>
0035 #include <KZip>
0036 
0037 #include <QFileDialog>
0038 #include <QTemporaryFile>
0039 #include <QTimer>
0040 #include <QVBoxLayout>
0041 #include <QScrollBar>
0042 #include <QMimeDatabase>
0043 #include <QMimeType>
0044 
0045 ScriptsConfig::ScriptsConfig( Amarok2ConfigDialog *parent )
0046     : ConfigDialogBase( parent )
0047     , m_configChanged( false )
0048     , m_parent( parent )
0049     , m_oldSelector( nullptr )
0050 {
0051     DEBUG_BLOCK
0052     Ui::ScriptsConfig gui;
0053     gui.setupUi( this );
0054 
0055     m_uninstallButton = gui.uninstallButton;
0056     m_timer = new QTimer(this);
0057     connect( m_timer, &QTimer::timeout, this, &ScriptsConfig::slotUpdateScripts );
0058     m_timer->setInterval( 200 );
0059 
0060     // Load config
0061     gui.kcfg_AutoUpdateScripts->setChecked( AmarokConfig::autoUpdateScripts() );
0062     gui.manageButton->setIcon( QIcon::fromTheme( QStringLiteral("get-hot-new-stuff-amarok") ) );
0063     connect( gui.manageButton, &QAbstractButton::clicked,
0064              this, &ScriptsConfig::slotManageScripts );
0065     connect( gui.installButton, &QAbstractButton::clicked,
0066              this, &ScriptsConfig::installLocalScript );
0067 
0068     m_selector = gui.scriptSelector;
0069     m_verticalLayout = gui.verticalLayout;
0070     slotReloadScriptSelector();
0071 
0072     connect( gui.reloadButton, &QAbstractButton::clicked, m_timer, QOverload<>::of(&QTimer::start) );
0073     connect( gui.uninstallButton, &QAbstractButton::clicked, this, &ScriptsConfig::slotUninstallScript );
0074 
0075     connect( ScriptManager::instance(), &ScriptManager::scriptsChanged,
0076              this, &ScriptsConfig::slotReloadScriptSelector );
0077 
0078     this->setEnabled( AmarokConfig::enableScripts() );
0079 }
0080 
0081 ScriptsConfig::~ScriptsConfig()
0082 {}
0083 
0084 void
0085 ScriptsConfig::slotManageScripts()
0086 {
0087     QStringList updateScriptsList;
0088     KNS3::DownloadDialog dialog(QStringLiteral("amarok.knsrc"), this);
0089     dialog.exec();
0090 
0091     if( !dialog.installedEntries().isEmpty() || !dialog.changedEntries().isEmpty() )
0092         m_timer->start();
0093 }
0094 
0095 void
0096 ScriptsConfig::updateSettings()
0097 {
0098     DEBUG_BLOCK
0099     if( m_configChanged )
0100     {
0101         m_selector->save();
0102         ScriptManager::instance()->configChanged( true );
0103     }
0104 }
0105 
0106 bool
0107 ScriptsConfig::hasChanged()
0108 {
0109     return m_configChanged;
0110 }
0111 
0112 bool
0113 ScriptsConfig::isDefault()
0114 {
0115     return false;
0116 }
0117 
0118 void
0119 ScriptsConfig::slotConfigChanged( bool changed )
0120 {
0121     m_configChanged = changed;
0122     if( changed )
0123         debug() << "config changed";
0124 }
0125 
0126 void
0127 ScriptsConfig::installLocalScript()
0128 {
0129     DEBUG_BLOCK
0130     // where's this config stored anyway, use amarokconfig instead?
0131     // the script can actually be updated if you get the folder name right
0132     int response =  KMessageBox::warningContinueCancel( this, i18n( "Manually installed scripts "
0133                                         "cannot be automatically updated, continue?" ), QString(), KStandardGuiItem::cont()
0134                                         , KStandardGuiItem::cancel(), QStringLiteral("manualScriptInstallWarning") );
0135     if( response == KMessageBox::Cancel )
0136         return;
0137 
0138     QString filePath = QFileDialog::getOpenFileName( this, i18n( "Select Archived Script" ) );
0139     if( filePath.isEmpty() )
0140         return;
0141 
0142     QString fileName = QFileInfo( filePath ).fileName();
0143     QMimeDatabase db;
0144     QMimeType mimeType = db.mimeTypeForFile( filePath );
0145     QScopedPointer<KArchive> archive;
0146     if( mimeType.inherits( QStringLiteral("application/zip") ) )
0147         archive.reset( new KZip( filePath ) );
0148     else
0149         archive.reset( new KTar( filePath ) );
0150 
0151     if( !archive || !archive->open( QIODevice::ReadOnly ) )
0152     {
0153         KMessageBox::error( this, i18n( "Invalid Archive" ) );
0154         return;
0155     }
0156 
0157     QString destination = QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation ) + QStringLiteral("amarok/scripts/") + fileName + '/';
0158     const KArchiveDirectory* const archiveDir = archive->directory();
0159     const QDir dir( destination );
0160     const KArchiveFile *specFile = findSpecFile( archiveDir );
0161     if( !specFile )
0162     {
0163         KMessageBox::error( this, i18n( "Invalid Script File" ) );
0164         return;
0165     }
0166 
0167     QTemporaryFile tempFile;
0168     tempFile.open();
0169     QIODevice *device = specFile->createDevice();
0170     tempFile.write( device->readAll() );
0171     delete device;
0172     tempFile.close();
0173 
0174     KPluginInfo newScriptInfo( tempFile.fileName() );
0175     if( !newScriptInfo.isValid() )
0176     {
0177         KMessageBox::error( this, i18n( "Invalid Script File" ) );
0178         return;
0179     }
0180 
0181     if( ScriptManager::instance()->m_scripts.contains( newScriptInfo.pluginName() ) )
0182     {
0183         QString existingVersion = ScriptManager::instance()->m_scripts[ newScriptInfo.pluginName() ]->info().version();
0184         QString message = i18n( "Another script with the name %1 already exists\nExisting Script's "
0185                                 "Version: %2\nSelected Script's Version: %3", newScriptInfo.name()
0186                                 , existingVersion, newScriptInfo.version() );
0187         KMessageBox::error( this, message );
0188         return;
0189     }
0190 
0191     for( int i = 1; dir.exists( destination ); ++i )
0192         destination += i;
0193     dir.mkpath( destination );
0194     archiveDir->copyTo( destination );
0195     KMessageBox::information( this, i18n( "The script %1 was successfully installed", newScriptInfo.name() ) );
0196     m_timer->start();
0197 }
0198 
0199 void
0200 ScriptsConfig::slotReloadScriptSelector()
0201 {
0202     DEBUG_BLOCK
0203     m_oldSelector = m_selector;
0204     m_selector = new ScriptSelector( this );
0205     QString key = QStringLiteral( "Generic" );
0206     m_selector->addScripts( ScriptManager::instance()->scripts( key ),
0207                             KPluginSelector::ReadConfigFile, i18n("Generic"), key );
0208 
0209     key = QStringLiteral( "Lyrics" );
0210     m_selector->addScripts( ScriptManager::instance()->scripts( key ),
0211                             KPluginSelector::ReadConfigFile, i18n("Lyrics"), key );
0212 
0213     key = QStringLiteral( "Scriptable Service" );
0214     m_selector->addScripts( ScriptManager::instance()->scripts( key ),
0215                             KPluginSelector::ReadConfigFile, i18n("Scriptable Service"), key );
0216     connect( m_selector, &ScriptSelector::changed, this, &ScriptsConfig::slotConfigChanged );
0217     connect( m_selector, &ScriptSelector::filtered, m_uninstallButton, &QPushButton::setDisabled );
0218     connect( m_selector, &ScriptSelector::changed,
0219              qobject_cast<Amarok2ConfigDialog*>(m_parent), &Amarok2ConfigDialog::updateButtons );
0220 
0221     m_verticalLayout->insertWidget( 0, m_selector );
0222     m_verticalLayout->removeWidget( m_oldSelector );
0223 
0224     m_selector->setFilter( m_oldSelector->filter() );
0225     QTimer::singleShot( 0, this, &ScriptsConfig::restoreScrollBar );
0226 }
0227 
0228 void
0229 ScriptsConfig::restoreScrollBar()
0230 {
0231     if( !m_oldSelector )
0232         return;
0233     m_selector->setVerticalPosition( m_oldSelector->verticalPosition() );
0234     m_oldSelector->deleteLater();
0235 }
0236 
0237 void
0238 ScriptsConfig::slotUpdateScripts()
0239 {
0240     m_timer->stop();
0241     ScriptManager::instance()->updateAllScripts();
0242 }
0243 
0244 void
0245 ScriptsConfig::slotUninstallScript()
0246 {
0247     DEBUG_BLOCK
0248     if( !ScriptManager::instance()->m_scripts.contains( m_selector->currentItem() ) )
0249         return;
0250 
0251     ScriptItem *item = ScriptManager::instance()->m_scripts.value( m_selector->currentItem() );
0252     /*
0253     int response = KMessageBox::warningContinueCancel( this, i18n( "You are advised to only uninstall manually "
0254                                                                     "installed scripts using this button." ) );
0255     if( response == KMessageBox::Cancel )
0256         return;
0257     */
0258 
0259     QFileInfo specFile( item->specPath() );
0260     qDebug() << "About to remove folder " << specFile.path();
0261     QDir( specFile.path() ).removeRecursively();
0262     m_timer->start();
0263 }
0264 
0265 const KArchiveFile*
0266 ScriptsConfig::findSpecFile( const KArchiveDirectory *dir ) const
0267 {
0268     foreach( const QString &entry, dir->entries() )
0269     {
0270         if( dir->entry( entry )->isFile() )
0271         {
0272             if( entry == QLatin1String("script.spec") )
0273                 return static_cast<const KArchiveFile*>( dir->entry( entry ) );
0274         }
0275         else
0276         {
0277             if( entry != QLatin1String(".") && entry != QLatin1String("..") )
0278             {
0279                 const KArchiveDirectory *subDir = static_cast<const KArchiveDirectory*>( dir->entry( entry ) );
0280                 if( subDir )
0281                 {
0282                     const KArchiveFile *file = findSpecFile( subDir );
0283                     if( !file )
0284                         continue;
0285                     return file;
0286                 }
0287             }
0288         }
0289     }
0290     return nullptr;
0291 }