diff --git a/src/libcalamaresui/CMakeLists.txt b/src/libcalamaresui/CMakeLists.txt index f6c8c9ac7..703a8bbe1 100644 --- a/src/libcalamaresui/CMakeLists.txt +++ b/src/libcalamaresui/CMakeLists.txt @@ -16,6 +16,7 @@ set( calamaresui_SOURCES utils/CalamaresUtilsGui.cpp utils/DebugWindow.cpp utils/ImageRegistry.cpp + utils/Paste.cpp utils/qjsonmodel.cpp utils/qjsonitem.cpp diff --git a/src/libcalamaresui/ViewManager.cpp b/src/libcalamaresui/ViewManager.cpp index b0f2b00a8..b27b36f80 100644 --- a/src/libcalamaresui/ViewManager.cpp +++ b/src/libcalamaresui/ViewManager.cpp @@ -21,23 +21,23 @@ #include "ViewManager.h" -#include "utils/Logger.h" #include "viewpages/BlankViewStep.h" #include "viewpages/ViewStep.h" + +#include "Branding.h" #include "ExecutionViewStep.h" #include "JobQueue.h" -#include "utils/Retranslator.h" -#include "Branding.h" #include "Settings.h" +#include "utils/Logger.h" +#include "utils/Paste.h" +#include "utils/Retranslator.h" + #include #include #include #include #include -#include -#include -#include namespace Calamares { @@ -112,9 +112,7 @@ ViewManager::ViewManager( QObject* parent ) m_next = makeButton( m_widget, "go-next" ); m_quit = makeButton( m_widget, "dialog-cancel" ); - CALAMARES_RETRANSLATE( - updateButtonLabels(); - ) + CALAMARES_RETRANSLATE( updateButtonLabels(); ) QBoxLayout* bottomLayout = new QHBoxLayout; mainLayout->addLayout( bottomLayout ); @@ -128,17 +126,21 @@ ViewManager::ViewManager( QObject* parent ) connect( m_back, &QPushButton::clicked, this, &ViewManager::back ); m_back->setEnabled( false ); - connect( m_quit, &QPushButton::clicked, this, - [this]() { if ( this->confirmCancelInstallation() ) qApp->quit(); } ); - connect( JobQueue::instance(), &JobQueue::failed, - this, &ViewManager::onInstallationFailed ); - connect( JobQueue::instance(), &JobQueue::finished, - this, &ViewManager::next ); + connect( m_quit, &QPushButton::clicked, this, [this]() { + if ( this->confirmCancelInstallation() ) + { + qApp->quit(); + } + } ); + connect( JobQueue::instance(), &JobQueue::failed, this, &ViewManager::onInstallationFailed ); + connect( JobQueue::instance(), &JobQueue::finished, this, &ViewManager::next ); - if (Calamares::Settings::instance()->disableCancel()) + if ( Calamares::Settings::instance()->disableCancel() ) + { m_quit->setVisible( false ); + } -onInstallationFailed("amessage","somedetails"); // TODO: remove this + // onInstallationFailed( "Tile of Failure", "Body of Failure"); // for testing paste functionality } @@ -162,7 +164,9 @@ ViewManager::addViewStep( ViewStep* step ) insertViewStep( m_steps.size(), step ); // If this is the first inserted view step, update status of "Next" button if ( m_steps.count() == 1 ) + { m_next->setEnabled( step->isNextEnabled() ); + } } @@ -172,18 +176,20 @@ ViewManager::insertViewStep( int before, ViewStep* step ) m_steps.insert( before, step ); QLayout* layout = step->widget()->layout(); if ( layout ) + { layout->setContentsMargins( 0, 0, 0, 0 ); + } m_stack->insertWidget( before, step->widget() ); connect( step, &ViewStep::enlarge, this, &ViewManager::enlarge ); - connect( step, &ViewStep::nextStatusChanged, - this, [this]( bool status ) - { + connect( step, &ViewStep::nextStatusChanged, this, [this]( bool status ) { ViewStep* vs = qobject_cast< ViewStep* >( sender() ); if ( vs ) { if ( vs == m_steps.at( m_currentStep ) ) + { m_next->setEnabled( status ); + } } } ); @@ -195,25 +201,24 @@ ViewManager::insertViewStep( int before, ViewStep* step ) void ViewManager::onInstallationFailed( const QString& message, const QString& details ) { -bool shouldOfferWebPaste = true; // TODO: config var -QString ficheHost = "termbin.com"; // TODO: config var -quint16 fichePort = 9999; // TODO: config var + bool shouldOfferWebPaste = false; // TODO: config var cError() << "Installation failed:"; cDebug() << "- message:" << message; cDebug() << "- details:" << details; - QString heading = Calamares::Settings::instance()->isSetupMode() - ? tr( "Setup Failed" ) - : tr( "Installation Failed" ); + QString heading + = Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Failed" ) : tr( "Installation Failed" ); QString pasteMsg = tr( "Would you like to paste the install log to the web?" ); - QString pasteUrlFmt = tr( "Install log posted to:\n%1" ); - QString pasteUrlTitle = tr( "Install Log Paste URL" ); QString text = "

" + message + "

"; if ( !details.isEmpty() ) + { text += "

" + details + "

"; + } if ( shouldOfferWebPaste ) + { text += "

" + pasteMsg + "

"; + } QMessageBox* msgBox = new QMessageBox(); msgBox->setIcon( QMessageBox::Critical ); @@ -236,107 +241,48 @@ quint16 fichePort = 9999; // TODO: config var msgBox->show(); cDebug() << "Calamares will quit when the dialog closes."; - connect( msgBox, &QMessageBox::buttonClicked, - [msgBox, ficheHost, fichePort, pasteUrlFmt, pasteUrlTitle] ( QAbstractButton* button ) - { - if ( button->text() != tr( "&Yes" ) ) + connect( msgBox, &QMessageBox::buttonClicked, [this,msgBox]( QAbstractButton* button ) { + if ( msgBox->buttonRole( button ) == QMessageBox::ButtonRole::YesRole ) { - QApplication::quit(); - return; + // TODO: host and port should be configurable + QString pasteUrlMsg = CalamaresUtils::sendLogToPastebin( msgBox, QStringLiteral( "termbin.com" ), 9999 ); + + QString pasteUrlTitle = tr( "Install Log Paste URL" ); + if ( pasteUrlMsg.isEmpty() ) + { + pasteUrlMsg = tr( "The upload was unsuccessful. No web-paste was done." ); + } + + // TODO: make the URL clickable, or copy it to the clipboard automatically + QMessageBox::critical(nullptr, + pasteUrlTitle, + pasteUrlMsg); } - - QFile pasteSourceFile( Logger::logFile() ); - if ( !pasteSourceFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) - { - cError() << "Could not open log file"; - return; - } - - QByteArray pasteData; - while ( !pasteSourceFile.atEnd() ) - { - pasteData += pasteSourceFile.readLine(); - } - - QTcpSocket* socket = new QTcpSocket(msgBox); - socket->connectToHost( ficheHost, fichePort ); - - if ( !socket->waitForConnected() ) - { - cError() << "Could not connect to paste server"; - socket->close(); - return; - } - - cDebug() << "Connected to paste server"; - - socket->write( pasteData ); - - if ( !socket->waitForBytesWritten() ) - { - cError() << "Could not write to paste server"; - socket->close(); - return; - } - - cDebug() << "Paste data written to paste server"; - - if ( !socket->waitForReadyRead() ) - { - cError() << "No data from paste server"; - socket->close(); - return; - } - - cDebug() << "Reading response from paste server"; - - char resp[1024]; resp[0] = '\0'; - qint64 nBytesRead = socket->readLine(resp, 1024); - socket->close(); - - QUrl pasteUrl = QUrl( QString( resp ).trimmed(), QUrl::StrictMode ); - QString pasteUrlStr = pasteUrl.toString() ; - QRegularExpression pasteUrlRegex( "^http[s]?://" + ficheHost ); - QString pasteUrlMsg = QString( pasteUrlFmt ).arg( pasteUrlStr ); - - if ( nBytesRead < 8 || !pasteUrl.isValid() || - !pasteUrlRegex.match( pasteUrlStr ).hasMatch() ) - { - cError() << "No data from paste server"; - return; - } - - cDebug() << pasteUrlMsg; - - QMessageBox* pasteUrlMsgBox = new QMessageBox(); - pasteUrlMsgBox->setIcon( QMessageBox::Critical ); - pasteUrlMsgBox->setWindowTitle( pasteUrlTitle ); - pasteUrlMsgBox->setStandardButtons( QMessageBox::Close ); - pasteUrlMsgBox->setText( pasteUrlMsg ); - pasteUrlMsgBox->show(); - - connect( pasteUrlMsgBox, &QMessageBox::buttonClicked, qApp, &QApplication::quit ); - }); + QApplication::quit(); + } ); } void -ViewManager::onInitFailed( const QStringList& modules) +ViewManager::onInitFailed( const QStringList& modules ) { // Because this means the installer / setup program is broken by the distributor, // don't bother being precise about installer / setup wording. QString title( tr( "Calamares Initialization Failed" ) ); - QString description( tr( "%1 can not be installed. Calamares was unable to load all of the configured modules. This is a problem with the way Calamares is being used by the distribution." ) ); + QString description( tr( "%1 can not be installed. Calamares was unable to load all of the configured modules. " + "This is a problem with the way Calamares is being used by the distribution." ) ); QString detailString; if ( modules.count() > 0 ) { description.append( tr( "
The following modules could not be loaded:" ) ); QStringList details; - details << QLatin1Literal("
    "); - for( const auto& m : modules ) - details << QLatin1Literal("
  • ") << m << QLatin1Literal("
  • "); - details << QLatin1Literal("
"); + details << QLatin1Literal( "
    " ); + for ( const auto& m : modules ) + { + details << QLatin1Literal( "
  • " ) << m << QLatin1Literal( "
  • " ); + } + details << QLatin1Literal( "
" ); detailString = details.join( QString() ); } @@ -371,10 +317,8 @@ ViewManager::currentStepIndex() const static inline bool stepIsExecute( const ViewStepList& steps, int index ) { - return - ( 0 <= index ) && - ( index < steps.count() ) && - ( qobject_cast< ExecutionViewStep* >( steps.at( index ) ) != nullptr ); + return ( 0 <= index ) && ( index < steps.count() ) + && ( qobject_cast< ExecutionViewStep* >( steps.at( index ) ) != nullptr ); } void @@ -389,11 +333,10 @@ ViewManager::next() // Special case when the user clicks next on the very last page in a view phase // and right before switching to an execution phase. // Depending on Calamares::Settings, we show an "are you sure" prompt or not. - if ( settings->showPromptBeforeExecution() && stepIsExecute( m_steps, m_currentStep+1 ) ) + if ( settings->showPromptBeforeExecution() && stepIsExecute( m_steps, m_currentStep + 1 ) ) { - QString title = settings->isSetupMode() - ? tr( "Continue with setup?" ) - : tr( "Continue with installation?" ); + QString title + = settings->isSetupMode() ? tr( "Continue with setup?" ) : tr( "Continue with installation?" ); QString question = settings->isSetupMode() ? tr( "The %1 setup program is about to make changes to your " "disk in order to set up %2.
You will not be able " @@ -401,21 +344,21 @@ ViewManager::next() : tr( "The %1 installer is about to make changes to your " "disk in order to install %2.
You will not be able " "to undo these changes." ); - QString confirm = settings->isSetupMode() - ? tr( "&Set up now" ) - : tr( "&Install now" ); - - int reply = - QMessageBox::question( m_widget, - title, - question.arg( *Calamares::Branding::ShortProductName, *Calamares::Branding::ShortVersionedName ), - confirm, - tr( "Go &back" ), - QString(), - 0, - 1 ); + QString confirm = settings->isSetupMode() ? tr( "&Set up now" ) : tr( "&Install now" ); + + int reply = QMessageBox::question( + m_widget, + title, + question.arg( *Calamares::Branding::ShortProductName, *Calamares::Branding::ShortVersionedName ), + confirm, + tr( "Go &back" ), + QString(), + 0 /* default first button, i.e. confirm */, + 1 /* escape is second button, i.e. cancel */ ); if ( reply == 1 ) + { return; + } } m_currentStep++; @@ -436,7 +379,7 @@ ViewManager::next() m_next->setEnabled( false ); m_back->setEnabled( false ); } - updateCancelEnabled( !settings->disableCancel() && !(executing && settings->disableCancelDuringExec() ) ); + updateCancelEnabled( !settings->disableCancel() && !( executing && settings->disableCancelDuringExec() ) ); } else { @@ -457,9 +400,7 @@ ViewManager::updateButtonLabels() { const auto* const settings = Calamares::Settings::instance(); - QString nextIsInstallationStep = settings->isSetupMode() - ? tr( "&Set up" ) - : tr( "&Install" ); + QString nextIsInstallationStep = settings->isSetupMode() ? tr( "&Set up" ) : tr( "&Install" ); QString quitOnCompleteTooltip = settings->isSetupMode() ? tr( "Setup is complete. Close the setup program." ) : tr( "The installation is complete. Close the installer." ); @@ -468,7 +409,7 @@ ViewManager::updateButtonLabels() : tr( "Cancel installation without changing the system." ); // If we're going into the execution step / install phase, other message - if ( stepIsExecute( m_steps, m_currentStep+1 ) ) + if ( stepIsExecute( m_steps, m_currentStep + 1 ) ) { m_next->setText( nextIsInstallationStep ); setButtonIcon( m_next, "run-install" ); @@ -494,8 +435,11 @@ ViewManager::updateButtonLabels() else { if ( settings->disableCancel() ) + { m_quit->setVisible( false ); // In case we went back from final - updateCancelEnabled( !settings->disableCancel() && !( stepIsExecute( m_steps, m_currentStep ) && settings->disableCancelDuringExec() ) ); + } + updateCancelEnabled( !settings->disableCancel() + && !( stepIsExecute( m_steps, m_currentStep ) && settings->disableCancelDuringExec() ) ); m_quit->setText( tr( "&Cancel" ) ); m_quit->setToolTip( cancelBeforeInstallationTooltip ); @@ -516,46 +460,53 @@ ViewManager::back() emit currentStepChanged(); } else if ( !step->isAtBeginning() ) + { step->back(); - else return; + } + else + { + return; + } m_next->setEnabled( m_steps.at( m_currentStep )->isNextEnabled() ); m_back->setEnabled( m_steps.at( m_currentStep )->isBackEnabled() ); if ( m_currentStep == 0 && m_steps.first()->isAtBeginning() ) + { m_back->setEnabled( false ); + } updateButtonLabels(); } -bool ViewManager::confirmCancelInstallation() +bool +ViewManager::confirmCancelInstallation() { const auto* const settings = Calamares::Settings::instance(); // When we're at the very end, then it's always OK to exit. if ( isAtVeryEnd() ) + { return true; + } // Not at the very end, cancel/quit might be disabled if ( settings->disableCancel() ) + { return false; + } if ( settings->disableCancelDuringExec() && stepIsExecute( m_steps, m_currentStep ) ) + { return false; + } // Otherwise, confirm cancel/quit. - QString title = settings->isSetupMode() - ? tr( "Cancel setup?" ) - : tr( "Cancel installation?" ); - QString question = settings->isSetupMode() - ? tr( "Do you really want to cancel the current setup process?\n" - "The setup program will quit and all changes will be lost." ) - : tr( "Do you really want to cancel the current install process?\n" - "The installer will quit and all changes will be lost." ); - QMessageBox mb( QMessageBox::Question, - title, - question, - QMessageBox::Yes | QMessageBox::No, - m_widget ); + QString title = settings->isSetupMode() ? tr( "Cancel setup?" ) : tr( "Cancel installation?" ); + QString question = settings->isSetupMode() ? tr( "Do you really want to cancel the current setup process?\n" + "The setup program will quit and all changes will be lost." ) + : tr( "Do you really want to cancel the current install process?\n" + "The installer will quit and all changes will be lost." ); + QMessageBox mb( QMessageBox::Question, title, question, QMessageBox::Yes | QMessageBox::No, m_widget ); mb.setDefaultButton( QMessageBox::No ); mb.button( QMessageBox::Yes )->setText( tr( "&Yes" ) ); mb.button( QMessageBox::No )->setText( tr( "&No" ) ); @@ -570,4 +521,4 @@ ViewManager::updateCancelEnabled( bool enabled ) emit cancelEnabled( enabled ); } -} // namespace +} // namespace Calamares diff --git a/src/libcalamaresui/ViewManager.h b/src/libcalamaresui/ViewManager.h index c9f554ee8..fca74a367 100644 --- a/src/libcalamaresui/ViewManager.h +++ b/src/libcalamaresui/ViewManager.h @@ -133,7 +133,8 @@ private: bool isAtVeryEnd() const { - return ( m_currentStep >= m_steps.count() ) || ( m_currentStep == m_steps.count() - 1 && m_steps.last()->isAtEnd() ); + return ( m_currentStep >= m_steps.count() ) + || ( m_currentStep == m_steps.count() - 1 && m_steps.last()->isAtEnd() ); } static ViewManager* s_instance; @@ -148,6 +149,6 @@ private: QPushButton* m_quit; }; -} +} // namespace Calamares -#endif // VIEWMANAGER_H +#endif // VIEWMANAGER_H diff --git a/src/libcalamaresui/utils/Paste.cpp b/src/libcalamaresui/utils/Paste.cpp new file mode 100644 index 000000000..6fead59e6 --- /dev/null +++ b/src/libcalamaresui/utils/Paste.cpp @@ -0,0 +1,99 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Bill Auger + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "Paste.h" + +#include "utils/Logger.h" + +#include +#include +#include +#include + +namespace CalamaresUtils +{ + +QString +sendLogToPastebin( QObject* parent, const QString& ficheHost, int fichePort ) +{ + QString pasteUrlFmt = parent->tr( "Install log posted to:\n%1" ); + QFile pasteSourceFile( Logger::logFile() ); + if ( !pasteSourceFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + cError() << "Could not open log file"; + return QString(); + } + + QByteArray pasteData; + while ( !pasteSourceFile.atEnd() ) + { + pasteData += pasteSourceFile.readLine(); + } + + QTcpSocket* socket = new QTcpSocket( parent ); + socket->connectToHost( ficheHost, fichePort ); + + if ( !socket->waitForConnected() ) + { + cError() << "Could not connect to paste server"; + socket->close(); + return QString(); + } + + cDebug() << "Connected to paste server"; + + socket->write( pasteData ); + + if ( !socket->waitForBytesWritten() ) + { + cError() << "Could not write to paste server"; + socket->close(); + return QString(); + } + + cDebug() << "Paste data written to paste server"; + + if ( !socket->waitForReadyRead() ) + { + cError() << "No data from paste server"; + socket->close(); + return QString(); + } + + cDebug() << "Reading response from paste server"; + + char resp[ 1024 ]; + resp[ 0 ] = '\0'; + qint64 nBytesRead = socket->readLine( resp, 1024 ); + socket->close(); + + QUrl pasteUrl = QUrl( QString( resp ).trimmed(), QUrl::StrictMode ); + QString pasteUrlStr = pasteUrl.toString(); + QRegularExpression pasteUrlRegex( "^http[s]?://" + ficheHost ); + QString pasteUrlMsg = QString( pasteUrlFmt ).arg( pasteUrlStr ); + + if ( nBytesRead < 8 || !pasteUrl.isValid() || !pasteUrlRegex.match( pasteUrlStr ).hasMatch() ) + { + cError() << "No data from paste server"; + return QString(); + } + + cDebug() << "Paste server results:" << pasteUrlMsg; + return pasteUrlMsg; +} +} // namespace CalamaresUtils diff --git a/src/libcalamaresui/utils/Paste.h b/src/libcalamaresui/utils/Paste.h new file mode 100644 index 000000000..2ac77f2f3 --- /dev/null +++ b/src/libcalamaresui/utils/Paste.h @@ -0,0 +1,36 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Bill Auger + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef UTILS_PASTE_H +#define UTILS_PASTE_H + +class QObject; +class QString; + +namespace CalamaresUtils +{ + +/** @brief Send the current log file to a pastebin + * + * Returns the (string) URL that the pastebin gives us. + */ +QString sendLogToPastebin( QObject* parent, const QString& ficheHost, int fichePort ); + +} // namespace CalamaresUtils + +#endif