source: ogBrowser-Git/src/mainwindow.cpp @ 98c1a79

jenkinsmain
Last change on this file since 98c1a79 was dae65b7, checked in by Vadim Troshchinskiy <vtroshchinskiy@…>, 19 months ago

Update browser to Qt5

  • Property mode set to 100644
File size: 17.9 KB
Line 
1#include "mainwindow.h"
2#include <QtWebEngineWidgets>
3#include <QStringList>
4#include <QWebEngineView>
5#include <QDockWidget>
6#include <QtDebug>
7#include <QWebEnginePage>
8#include <QProcess>
9#include <QTextEdit>
10#include <QMessageBox>
11#include <QPushButton>
12#include <QDateTime>
13#include <QProgressBar>
14#include <QTabWidget>
15#include <QLineEdit>
16#include <QNetworkReply>
17#include <QSslError>
18#include <QTimer>
19#include <libintl.h>
20
21#include "qtermwidget.h"
22#include "digitalclock.h"
23
24#define BUFFERSIZE 2048
25#define REGEXP_STRING "^\\[(\\d+)\\]"
26
27#define CURRENT_TIME() QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss")
28
29MainWindow::MainWindow(QWidget *parent)
30    : QMainWindow(parent),m_web(new QWebEngineView()),m_output(new QTextEdit()),
31      m_process(new QProcess(this)),
32      m_logfile(0),m_logstream(0),m_numberTerminal(0)
33{
34    // Graphic
35    showFullScreen();
36    setWindowTitle(tr("OpenGnsys Browser"));
37    setCentralWidget(m_web);
38    readEnvironmentValues();
39
40    // Open the log file for append
41    if(m_env.contains("OGLOGFILE") && m_env["OGLOGFILE"]!="")
42    {
43        QFile* m_logfile=new QFile(m_env["OGLOGFILE"]);
44        if(!m_logfile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
45        {
46            delete m_logfile;
47            print(tr(gettext("El fichero de log no ha podido ser abierto: "))+m_env["OGLOGFILE"]+".");
48        }
49        else
50        {
51            m_logstream=new QTextStream(m_logfile);
52        }
53    }
54
55    // Output
56    m_output->setReadOnly(true);
57    m_output->setFontPointSize(16);
58
59    // Button Dock
60    QDockWidget* dock=new QDockWidget();
61    dock->setAllowedAreas(Qt::BottomDockWidgetArea);
62    QWidget* dummy=new QWidget();
63    dummy->setMaximumHeight(0);
64    dock->setTitleBarWidget(dummy);
65
66    // TabWidget
67    m_tabs=new QTabWidget(dock);
68    QPushButton *button=new QPushButton(tr(gettext("&Nueva Terminal")));
69    button->setFocusPolicy(Qt::TabFocus);
70    m_tabs->setCornerWidget(button);
71    m_tabs->setFocusPolicy(Qt::NoFocus);
72    m_tabs->addTab(m_output,tr(gettext("Salida")));
73    slotCreateTerminal();
74    // Assign tabs to dock
75    dock->setWidget(m_tabs);
76    // Assign tabs dock to the mainwindow if admin mode is active
77    if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
78        addDockWidget(Qt::BottomDockWidgetArea,dock);
79
80    // Top Dock
81    dock=new QDockWidget();
82    dock->setAllowedAreas(Qt::TopDockWidgetArea);
83    QWidget* dummy2=new QWidget();
84    dummy2->setMaximumHeight(0);
85    dock->setTitleBarWidget(dummy2);
86    // WebBar
87    m_webBar=new QLineEdit(dock);
88    // WebBar to dock
89    dock->setWidget(m_webBar);
90    // Assign top dock to the mainwindow if admin mode is active
91    if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
92        addDockWidget(Qt::TopDockWidgetArea,dock);
93
94    // Status bar
95    QStatusBar* st=statusBar();
96    st->setSizeGripEnabled(false);
97    // OpenGnsys logo (or alternate text)
98    m_logo=new QLabel();
99    QPixmap logo;
100    if(logo.load("/opt/opengnsys/lib/pictures/oglogo.png"))
101        m_logo->setPixmap(logo);
102    else
103        m_logo->setText("OG");
104    m_logo->setToolTip(tr(gettext("Proyecto OpenGnsys"))+"\nhttps://opengnsys.es");
105    // Progress bar
106    m_progressBar=new QProgressBar(this);
107    m_progressBar->setRange(0,100);
108    // Connection speed
109    QString speed=readSpeed();
110    m_speedInfo=new QLabel(speed);
111    m_speedInfo->setAlignment(Qt::AlignCenter);
112    if(m_env.contains("DEFAULTSPEED") && m_env["DEFAULTSPEED"]!="")
113        if(speed.compare(m_env["DEFAULTSPEED"])!=0)
114            m_speedInfo->setStyleSheet("background-color: darkred; color: white; font-weight: bold;");
115    // Clock
116    m_clock=new DigitalClock(this);
117
118    // Qindel:
119    // https://doc.qt.io/qt-6/qtwebenginewidgets-qtwebkitportingguide.html
120    // There is no way to connect a signal to run C++ code when a link is clicked.
121    // However, link clicks can be delegated to the Qt application instead of having the HTML handler engine process them
122    // by overloading the QWebEnginePage::acceptNavigationRequest() function.
123    //
124    //m_web->page()->setLinkDelegationPolicy(QWebEnginePage::DelegateAllLinks);
125
126    // Web signals
127    // Qindel: This signal no longer exists, as per the above.
128    //connect(m_web,SIGNAL(linkClicked(const QUrl&)),this, SLOT(slotLinkHandle(const QUrl&)));
129
130    connect(m_web,SIGNAL(loadStarted()),this,SLOT(slotWebLoadStarted()));
131    connect(m_web,SIGNAL(loadFinished(bool)),this,SLOT(slotWebLoadFinished(bool)));
132    connect(m_web,SIGNAL(loadProgress(int)),this,SLOT(slotWebLoadProgress(int)));
133    connect(m_web,SIGNAL(urlChanged(const QUrl&)),this,SLOT(slotUrlChanged(const QUrl&)));
134    // Ignore SSL errors.
135
136
137    // Qindel:
138    //connect(m_web->page()->networkAccessManager(),
139//            SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)), this,
140//            SLOT(slotSslErrors(QNetworkReply*)));
141
142    // Process signals
143    connect(m_process,SIGNAL(started()),this,SLOT(slotProcessStarted()));
144    connect(m_process,SIGNAL(finished(int,QProcess::ExitStatus)),
145            this,SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
146    connect(m_process,SIGNAL(error(QProcess::ProcessError)),
147            this,SLOT(slotProcessError(QProcess::ProcessError)));
148    connect(m_process,SIGNAL(readyReadStandardOutput()),this,SLOT(slotProcessOutput()));
149    connect(m_process,SIGNAL(readyReadStandardError()),
150            this,SLOT(slotProcessErrorOutput()));
151
152    // Dock signals
153    connect(button,SIGNAL(clicked()),this,SLOT(slotCreateTerminal()));
154    connect(m_webBar,SIGNAL(returnPressed()),this,SLOT(slotWebBarReturnPressed()));
155
156    QStringList arguments=QCoreApplication::arguments();
157    m_webBar->setText(arguments[1]);
158    m_web->load(QUrl(arguments[1]));
159}
160
161MainWindow::~MainWindow()
162{
163    if(m_logfile)
164    {
165        m_logfile->close();
166        delete m_logfile;
167    }
168    if(m_logstream)
169        delete m_logstream;
170}
171
172void MainWindow::slotLinkHandle(const QUrl &url)
173{
174    // Check if it's executing another process
175    if(m_process->state() != QProcess::NotRunning)
176    {
177        print(tr(gettext("Hay otro proceso en ejecución. Por favor espere.")));
178        return;
179    }
180    QString urlString = url.toString();
181    QString urlScheme = url.scheme();
182    // Clear the output widget for a normal user
183    if(! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true")
184    {
185        m_output->clear();
186    }
187    if(urlScheme == COMMAND_CONFIRM || urlScheme == COMMAND_CONFIRM_OUTPUT ||
188       urlScheme == COMMAND_OUTPUT_CONFIRM || urlScheme == COMMAND_WITH_CONFIRMATION)
189    {
190        // For all command with confirmation links, show a popup box
191        QMessageBox msgBox;
192        msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
193        msgBox.setWindowTitle(tr(gettext("AVISO")));
194        msgBox.setIcon(QMessageBox::Question);
195        msgBox.setTextFormat(Qt::RichText);
196        msgBox.setText(tr(gettext("La siguiente acci&oacute;n puede modificar datos o tardar varios minutos. El equipo no podr&aacute; ser utilizado durante su ejecuci&oacute;n.")));
197        QPushButton *execButton = msgBox.addButton(tr(gettext("Ejecutar")), QMessageBox::ActionRole);
198        msgBox.addButton(tr(gettext("Cancelar")), QMessageBox::RejectRole);
199        msgBox.setDefaultButton(execButton);
200        msgBox.exec();
201        // Continue if user press the execution button
202        if (msgBox.clickedButton() == execButton)
203        {
204            // For command with confirmation and output link, show an output window to non-admin user
205            if((urlScheme == COMMAND_CONFIRM_OUTPUT || urlScheme == COMMAND_OUTPUT_CONFIRM) &&
206               (! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true"))
207            {
208                int w=MainWindow::width(), h=MainWindow::height();
209                m_output->setWindowFlags(Qt::Window);
210                m_output->move(100, 100);
211                m_output->setFixedSize(w*0.8-100, h*0.8-100);
212                m_output->show();
213            }
214            // Execute the command
215            executeCommand(urlString.remove(0, urlScheme.length()+1));
216        }
217    }
218    else if(urlScheme == COMMAND || urlScheme == COMMAND_OUTPUT)
219    {
220        // For command with output link, show an output window to non-admin user
221        if(urlScheme == COMMAND_OUTPUT &&
222           (! m_env.contains("ogactiveadmin") || m_env["ogactiveadmin"] != "true"))
223        {
224            int w=MainWindow::width(), h=MainWindow::height();
225            m_output->setWindowFlags(Qt::Window);
226            m_output->move(100, 100);
227            m_output->setFixedSize(w*0.8-100, h*0.8-100);
228            m_output->show();
229        }
230        // Execute the command
231        executeCommand(urlString.remove(0, urlScheme.length()+1));
232    }
233    else
234    {
235        // For other link, load webpage
236        m_web->load(url);
237    }
238}
239
240void MainWindow::slotWebLoadStarted()
241{
242    startProgressBar();
243    m_progressBar->setFormat(gettext("%p% Cargando"));
244}
245
246void MainWindow::slotWebLoadProgress(int progress)
247{
248    m_progressBar->setValue(progress);
249}
250
251void MainWindow::slotWebLoadFinished(bool ok)
252{
253    // If any error ocurred, show a pop up
254    // Sometimes when the url hasn't got a dot, i.e /var/www/pageweb,
255    // the return value is always true so we check the bytes received too
256    if(ok == false)
257    {
258        QMessageBox msgBox;
259        msgBox.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
260        msgBox.setWindowTitle(tr(gettext("AVISO")));
261        msgBox.setIcon(QMessageBox::Question);
262        msgBox.setTextFormat(Qt::RichText);
263        msgBox.setText(tr(gettext("La p&aacute;gina no se puede cargar.")));
264
265        QPushButton *reloadButton = msgBox.addButton(tr(gettext("Recargar")), QMessageBox::ActionRole);
266        msgBox.addButton(tr(gettext("Abortar")), QMessageBox::RejectRole);
267        msgBox.exec();
268
269        if (msgBox.clickedButton() == reloadButton)
270        {
271            m_web->reload();
272        }
273        else
274        {
275            close();
276        }
277    }
278    else
279    {
280        finishProgressBar();
281    }
282}
283
284void MainWindow::slotUrlChanged(const QUrl &url)
285{
286    m_webBar->setText(url.toString());
287}
288
289void MainWindow::slotSslErrors(QNetworkReply* reply)
290{
291    reply->ignoreSslErrors();
292}
293
294void MainWindow::slotProcessStarted()
295{
296    startProgressBar();
297}
298
299void MainWindow::slotProcessOutput()
300{
301    m_process->setReadChannel(QProcess::StandardOutput);
302    char buf[BUFFERSIZE];
303    while((m_process->readLine(buf,BUFFERSIZE) > 0))
304    {
305        QString s(buf);
306        if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
307        {
308            m_output->insertPlainText(tr("Proc. stdout: "));
309        }
310        print(s);
311        captureOutputForStatusBar(s);
312    }
313}
314
315void MainWindow::slotProcessErrorOutput()
316{
317    m_process->setReadChannel(QProcess::StandardError);
318    char buf[BUFFERSIZE];
319    while((m_process->readLine(buf,BUFFERSIZE) > 0))
320    {
321        QString s(buf);
322        if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
323        {
324            m_output->insertPlainText(tr("Proc. stderr: "));
325        }
326        m_output->setTextColor(QColor(Qt::darkBlue));
327        print(s);
328        m_output->setTextColor(QColor(Qt::black));
329    }
330}
331
332void MainWindow::slotProcessFinished(int code, QProcess::ExitStatus status)
333{
334    if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
335    {
336        // Admin user: show process status
337        if(status==QProcess::NormalExit)
338        {
339            if(code > 0)
340            {
341                m_output->setTextColor(QColor(Qt::darkRed));
342            }
343            print("\n"+tr(gettext("Fin del proceso. Valor de retorno: "))+QString::number(code));
344        }
345        else
346        {
347            m_output->setTextColor(QColor(Qt::darkRed));
348            print("\n"+tr(gettext("El proceso ha fallado inesperadamente. Salida: ")+code));
349        }
350        m_output->setTextColor(QColor(Qt::black));
351    }
352    else
353    {
354        // Non-admin user: show instruction to close the popup window
355        write(tr(gettext("Fin del proceso. Valor de retorno: "))+QString::number(code));
356        m_output->setFontUnderline(true);
357        print("\n\n"+tr(gettext("AVISO: Pulsar el botón superior derecho para cerrar"))+" [X]");
358        m_output->setFontUnderline(false);
359    }
360    // On error, show a message box
361    if(code > 0 && ! m_output->isActiveWindow())
362    {
363        showErrorMessage(gettext("Código de salida: ")+QString::number(code));
364    }
365    finishProgressBar();
366}
367
368void MainWindow::slotProcessError(QProcess::ProcessError error)
369{
370    QString errorMsg;
371    switch(error)
372    {
373        case QProcess::FailedToStart:
374            errorMsg=tr(gettext("Imposible lanzar el proceso."));
375            break;
376        case QProcess::WriteError:
377            errorMsg=tr(gettext("Error de escritura en el proceso."));
378            break;
379        case QProcess::ReadError:
380            errorMsg=tr(gettext("Error de lectura del proceso."));
381            break;
382        // No capturo crashed porque la pillo por finished
383        case QProcess::Crashed:
384        case QProcess::Timedout:
385            break;
386        case QProcess::UnknownError:
387        default:
388            errorMsg=tr(gettext("Error desconocido."));
389            break;
390    }
391    // Print error and show message box with timeout.
392    if(!errorMsg.isNull()) {
393        m_output->setTextColor(QColor(Qt::darkRed));
394        print(errorMsg);
395        m_output->setTextColor(QColor(Qt::black));
396        showErrorMessage(errorMsg);
397    }
398    finishProgressBar();
399}
400
401void MainWindow::slotCreateTerminal()
402{
403    QTermWidget* console = new QTermWidget(1,this);
404    QFont font = QApplication::font();
405    font.setFamily("DejaVu Sans Mono");
406    font.setPointSize(12);
407
408    console->setTerminalFont(font);
409    console->setFocusPolicy(Qt::StrongFocus);
410    console->setScrollBarPosition(QTermWidget::ScrollBarRight);
411
412    ++m_numberTerminal;
413
414    connect(console,SIGNAL(finished()),this,SLOT(slotDeleteTerminal()));
415
416    QString name=tr("Term ")+QString::number(m_numberTerminal);
417    m_tabs->addTab(console,name);
418}
419
420void MainWindow::slotDeleteTerminal()
421{
422    QWidget *widget = qobject_cast<QWidget *>(sender());
423    Q_ASSERT(widget);
424    m_tabs->removeTab(m_tabs->indexOf(widget));
425    delete widget;
426}
427
428void MainWindow::slotWebBarReturnPressed()
429{
430    QUrl url(m_webBar->text());
431    if(url.isValid())
432        slotLinkHandle(url);
433}
434
435int MainWindow::readEnvironmentValues()
436{
437    // The return value
438    int ret=true;
439
440    // Get all environment variables
441    QStringList environmentlist=QProcess::systemEnvironment();
442    // This is the list of the important variables
443    QStringList variablelist=QString(ENVIRONMENT).split(",");
444
445    // This is an auxiliar variable
446    QStringList stringlist;
447
448    foreach (QString str,variablelist)
449    {
450        // Look for the variable in the environment
451        stringlist=environmentlist.filter(str+"=");
452
453        if(stringlist.isEmpty())
454        {
455            m_env[str]="";
456            ret=false;
457        }
458        else
459        {
460            // Get the first element and get the value part
461            m_env[str]=(stringlist.first().split("="))[1];
462        }
463    }
464
465    return ret;
466}
467
468// Write a string to the log file
469void MainWindow::write(QString s)
470{
471    if(! s.endsWith("\n"))
472        s+="\n";
473    if(m_logstream)
474    {
475        *m_logstream<<CURRENT_TIME()<<": browser: "<<s;
476        m_logstream->flush();
477    }
478}
479
480// Print and log a string
481void MainWindow::print(QString s)
482{
483    if(! s.endsWith("\n"))
484        s+="\n";
485    write(s);
486    if(m_output)
487        m_output->insertPlainText(s);
488}
489
490// Show message in status bar
491void MainWindow::captureOutputForStatusBar(QString output)
492{
493    // Modify the status bar
494    output=output.trimmed();
495    // Get percentage (string starts with "[Number]")
496    QRegExp regexp(REGEXP_STRING);
497    if(regexp.indexIn(output) != -1)
498    {
499        int pass=regexp.cap(1).toInt();
500        output.replace(regexp,"");
501        m_progressBar->setValue(pass);
502        m_progressBar->setFormat("%p%"+output);
503    }
504}
505
506// Init status bar
507void MainWindow::startProgressBar()
508{
509    QStatusBar* st=statusBar();
510    st->clearMessage();
511    st->addWidget(m_logo);
512    st->addWidget(m_progressBar,90);
513    st->addWidget(m_speedInfo,5);
514    st->addWidget(m_clock,5);
515    m_progressBar->show();
516    m_clock->show();
517    m_web->setEnabled(false);
518}
519
520// Reset status bar
521void MainWindow::finishProgressBar()
522{
523    m_progressBar->reset();
524    m_web->setEnabled(true);
525}
526
527// Execute a command
528void MainWindow::executeCommand(QString &string)
529{
530    QStringList list=string.split(" ",Qt::SkipEmptyParts);
531    QString program=list.takeFirst();
532    m_process->setReadChannel(QProcess::StandardOutput);
533    // Assign the same Browser's environment to the process
534    m_process->setEnvironment(QProcess::systemEnvironment());
535    m_process->start(program,list);
536    // Only show the command line to admin user
537    if(m_env.contains("ogactiveadmin") && m_env["ogactiveadmin"] == "true")
538    {
539        m_output->setTextColor(QColor(Qt::darkGreen));
540        print(tr(gettext("Lanzando el comando: "))+string);
541        m_output->setTextColor(QColor(Qt::black));
542    }
543    else
544    {
545        write(tr(gettext("Lanzando el comando: "))+string);
546    }
547    startProgressBar();
548}
549
550// Returns communication speed
551QString MainWindow::readSpeed() {
552    if(m_env.contains("OGLOGFILE"))
553    {
554        QString infoFile=m_env["OGLOGFILE"].replace(".log", ".info.html");
555        //QString command="grep -hoe \"[0-9]*Mb/s\" "+infoFile+" 2>/dev/null";
556        QProcess process;
557        process.start("grep", QStringList({"-hoe", "[0-9]*Mb/s", infoFile}));
558        process.waitForFinished();
559        QString speed(process.readAllStandardOutput());
560        return speed.simplified();
561    }
562    else
563    {
564        return QString("");
565    }
566}
567
568// Show an error box with timeout
569void MainWindow::showErrorMessage(QString text)
570{
571    QMessageBox* msgBox=new QMessageBox();
572    msgBox->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
573    msgBox->setWindowTitle(gettext("ERROR"));
574    msgBox->setIcon(QMessageBox::Warning);
575    msgBox->setText(text);
576    msgBox->show();
577    QTimer::singleShot(5000, msgBox, SLOT(close()));
578}
Note: See TracBrowser for help on using the repository browser.