Change 'EXECUTABLE-config' URI handler to request a confirmation to the user before using it.

Delay its execution on startup.
Add auto_apply_provisioning_config_uri_handler rc to remove confirmations.
Default URL uses https.
Fix dialog description with wrapping text and elides.
Fix busy color.
Fix duplicate connections on restarting App that done multiple CLI commands.
This commit is contained in:
Julien Wadel 2023-03-30 18:19:23 +02:00
parent d2221c9a43
commit 6d454b659c
25 changed files with 214 additions and 67 deletions

View file

@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Display last seen for contacts.
- New language support: Czech
- An option to set dial prefix and its use on numbers.
- Fetch remote provisioning from URI handler and with confirmation.
## 5.0.15 - undefined

View file

@ -1823,6 +1823,11 @@ Klikněte zde: <a href="%1">%1</a>
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation>URI videokonference není nastaven. Abyste mohli vytvářet nové konference, musíte jej změnit v nastavení účtu.</translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1813,6 +1813,11 @@ Klik her: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1813,6 +1813,11 @@ Klicken Sie hier: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1813,6 +1813,11 @@ Click here: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation>Video conference URI is not set. You have to change it in your account settings in order to create new conferences.</translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation>Do you want to download and apply configuration from this URL?</translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1813,6 +1813,11 @@ Haga clic aquí: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1813,6 +1813,11 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation>L&apos;URI de conférence vidéo n&apos;a pas é renseigné. Vous devez la mettre à jour dans les options de comptes pour pouvoir créer de nouvelles réunions.</translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1803,6 +1803,11 @@ Kattintson ide: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1813,6 +1813,11 @@ Clicca: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation>L&apos;URI della video conferenza non è stato impostato. Devi cambiarlo nelle impostazioni del tuo account per poter creare nuove conferenze.</translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1803,6 +1803,11 @@
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation>URIが設定されていません</translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1823,6 +1823,11 @@ Spustelėkite čia: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1813,6 +1813,11 @@ Clique aqui: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation>A URI não está configurada para videoconferência. Você tem que mudá-lo nas configurações de sua conta a fim de criar novas conferências.</translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1823,6 +1823,11 @@
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation>URI видеоконференции не задан. Измените его в параметрах своей учётной записи, чтобы создавать новые конференции.</translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1813,6 +1813,11 @@ Klicka här: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1803,6 +1803,11 @@ Buraya tıklayın: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1823,6 +1823,11 @@
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -1803,6 +1803,11 @@
<extracomment>&apos;Video conference URI is not set. You have to change it in your account settings in order to create new meetings.&apos; : Tooltip to warn the user to change a setting to activate an action.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirmFetchUri</source>
<extracomment>&apos;Do you want to download and apply configuration from this URL?&apos; : text to confirm to fetch a specified URL</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindowMenuBar</name>

View file

