File indexing completed on 2024-05-19 04:59:18

0001 /* ============================================================
0002 * Personal Information Manager plugin for Falkon
0003 * Copyright (C) 2012-2016 David Rosca <nowrep@gmail.com>
0004 * Copyright (C) 2012-2014 Mladen Pejaković <pejakm@autistici.org>
0005 *
0006 * This program is free software: you can redistribute it and/or modify
0007 * it under the terms of the GNU General Public License as published by
0008 * the Free Software Foundation, either version 3 of the License, or
0009 * (at your option) any later version.
0010 *
0011 * This program is distributed in the hope that it will be useful,
0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 * GNU General Public License for more details.
0015 *
0016 * You should have received a copy of the GNU General Public License
0017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018 * ============================================================ */
0019 #include "PIM_handler.h"
0020 #include "PIM_settings.h"
0021 #include "webview.h"
0022 #include "webpage.h"
0023 #include "webhittestresult.h"
0024 
0025 #include <QApplication>
0026 #include <QSettings>
0027 #include <QLabel>
0028 #include <QToolTip>
0029 #include <QKeyEvent>
0030 
0031 PIM_Handler::PIM_Handler(const QString &sPath, QObject* parent)
0032     : QObject(parent)
0033     , m_settingsFile(sPath + QL1S("/extensions.ini"))
0034     , m_loaded(false)
0035 {
0036 }
0037 
0038 void PIM_Handler::loadSettings()
0039 {
0040     QSettings settings(m_settingsFile, QSettings::IniFormat);
0041 
0042     settings.beginGroup(QSL("PIM"));
0043     m_allInfo[PI_LastName] = settings.value(QSL("LastName"), QString()).toString();
0044     m_allInfo[PI_FirstName] = settings.value(QSL("FirstName"), QString()).toString();
0045     m_allInfo[PI_Email] = settings.value(QSL("Email"), QString()).toString();
0046     m_allInfo[PI_Mobile] = settings.value(QSL("Mobile"), QString()).toString();
0047     m_allInfo[PI_Phone] = settings.value(QSL("Phone"), QString()).toString();
0048     m_allInfo[PI_Address] = settings.value(QSL("Address"), QString()).toString();
0049     m_allInfo[PI_City] = settings.value(QSL("City"), QString()).toString();
0050     m_allInfo[PI_Zip] = settings.value(QSL("Zip"), QString()).toString();
0051     m_allInfo[PI_State] = settings.value(QSL("State"), QString()).toString();
0052     m_allInfo[PI_Country] = settings.value(QSL("Country"), QString()).toString();
0053     m_allInfo[PI_HomePage] = settings.value(QSL("HomePage"), QString()).toString();
0054     m_allInfo[PI_Special1] = settings.value(QSL("Special1"), QString()).toString();
0055     m_allInfo[PI_Special2] = settings.value(QSL("Special2"), QString()).toString();
0056     m_allInfo[PI_Special3] = settings.value(QSL("Special3"), QString()).toString();
0057     settings.endGroup();
0058 
0059     m_translations[PI_LastName] = tr("Last Name");
0060     m_translations[PI_FirstName] = tr("First Name");
0061     m_translations[PI_Email] = tr("E-mail");
0062     m_translations[PI_Mobile] = tr("Mobile");
0063     m_translations[PI_Phone] = tr("Phone");
0064     m_translations[PI_Address] = tr("Address");
0065     m_translations[PI_City] = tr("City");
0066     m_translations[PI_Zip] = tr("ZIP Code");
0067     m_translations[PI_State] = tr("State/Region");
0068     m_translations[PI_Country] = tr("Country");
0069     m_translations[PI_HomePage] = tr("Home Page");
0070     m_translations[PI_Special1] = tr("Custom 1");
0071     m_translations[PI_Special2] = tr("Custom 2");
0072     m_translations[PI_Special3] = tr("Custom 3");
0073 
0074     m_infoMatches[PI_LastName] << QSL("lastname") << QSL("surname");
0075     m_infoMatches[PI_FirstName] << QSL("firstname") << QSL("name");
0076     m_infoMatches[PI_Email] << QSL("email") << QSL("e-mail") << QSL("mail");
0077     m_infoMatches[PI_Mobile] << QSL("mobile") << QSL("mobilephone");
0078     m_infoMatches[PI_Phone] << QSL("phone") << QSL("telephone");
0079     m_infoMatches[PI_Address] << QSL("address");
0080     m_infoMatches[PI_City] << QSL("city");
0081     m_infoMatches[PI_Zip] << QSL("zip");
0082     m_infoMatches[PI_State] << QSL("state") << QSL("region");
0083     m_infoMatches[PI_Country] << QSL("country");
0084     m_infoMatches[PI_HomePage] << QSL("homepage") << QSL("www");
0085 
0086     m_loaded = true;
0087 }
0088 
0089 void PIM_Handler::showSettings(QWidget* parent)
0090 {
0091     if (!m_settings) {
0092         m_settings = new PIM_Settings(m_settingsFile, parent);
0093 
0094         connect(m_settings.data(), &QDialog::accepted, this, &PIM_Handler::loadSettings);
0095     }
0096 
0097     m_settings.data()->show();
0098     m_settings.data()->raise();
0099 }
0100 
0101 void PIM_Handler::populateWebViewMenu(QMenu* menu, WebView* view, const WebHitTestResult &hitTest)
0102 {
0103     m_view = view;
0104     m_clickedPos = hitTest.pos();
0105 
0106     if (!hitTest.isContentEditable()) {
0107         return;
0108     }
0109 
0110     if (!m_loaded) {
0111         loadSettings();
0112     }
0113 
0114     auto* pimMenu = new QMenu(tr("Insert Personal Information"));
0115     pimMenu->setIcon(QIcon(QStringLiteral(":/PIM/data/PIM.png")));
0116 
0117     if (!m_allInfo[PI_FirstName].isEmpty() && !m_allInfo[PI_LastName].isEmpty()) {
0118         const QString fullname = m_allInfo[PI_FirstName] + QLatin1Char(' ') + m_allInfo[PI_LastName];
0119         QAction* action = pimMenu->addAction(fullname, this, &PIM_Handler::pimInsert);
0120         action->setData(fullname);
0121     }
0122 
0123     for (int i = 0; i < PI_Max; ++i) {
0124         const QString info = m_allInfo[PI_Type(i)];
0125         if (info.isEmpty()) {
0126             continue;
0127         }
0128 
0129         QAction* action = pimMenu->addAction(info, this, &PIM_Handler::pimInsert);
0130         action->setData(info);
0131         action->setStatusTip(m_translations[PI_Type(i)]);
0132     }
0133 
0134     pimMenu->addSeparator();
0135     pimMenu->addAction(tr("Edit"), this, SLOT(showSettings()));
0136 
0137     menu->addMenu(pimMenu);
0138     menu->addSeparator();
0139 }
0140 
0141 bool PIM_Handler::keyPress(WebView* view, QKeyEvent* event)
0142 {
0143     if (!view) {
0144         return false;
0145     }
0146 
0147     bool isEnter = event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter;
0148     bool isControlModifier = event->modifiers() & Qt::ControlModifier;
0149 
0150     if (!isEnter || !isControlModifier) {
0151         return false;
0152     }
0153 
0154     QString source = QL1S("var inputs = document.getElementsByTagName('input');"
0155                           "var table = %1;"
0156                           "for (var i = 0; i < inputs.length; ++i) {"
0157                           "    var input = inputs[i];"
0158                           "    if (input.type != 'text' || input.name == '')"
0159                           "        continue;"
0160                           "    for (var key in table) {"
0161                           "        if (!table.hasOwnProperty(key))"
0162                           "            continue;"
0163                           "        if (key == input.name || input.name.indexOf(key) != -1) {"
0164                           "            input.value = table[key];"
0165                           "            break;"
0166                           "        }"
0167                           "    }"
0168                           "}");
0169 
0170     view->page()->runJavaScript(source.arg(matchingJsTable()), WebPage::SafeJsWorld);
0171 
0172     return true;
0173 }
0174 
0175 void PIM_Handler::unloadPlugin()
0176 {
0177     delete m_settings.data();
0178 }
0179 
0180 void PIM_Handler::webPageCreated(WebPage* page)
0181 {
0182     connect(page, &QWebEnginePage::loadFinished, this, &PIM_Handler::pageLoadFinished);
0183 }
0184 
0185 void PIM_Handler::pimInsert()
0186 {
0187     if (!m_view || m_clickedPos.isNull())
0188         return;
0189 
0190     auto* action = qobject_cast<QAction*>(sender());
0191     if (!action)
0192         return;
0193 
0194     QString info = action->data().toString();
0195     info.replace(QLatin1Char('"'), QLatin1String("\\\""));
0196 
0197     QString source = QL1S("var e = document.elementFromPoint(%1, %2);"
0198                           "if (e) {"
0199                           "    var v = e.value.substring(0, e.selectionStart);"
0200                           "    v += \"%3\" + e.value.substring(e.selectionEnd);"
0201                           "    e.value = v;"
0202                           "}");
0203     source = source.arg(m_clickedPos.x()).arg(m_clickedPos.y()).arg(info);
0204     m_view->page()->runJavaScript(source, WebPage::SafeJsWorld);
0205 }
0206 
0207 void PIM_Handler::pageLoadFinished()
0208 {
0209     auto* page = qobject_cast<WebPage*>(sender());
0210     if (!page) {
0211         return;
0212     }
0213 
0214     if (!m_loaded) {
0215         loadSettings();
0216     }
0217 
0218     QString source = QL1S("var inputs = document.getElementsByTagName('input');"
0219                           "var table = %1;"
0220                           "for (var i = 0; i < inputs.length; ++i) {"
0221                           "    var input = inputs[i];"
0222                           "    if (input.type != 'text' || input.name == '')"
0223                           "        continue;"
0224                           "    for (var key in table) {"
0225                           "        if (!table.hasOwnProperty(key) || table[key] == '')"
0226                           "            continue;"
0227                           "        if (key == input.name || input.name.indexOf(key) != -1) {"
0228                           "            input.style['-webkit-appearance'] = 'none';"
0229                           "            input.style['-webkit-box-shadow'] = 'inset 0 0 2px 1px #EEE000';"
0230                           "            break;"
0231                           "        }"
0232                           "    }"
0233                           "}");
0234 
0235     page->runJavaScript(source.arg(matchingJsTable()), WebPage::SafeJsWorld);
0236 }
0237 
0238 QString PIM_Handler::matchingJsTable() const
0239 {
0240     QString values;
0241 
0242     QHashIterator<PI_Type, QStringList> i(m_infoMatches);
0243     while (i.hasNext()) {
0244         i.next();
0245 
0246         const auto ivalues = i.value();
0247         for (const QString &value : ivalues) {
0248             QString key = m_allInfo.value(i.key());
0249             key.replace(QL1C('"'), QL1S("\\\""));
0250             values.append(QSL("\"%1\":\"%2\",").arg(value, key));
0251         }
0252     }
0253 
0254     if (!values.isEmpty()) {
0255         values = values.left(values.size() - 1);
0256     }
0257 
0258     return QSL("{ %1 }").arg(values);
0259 }