Commit 66b750f6 authored by Karel Slaný's avatar Karel Slaný

Merge branch 'zfo-import' into 'develop'

Zfo import

See merge request !104
parents 31933226 9642e4f4
......@@ -145,6 +145,7 @@ SOURCES += \
src/worker/task_download_message_list.cpp \
src/worker/task_find_databox.cpp \
src/worker/task_find_databox_fulltext.cpp \
src/worker/task_import_zfo.cpp \
src/worker/task_keep_alive.cpp \
src/worker/task_send_message.cpp \
src/worker/task_send_sms.cpp \
......@@ -209,6 +210,7 @@ HEADERS += \
src/worker/task_download_message_list.h \
src/worker/task_find_databox.h \
src/worker/task_find_databox_fulltext.h \
src/worker/task_import_zfo.h \
src/worker/task_keep_alive.h \
src/worker/task_send_message.h \
src/worker/task_send_sms.h \
......
......@@ -43,8 +43,8 @@ Dialog {
}
AccessibleButton {
text: qsTr("OK")
enabled: (selectedFileIndex != -1)
visible: (selectedFileIndex != -1)
enabled: ((root.showDirs && !root.showFiles) || (selectedFileIndex != -1))
visible: ((root.showDirs && !root.showFiles) || (selectedFileIndex != -1))
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
}
}
......@@ -60,13 +60,19 @@ Dialog {
property bool multiSelect: false
property int selectedFileIndex: -1
property bool showDirs: true
property bool showFiles: true
signal finished(variant pathListModel)
function directoryOnly() {
return (root.showDirs && !root.showFiles);
}
function raise(title, filters, showFiles) {
root.title = title
folderModel.folder = folderModel.folder
folderModel.showFiles = showFiles
root.showFiles = showFiles
if (filters !== "") {
folderModel.nameFilters = [filters]
}
......@@ -77,6 +83,11 @@ Dialog {
for (fileList.currentIndex = 0; fileList.currentIndex < fileList.count; ++fileList.currentIndex) {
fileList.currentItem.color = datovkaPalette.window
}
if (directoryOnly()) {
var path = stripUrlPrefix(folderModel.folder)
pathListModel.clear()
pathListModel.append({path: path})
}
root.open()
}
......@@ -123,7 +134,7 @@ Dialog {
ListElement { label: qsTr("Pictures"); key: "picture" }
ListElement { label: qsTr("Temp"); key: "temp" }
}
onActivated: {
onCurrentIndexChanged: {
var location = InteractionFilesystem.DESKTOP_LOCATION
if (currentKey() === "document") {
location = InteractionFilesystem.DOCUMENTS_LOCATION
......@@ -185,6 +196,8 @@ Dialog {
Layout.fillWidth: true
FolderListModel {
id: folderModel
showDirs: root.showDirs
showFiles: root.showFiles
showDirsFirst: true
nameFilters: ["*.*"]
folder: standardLocationUrl(InteractionFilesystem.DESKTOP_LOCATION)
......@@ -237,13 +250,19 @@ Dialog {
}
function handleClick() {
var path
if (fileIsDir) {
/* Navigate to selected directory. */
folderModel.folder = fileURL
if (directoryOnly()) {
pathListModel.clear()
path = stripUrlPrefix(folderModel.folder)
pathListModel.append({path: path})
}
} else {
/* Select file. */
selectedFileIndex = index
var path = stripUrlPrefix(folderModel.folder) + "/" + folderModel.get(selectedFileIndex, "fileName")
path = stripUrlPrefix(folderModel.folder) + "/" + folderModel.get(selectedFileIndex, "fileName")
if (!multiSelect) {
// only one file can be selected and append to list
pathListModel.clear()
......
......@@ -68,6 +68,7 @@ ApplicationWindow {
property Component pageContactList: PageContactList {}
property Component pageDataboxDetail: PageDataboxDetail {}
property Component pageDataboxSearch: PageDataboxSearch {}
property Component pageImportMessage: PageImportMessage {}
property Component pageMenuAccount: PageMenuAccount {}
property Component pageMenuDatovkaSettings: PageMenuDatovkaSettings {}
property Component pageMenuMessage: PageMenuMessage {}
......
/*
* Copyright (C) 2014-2018 CZ.NIC
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtGraphicalEffects 1.0
import cz.nic.mobileDatovka 1.0
Component {
id: importMessage
Item {
id: mainLayout
/* These properties must be set by caller. */
property var pageView
property var statusBar
property string acntName
property string userName
property var accountModel: null
/* Clear import info and import results */
function clearInfo() {
infoText.text = ""
progressText.text = ""
}
Component.onCompleted: {
clearInfo()
}
Component.onDestruction: {
accounts.updateAccountCounters(accountModel)
statusBar.visible = false
}
/* File dialog for choosing of files/directories from the storage. */
FileDialogue {
id: fileDialogue
multiSelect: true
onFinished: {
var files = []
for (var j = 0; j < pathListModel.count; ++j) {
files.push(pathListModel.get(j).path)
}
clearInfo()
infoText.text = isds.importZfoMessages(userName, files, verifyMessage.checked)
pathListModel.clear()
}
}
PageHeader {
id: headerBar
title: qsTr("ZFO message import")
onBackClicked: {
pageView.pop(StackView.Immediate)
}
}
/* Object (associative array) holding functions. */
property var funcs: {
"zfoFile": function callZfoFile() {
clearInfo()
fileDialogue.raise(qsTr("Select ZFO files"), "*.zfo", true)
},
"zfoDir": function callZfoDir() {
clearInfo()
fileDialogue.raise(qsTr("Select import directory"), "*.*", false)
}
}
ListModel {
id: importMenuListModel
ListElement {
image: "qrc:/ui/datovka-file-zfo.svg"
showEntry: true
showNext: true
name: qsTr("Import selected ZFO files")
funcName: "zfoFile"
}
ListElement {
image: "qrc:/ui/datovka-folder-open.svg"
showEntry: true
showNext: true
name: qsTr("Import ZFO files from directory")
funcName: "zfoDir"
}
}
Flickable {
id: flickable
z: 0
anchors.top: headerBar.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
contentHeight: flickContent.implicitHeight
Pane {
id: flickContent
anchors.fill: parent
Column {
anchors.right: parent.right
anchors.left: parent.left
spacing: formItemVerticalSpacing
AccessibleText {
color: datovkaPalette.mid
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
width: parent.width
text: qsTr("Here you can import messages from ZFO files into the local database. You can manually specify individual ZFO files or you can select whole directories containing ZFO files.")
}
AccessibleSwitch {
id: verifyMessage
text: qsTr("Verify data on server")
font.pointSize: defaultTextFont.font.pointSize
checked: false
}
AccessibleText {
color: datovkaPalette.mid
wrapMode: Text.Wrap
width: parent.width
text: (verifyMessage.checked ?
(qsTr("Messages will be validated on the ISDS server before being imported. Imported messages are going to be sent to the ISDS server where they are going to be checked. Messages failing this test won't be imported.")
+ "\n" + qsTr("Working internet connection is required. The duration of the operation depends on the speed of your internet connection. The size of the generated upload traffic depends on the amount of imported data.")
+ "\n" + qsTr("We don't recommend using this option when you are using a mobile internet connection.")) :
qsTr("Imported messages won't be validated on the ISDS server."))
}
AccessibleMenu {
id: importMenuList
height: 2 * delegateHeight
width: parent.width
clip: true
spacing: 1
opacity: 1
funcArr: funcs
model: importMenuListModel
}
Text {
text: " "
}
AccessibleText {
id: infoText
color: datovkaPalette.text
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
width: parent.width
font.bold: true
text: ""
}
AccessibleText {
id: progressText
color: datovkaPalette.text
wrapMode: Text.Wrap
width: parent.width
font.pointSize: textFontSizeSmall
textFormat: TextEdit.RichText
text: ""
}
} // Column layout
} // Pane
ScrollIndicator.vertical: ScrollIndicator {}
} // Flickable
Connections {
target: isds
onZfoImportFinishedSig: {
infoText.text = txt
}
}
Connections {
target: isds
onZfoImportProgressSig: {
progressText.text = progressText.text + "<br/>" + txt
}
}
}
}
......@@ -73,6 +73,15 @@ Component {
"action": "new"
}, StackView.Immediate)
},
"importMsg": function callImportMsg() {
pageView.replace(pageImportMessage, {
"pageView": pageView,
"statusBar": statusBar,
"acntName" : acntName,
"userName": userName,
"accountModel": accountModel
}, StackView.Immediate)
},
"findBox": function callFindBox() {
pageView.replace(pageDataboxSearch, {
"pageView": pageView,
......@@ -123,6 +132,13 @@ Component {
name: qsTr("Create message")
funcName: "createMsg"
}
ListElement {
image: "qrc:/ui/file-import.svg"
showEntry: true
showNext: true
name: qsTr("Import message")
funcName: "importMsg"
}
ListElement {
image: "qrc:/ui/account-search.svg"
showEntry: true
......
......@@ -68,6 +68,15 @@ Component {
"statusBar": statusBar
}, StackView.Immediate)
},
"importMsg": function callImportMsg() {
pageView.replace(pageImportMessage, {
"pageView": pageView,
"statusBar": statusBar,
"acntName" : "",
"userName": "",
"accountModel": accountModel
}, StackView.Immediate)
},
"settGeneral": function callSettGeneral() {
pageView.replace(pageSettingsGeneral, {
"pageView": pageView,
......@@ -118,6 +127,13 @@ Component {
name: qsTr("Search message")
funcName: "searchMsg"
}
ListElement {
image: "qrc:/ui/file-import.svg"
showEntry: true
showNext: true
name: qsTr("Import message")
funcName: "importMsg"
}
ListElement {
image: "qrc:/ui/settings.svg"
showEntry: true
......
This diff is collapsed.
This diff is collapsed.
......@@ -131,6 +131,7 @@
<file>../qml/pages/PageContactList.qml</file>
<file>../qml/pages/PageDataboxDetail.qml</file>
<file>../qml/pages/PageDataboxSearch.qml</file>
<file>../qml/pages/PageImportMessage.qml</file>
<file>../qml/pages/PageMenuAccount.qml</file>
<file>../qml/pages/PageMenuDatovkaSettings.qml</file>
<file>../qml/pages/PageMenuMessage.qml</file>
......
/*
* Copyright (C) 2014-2017 CZ.NIC
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -47,6 +47,21 @@ bool Accounts::boxEffectiveOVM(const QString &userName)
return globAccountDbPtr->boxEffectiveOVM(userName);
}
void Accounts::updateAccountCounters(const QVariant &acntModelVariant)
{
qDebug("%s()", __func__);
AccountListModel *accountModel =
AccountListModel::fromVariant(acntModelVariant);
if (accountModel == Q_NULLPTR) {
Q_ASSERT(0);
qCritical("%s", "Cannot access account model.");
return;
}
loadModelCounters(accountModel);
}
void Accounts::updateNewMessageCounter(const QVariant &acntModelVariant,
const QString &userName)
{
......
/*
* Copyright (C) 2014-2017 CZ.NIC
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -56,6 +56,14 @@ public:
Q_INVOKABLE static
bool boxEffectiveOVM(const QString &userName);
/*!
* @brief Update message counters in account list.
*
* @param[in] accountModel Model whose counters should be updated.
*/
Q_INVOKABLE static
void updateAccountCounters(const QVariant &acntModelVariant);
/*!
* @brief Update new message counter.
*
......
/*
* Copyright (C) 2014-2017 CZ.NIC
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -23,6 +23,7 @@
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QRegExp>
#include <QStandardPaths>
#include <QTextStream>
......@@ -190,3 +191,27 @@ void deleteFilesFromDir(const QString &dirName)
QDir dir(filePath);
dir.removeRecursively();
}
QStringList getZfoFilesFromDir(const QString &importDir, bool includeSubDir)
{
QStringList filters("*.zfo");
QStringList fileList, filePathList;
QDir directory(QDir::home());
if (includeSubDir) {
QDirIterator it(importDir, filters, QDir::Files,
QDirIterator::Subdirectories);
while (it.hasNext()) {
filePathList.append(it.next());
}
} else {
directory.setPath(importDir);
fileList = directory.entryList(filters);
foreach (const QString &file, fileList) {
filePathList.append(importDir +
QDir::separator() + file);
}
}
return filePathList;
}
/*
* Copyright (C) 2014-2017 CZ.NIC
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -103,4 +103,13 @@ QString writeFileToDir(const QString &dirName, qint64 msgId,
*/
void deleteFilesFromDir(const QString &dirName);
/*!
* @brief Get paths of ZFO files from selected folder.
*
* @param[in] importDir Import source directory path.
* @param[in] includeSubDir Include subdirectories.
* @return List of full path to ZFO files.
*/
QStringList getZfoFilesFromDir(const QString &importDir, bool includeSubDir);
#endif /* _FILESYSTEM_H_ */
......@@ -100,6 +100,7 @@ const struct QmlTypeEntry qmlPages[] = {
{ "PageContactList", 1, 0 },
{ "PageDataboxDetail", 1, 0 },
{ "PageDataboxSearch", 1, 0 },
{ "PageImportMessage", 1, 0 },
{ "PageMenuAccount", 1, 0 },
{ "PageMenuDatovkaSettings", 1, 0 },
{ "PageMenuMessage", 1, 0 },
......
/*
* Copyright (C) 2014-2017 CZ.NIC
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -27,6 +27,7 @@
#include "src/auxiliaries/attachment_helper.h"
#include "src/datovka_shared/utility/strings.h"
#include "src/dialogues/dialogues.h"
#include "src/io/filesystem.h"
#include "src/models/accountmodel.h"
#include "src/models/databoxmodel.h"
#include "src/models/messagemodel.h"
......@@ -44,6 +45,7 @@
#include "src/worker/task_download_message_list.h"
#include "src/worker/task_find_databox.h"
#include "src/worker/task_find_databox_fulltext.h"
#include "src/worker/task_import_zfo.h"
#include "src/worker/task_keep_alive.h"
#include "src/worker/task_send_sms.h"
......@@ -81,6 +83,9 @@ IsdsWrapper::IsdsWrapper(QObject *parent)
this,
SLOT(downloadMessageListFinished(QString, bool, QString, QString, bool)));
connect(&globMsgProcEmitter, SIGNAL(importZFOFinishedSignal(int, int, QString)),
this, SLOT(importZFOFinished(int, int, QString)));
connect(&globMsgProcEmitter,
SIGNAL(sendMessageFinishedSignal(QString, QString, QString, QString, qint64, bool, QString)),
this,
......@@ -93,6 +98,69 @@ IsdsWrapper::~IsdsWrapper(void)
m_workPool.stop();
}
QString IsdsWrapper::importZfoMessages(const QString &userName,
const QStringList &fileList, bool authenticate)
{
qDebug("%s()", __func__);
if (fileList.isEmpty()) {
return tr("No zfo files or selected folder.");
}
/* Get user names of all accounts if an user name is not specified */
QStringList userNameList;
if (userName.isEmpty()) {
userNameList = AccountListModel::globAccounts.keys();
} else {
userNameList.append(userName);
}
if (userNameList.isEmpty()) {
return tr("No account for zfo import.");
}
/* User must be logged to isds if messages must be verified */
if (authenticate) {
foreach (const QString &userName, userNameList) {
if (!isLoggedToIsds(userName)) {
continue;
}
}
}
/* Test, if folder or file(s) was selected */
QStringList filePathList = fileList;
if (1 == fileList.count()) {
if (!QFileInfo(fileList.at(0)).isFile()) {
filePathList = getZfoFilesFromDir(fileList.at(0), true);
if (filePathList.isEmpty()) {
return tr("No zfo files in the selected directory.");
}
}
}
int zfoTotal = filePathList.count();
int zfoNumber = 0;
if (zfoTotal < 1) {
return tr("No zfo file for import.");
}
emit statusBarTextChanged(tr("ZFO import"), true, true);
foreach (const QString &file, filePathList) {
zfoNumber++;
TaskImportZfo *task;
task = new (std::nothrow) TaskImportZfo(&m_isdsSession,
&m_netLayer, &m_dbWrapper, userNameList, file,
authenticate, zfoNumber, zfoTotal);
task->setAutoDelete(true);
m_workPool.assignLo(task);
}
return tr("ZFO import is running... Wait until import will finished!");
}
bool IsdsWrapper::changePassword(const QString &userName, const QString &oldPwd,
const QString &newPwd, const QString &otpCode)
{
......@@ -798,6 +866,21 @@ void IsdsWrapper::downloadMessageListFinished(const QString &userName,
}
}
void IsdsWrapper::importZFOFinished(int zfoNumber, int zfoTotal,
QString infoText)
{
if (zfoNumber < zfoTotal) {
emit zfoImportProgressSig(QString::number(zfoNumber) + ". " + infoText);
emit statusBarTextChanged(
tr("ZFO import: %1/%2").arg(zfoNumber).arg(zfoTotal), true,
true);
} else {
emit zfoImportProgressSig(QString::number(zfoNumber) + ". " + infoText);
emit zfoImportFinishedSig(tr("ZFO import has been finished with the result:"));
emit statusBarTextChanged(tr("ZFO import: done"), false, true);
}
}
void IsdsWrapper::sendMessageFinished(const QString &userName,
const QString &transactId, const QString &dbIDRecipient,
const QString &dmRecipient, qint64 msgId, bool success,
......
/*
* Copyright (C) 2014-2017 CZ.NIC
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -54,6 +54,18 @@ public:
*/
~IsdsWrapper(void);
/*!
* @brief Import ZFO files to local database.
*
* @param[in] userName Account username string (may be NULL).
* @param[in] fileList File list or dir path.
* @param[in] authenticate True if message must be authenticate in ISDS.
* @return info/error text.
*/
Q_INVOKABLE
QString importZfoMessages(const QString &userName,
const QStringList &fileList, bool authenticate);
/*!
* @brief Change ISDS login password.
*
......@@ -415,6 +427,20 @@ signals:
*/
void unsuccessedLoginToIsdsSig(QString userName);
/*!
* @brief Send info to QML that ZFO import has been finished.
*
* @param[in] txt Result text.
*/
void zfoImportFinishedSig(QString txt);
/*!
* @brief Send info to QML that one ZFO file has been imported.
*
* @param[in] txt Result text.
*/
void zfoImportProgressSig(QString txt);
public slots:
/*!
......@@ -462,6 +488,16 @@ public slots:
bool success, const QString &statusBarText, const QString &errTxt,
bool isMsgReceived);
/*!
* @brief Do some actions when import of one ZFO has been finished.
*
* @param[in] zfoNumber ZFO number in import process.
* @param[in] zfoTotal Overall number of ZFO for import.
* @param[in] infoText Import result description (may be NULL).
*/
void importZFOFinished(int zfoNumber, int zfoTotal,
QString infoText);
/*!
* @brief Do post actions when send message finished.
*
......
/*
* Copyright (C) 2014-2017 CZ.NIC
* Copyright (C) 2014-2018 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -36,6 +36,18 @@ XmlLayer::XmlLayer(QObject *parent)
{
}
QByteArray XmlLayer::xmlCreateAuthenticateMessageSoapRequest(
const QByteArray &msg)
{
QString xmlContent("<AuthenticateMessage xmlns=\"");
xmlContent.append(ISDS_NS);
xmlContent.append("\"><dmMessage>");
xmlContent.append(msg);
xmlContent.append("</dmMessage>");
xmlContent.append("</AuthenticateMessage>");
return xmlCreateSoapEnvelope(xmlContent);
}
QByteArray XmlLayer::xmlCreateDownloadMessageSoapRequest(qint64 msgId,
enum Messages::MessageType msgDirect)
{
......@@ -1506,3 +1518,93 @@ bool XmlLayer::parseFindDataBoxSoapResponse(const QByteArray &xmlData,
return ret;
}
bool XmlLayer::parseZFO(const QByteArray &content, MsgEnvelope &msg,
QList<AttachmentData> &fileList, QString &txt)
{
qDebug("%s()", __func__);
QByteArray cmsData = content;
/* decode cms and obtain message xml data - used openssl */
void *xmlContent = NULL;
size_t xmlContentLen = 0;
if (extract_cms_data(cmsData.data(), cmsData.length(), &xmlContent,
&xmlContentLen) != 0) {
txt = tr("ZFO format invalid.");
return false;
}
if (xmlContentLen == 0) {
free(xmlContent); xmlContent = NULL;
txt = tr("ZFO format invalid.");
return false;
}
QByteArray soap((char*)xmlContent, xmlContentLen);
free(xmlContent); xmlContent = NULL;
/* add xml SOAP header anf footer */
soap.prepend("<?xml version='1.0' encoding='utf-8'?>"
"<SOAP-ENV:Envelope "
"xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
"<SOAP-ENV:Body>");