@ -165,34 +165,60 @@ static inline string getConfigPathIfExists (const QCommandLineParser &parser) {
return configPath;
}
bool App::setFetchConfig (QCommandLineParser *parser) {
bool fetched = false;
QString filePath = parser->value("fetch-config");
QString App::getFetchConfig (QString filePath, bool * error) {
*error = false;
if( !filePath.isEmpty()){
if(QUrl(filePath).isRelative()){// this is a file path
filePath = Utils::coreStringToAppString(Paths::getConfigFilePath(filePath, false));
if(!filePath.isEmpty())
filePath = "file://"+filePath;
}
if(!filePath.isEmpty()){
auto instance = CoreManager::getInstance();
if(instance){
auto core = instance->getCore();
if(core){
filePath.replace('\\','/');
if(core->setProvisioningUri(Utils::appStringToCoreString(filePath)) == 0){
parser->process(cleanParserKeys(parser, QStringList("fetch-config")));// Remove this parameter from the parser
fetched = true;
}else
fetched = false;
}
}
}
if(!fetched){
if(filePath.isEmpty()){
qWarning() <<"Remote provisionning cannot be retrieved. Command have beend cleaned";
createParser();
*error = true;
}
}
return filePath;
}
QString App::getFetchConfig (QCommandLineParser *parser) {
QString filePath = parser->value("fetch-config");
bool error = false;
filePath = getFetchConfig(filePath, &error);
if(error) {
qWarning() <<"Remote provisionning cannot be retrieved. Command have beend cleaned";
createParser();
}else if( !filePath.isEmpty())
mParser->process(cleanParserKeys(mParser, QStringList("fetch-config")));// Remove this parameter from the parser
return filePath;
}
void App::useFetchConfig(const QString& filePath){
if( !filePath.isEmpty()){
if(CoreManager::getInstance()->getSettingsModel()->getAutoApplyProvisioningConfigUriHandlerEnabled())
setFetchConfig(filePath);
else
emit requestFetchConfig(filePath);
}
}
bool App::setFetchConfig (QString filePath) {
bool fetched = false;
qDebug() << "setFetchConfig with " << filePath;
if(!filePath.isEmpty()){
auto instance = CoreManager::getInstance();
if(instance){
auto core = instance->getCore();
if(core){
filePath.replace('\\','/');
fetched = core->setProvisioningUri(Utils::appStringToCoreString(filePath)) == 0;
}
}
}
if(!fetched){
qWarning() <<"Remote provisionning cannot be retrieved. Command have beend cleaned";
}else
restart();
return fetched;
}
// -----------------------------------------------------------------------------
@ -255,6 +281,13 @@ App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::U
qInfo() << QStringLiteral("Starting " APPLICATION_NAME " (bin: " EXECUTABLE_NAME ")");
qInfo() << QStringLiteral("Use locale: %1 with language: %2").arg(mLocale.name()).arg(QLocale::languageToString(mLocale.language()));
// Deal with received messages and CLI.
QObject::connect(this, &App::receivedMessage, this, [](int, const QByteArray &byteArray) {
QString command(byteArray);
qInfo() << QStringLiteral("Received command from other application: `%1`.").arg(command);
Cli::executeCommand(command);
});
}
App::~App () {
@ -321,6 +354,7 @@ static QQuickWindow *createSubWindow (QQmlApplicationEngine *engine, const char
// -----------------------------------------------------------------------------
void App::initContentApp () {
std::string configPath;
shared_ptr<linphone::Config> config;
bool mustBeIconified = false;
@ -329,7 +363,6 @@ void App::initContentApp () {
// Destroy qml components and linphone core if necessary.
if (mEngine) {
needRestart = false;
setFetchConfig(mParser);
setOpened(false);
qInfo() << QStringLiteral("Restarting app...");
@ -357,12 +390,7 @@ void App::initContentApp () {
// Don't quit if last window is closed!!!
setQuitOnLastWindowClosed(false);
// Deal with received messages and CLI.
QObject::connect(this, &App::receivedMessage, this, [](int, const QByteArray &byteArray) {
QString command(byteArray);
qInfo() << QStringLiteral("Received command from other application: `%1`.").arg(command);
Cli::executeCommand(command);
});
#ifndef Q_OS_MACOS
mustBeIconified = mParser->isSet("iconified");
@ -453,8 +481,9 @@ void App::initContentApp () {
CoreManager::getInstance(),
&CoreManager::coreManagerInitialized, CoreManager::getInstance(),
[this, mustBeIconified]() mutable {
if(CoreManager::getInstance()->started())
if(CoreManager::getInstance()->started()) {
openAppAfterInit(mustBeIconified);
}
}
);
@ -1062,37 +1091,35 @@ void App::openAppAfterInit (bool mustBeIconified) {
checkForUpdates();
#endif // ifdef ENABLE_UPDATE_CHECK
if(setFetchConfig(mParser))
restart();
else{
// Launch call if wanted and clean parser
if( mParser->isSet("call") && coreManager->isLastRemoteProvisioningGood()){
QString sipAddress = mParser->value("call");
mParser->parse(cleanParserKeys(mParser, QStringList("call")));// Clean call from parser
if(coreManager->started()){
coreManager->getCallsListModel()->launchAudioCall(sipAddress);
}else{
QObject * context = new QObject();
QObject::connect(CoreManager::getInstance(), &CoreManager::coreManagerInitialized,context,
[sipAddress,coreManager, context]() mutable {
if(context){
delete context;
context = nullptr;
coreManager->getCallsListModel()->launchAudioCall(sipAddress);
}
});
}
// Launch call if wanted and clean parser
if( mParser->isSet("call") && coreManager->isLastRemoteProvisioningGood()){
QString sipAddress = mParser->value("call");
mParser->parse(cleanParserKeys(mParser, QStringList("call")));// Clean call from parser
if(coreManager->started()){
coreManager->getCallsListModel()->launchAudioCall(sipAddress);
}else{
QObject * context = new QObject();
QObject::connect(CoreManager::getInstance(), &CoreManager::coreManagerInitialized,context,
[sipAddress,coreManager, context]() mutable {
if(context){
delete context;
context = nullptr;
coreManager->getCallsListModel()->launchAudioCall(sipAddress);
}
});
}
#ifndef __APPLE__
if (!mustBeIconified)
smartShowWindow(mainWindow);
#else
Q_UNUSED(mustBeIconified);
smartShowWindow(mainWindow);
#endif
setOpened(true);
}
QString fetchFilePath = getFetchConfig(mParser);
mustBeIconified = mustBeIconified && fetchFilePath.isEmpty();
#ifndef __APPLE__
if (!mustBeIconified)
smartShowWindow(mainWindow);
#else
Q_UNUSED(mustBeIconified);
smartShowWindow(mainWindow);
#endif
setOpened(true);
useFetchConfig(fetchFilePath);
}
// -----------------------------------------------------------------------------

View file

@ -63,7 +63,10 @@ public:
QString getCommandArgument ();
bool setFetchConfig (QCommandLineParser *parser);
QString getFetchConfig (QString filePath, bool * error);
QString getFetchConfig (QCommandLineParser *parser);// Return file path of fetch-config
void useFetchConfig(const QString& filePath); // Check if the fetch is auto or not and make gui request if needed.
Q_INVOKABLE bool setFetchConfig (QString filePath); // return true if filepath has been set.
#ifdef Q_OS_MACOS
bool event (QEvent *event) override;
@ -131,6 +134,7 @@ signals:
void autoStartChanged (bool enabled);
void opened (bool status);
void requestFetchConfig(QString filePath);
private:
void createParser ();

View file

@ -488,7 +488,7 @@ void Cli::executeCommand (const QString &command, CommandFormat *format) {
// Detect if command is a CLI by testing commands
const QString &functionName = parseFunctionName(command);
const std::string configURI = string(EXECUTABLE_NAME)+"-config";
if(!functionName.isEmpty()){// It is a CLI
qInfo() << QStringLiteral("Detecting cli command: `%1`...").arg(command);
QHash<QString, QString> args = parseArgs(command);
@ -505,7 +505,7 @@ void Cli::executeCommand (const QString &command, CommandFormat *format) {
}else{
scheme = tempSipAddress[0].toStdString();
bool ok = false;
for (const string &validScheme : { string("sip"), "sip-"+string(EXECUTABLE_NAME), string("sips"), "sips-"+string(EXECUTABLE_NAME), string("tel"), string("callto"), string(EXECUTABLE_NAME)+ "-config" })
for (const string &validScheme : { string("sip"), "sip-"+string(EXECUTABLE_NAME), string("sips"), "sips-"+string(EXECUTABLE_NAME), string("tel"), string("callto"), configURI })
if (scheme == validScheme)
ok = true;
if( !ok){
@ -515,18 +515,22 @@ void Cli::executeCommand (const QString &command, CommandFormat *format) {
tempSipAddress[0] = "sip";
transformedCommand = tempSipAddress.join(':');
}
if( scheme == string(EXECUTABLE_NAME)+"-config" ){
if( scheme == configURI ){
QHash<QString, QString> args = parseArgs(command);
QString fetchUrl;
if(args.contains("fetch-config"))
args["fetch-config"] = QByteArray::fromBase64(args["fetch-config"].toUtf8() );
fetchUrl = QByteArray::fromBase64(args["fetch-config"].toUtf8() );
else {
QUrl url(command);
url.setScheme("https");
args["fetch-config"] = url.toString();
QUrl url(command.mid(configURI.size()+1));// Remove 'exec-config:'
if(url.scheme().isEmpty())
url.setScheme("https");
fetchUrl = url.toString();
}
if (format)
*format = CliFormat;
mCommands["show"].execute(args);
QHash<QString, QString> dummy;
mCommands["show"].execute(dummy);// Just open the app.
App::getInstance()->useFetchConfig(fetchUrl);
}else{
shared_ptr<linphone::Address> address;
if(Utils::isUsername(transformedCommand)){

View file

@ -213,6 +213,16 @@ void SettingsModel::setFetchRemoteConfigurationEnabled (bool status) {
emit fetchRemoteConfigurationEnabledChanged(status);
}
bool SettingsModel::getAutoApplyProvisioningConfigUriHandlerEnabled () const {
return !!mConfig->getInt(UiSection, "auto_apply_provisioning_config_uri_handler", 0);
}
void SettingsModel::setAutoApplyProvisioningConfigUriHandlerEnabled (bool status) {
mConfig->setInt(UiSection, "auto_apply_provisioning_config_uri_handler", status);
emit autoApplyProvisioningConfigUriHandlerEnabledChanged();
}
// ---------------------------------------------------------------------------
bool SettingsModel::getAssistantSupportsPhoneNumbers () const {

View file

@ -51,6 +51,7 @@ class SettingsModel : public QObject {
Q_PROPERTY(bool fetchRemoteConfigurationEnabled READ getFetchRemoteConfigurationEnabled WRITE setFetchRemoteConfigurationEnabled NOTIFY fetchRemoteConfigurationEnabledChanged)
Q_PROPERTY(bool useAppSipAccountEnabled READ getUseAppSipAccountEnabled WRITE setUseAppSipAccountEnabled NOTIFY useAppSipAccountEnabledChanged)
Q_PROPERTY(bool useOtherSipAccountEnabled READ getUseOtherSipAccountEnabled WRITE setUseOtherSipAccountEnabled NOTIFY useOtherSipAccountEnabledChanged)
Q_PROPERTY(bool autoApplyProvisioningConfigUriHandlerEnabled READ getAutoApplyProvisioningConfigUriHandlerEnabled WRITE setAutoApplyProvisioningConfigUriHandlerEnabled NOTIFY autoApplyProvisioningConfigUriHandlerEnabledChanged)
Q_PROPERTY(bool assistantSupportsPhoneNumbers READ getAssistantSupportsPhoneNumbers WRITE setAssistantSupportsPhoneNumbers NOTIFY assistantSupportsPhoneNumbersChanged)
// Webviews config
@ -278,6 +279,9 @@ public:
bool getFetchRemoteConfigurationEnabled () const;
void setFetchRemoteConfigurationEnabled (bool status);
bool getAutoApplyProvisioningConfigUriHandlerEnabled () const;
void setAutoApplyProvisioningConfigUriHandlerEnabled (bool status);
bool getUseAppSipAccountEnabled () const;
void setUseAppSipAccountEnabled (bool status);
@ -675,6 +679,7 @@ signals:
void fetchRemoteConfigurationEnabledChanged (bool status);
void useAppSipAccountEnabledChanged (bool status);
void useOtherSipAccountEnabledChanged (bool status);
void autoApplyProvisioningConfigUriHandlerEnabledChanged();
void assistantSupportsPhoneNumbersChanged (bool status);

View file

@ -29,6 +29,7 @@ Item {
font.pointSize: DialogStyle.description.pointSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
wrapMode: Text.Wrap
elide: Text.ElideRight
}
}

View file

@ -594,7 +594,7 @@ ColumnLayout {
anchors.left: filterButtons.right
anchors.leftMargin: 50
anchors.verticalCenter: parent.verticalCenter
color: BusyIndicatorStyle.alternateColor
color: BusyIndicatorStyle.alternateColor.color
running: chatArea.tryingToLoadMoreEntries
}

View file

@ -438,4 +438,19 @@ ApplicationWindow {
mainSearchBar.text = sipAddress
}
}
Connections{
target: App
onRequestFetchConfig: {
window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), {
flat: true,
//: 'Do you want to download and apply configuration from this URL?' : text to confirm to fetch a specified URL
descriptionText: '<b>'+qsTr('confirmFetchUri')
+'</b><br/><br/>'+filePath,
}, function (status) {
if (status) {
App.setFetchConfig(filePath)
}
})
}
}
}