1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2017 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 * Copyright (C) 2017 Piotr Wójcik <chocimier@tlen.pl>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **************************************************************************/
20
21 #include "Migrator.h"
22 #include "ActionsManager.h"
23 #include "Application.h"
24 #include "IniSettings.h"
25 #include "JsonSettings.h"
26 #include "SessionsManager.h"
27 #include "SettingsManager.h"
28 #include "ToolBarsManager.h"
29 #include "../ui/ItemViewWidget.h"
30
31 #include <QtCore/QDate>
32 #include <QtCore/QDir>
33 #include <QtCore/QJsonArray>
34 #include <QtCore/QJsonObject>
35 #include <QtCore/QSettings>
36 #include <QtCore/QTextStream>
37 #include <QtWidgets/QCheckBox>
38 #include <QtWidgets/QDialog>
39 #include <QtWidgets/QDialogButtonBox>
40 #include <QtWidgets/QLabel>
41 #include <QtWidgets/QVBoxLayout>
42
43 namespace Otter
44 {
45
46 class KeyboardAndMouseProfilesIniToJsonMigration final : public Migration
47 {
48 public:
KeyboardAndMouseProfilesIniToJsonMigration()49 KeyboardAndMouseProfilesIniToJsonMigration() : Migration()
50 {
51 }
52
createBackup() const53 void createBackup() const override
54 {
55 const QString keyboardBackupPath(createBackupPath(QLatin1String("keyboard")));
56 const QList<QFileInfo> keyboardEntries(QDir(SessionsManager::getWritableDataPath(QLatin1String("keyboard"))).entryInfoList({QLatin1String("*.ini")}));
57
58 for (int i = 0; i < keyboardEntries.count(); ++i)
59 {
60 QFile::copy(keyboardEntries.at(i).absoluteFilePath(), keyboardBackupPath + keyboardEntries.at(i).fileName());
61 }
62
63 const QString mouseBackupPath(createBackupPath(QLatin1String("mouse")));
64 const QList<QFileInfo> mouseEntries(QDir(SessionsManager::getWritableDataPath(QLatin1String("mouse"))).entryInfoList({QLatin1String("*.ini")}));
65
66 for (int i = 0; i < mouseEntries.count(); ++i)
67 {
68 QFile::copy(mouseEntries.at(i).absoluteFilePath(), mouseBackupPath + mouseEntries.at(i).fileName());
69 }
70 }
71
migrate() const72 void migrate() const override
73 {
74 const QList<QFileInfo> keyboardEntries(QDir(SessionsManager::getWritableDataPath(QLatin1String("keyboard"))).entryInfoList({QLatin1String("*.ini")}, QDir::Files));
75
76 for (int i = 0; i < keyboardEntries.count(); ++i)
77 {
78 KeyboardProfile profile(keyboardEntries.at(i).baseName());
79 QVector<KeyboardProfile::Action> definitions;
80 IniSettings settings(keyboardEntries.at(i).absoluteFilePath());
81 const QStringList comments(settings.getComment().split(QLatin1Char('\n')));
82
83 for (int j = 0; j < comments.count(); ++j)
84 {
85 const QString key(comments.at(j).section(QLatin1Char(':'), 0, 0).trimmed());
86 const QString value(comments.at(j).section(QLatin1Char(':'), 1).trimmed());
87
88 if (key == QLatin1String("Title"))
89 {
90 profile.setTitle(value);
91 }
92 else if (key == QLatin1String("Description"))
93 {
94 profile.setDescription(value);
95 }
96 else if (key == QLatin1String("Author"))
97 {
98 profile.setAuthor(value);
99 }
100 else if (key == QLatin1String("Version"))
101 {
102 profile.setVersion(value);
103 }
104 }
105
106 const QStringList actions(settings.getGroups());
107
108 for (int j = 0; j < actions.count(); ++j)
109 {
110 const int action(ActionsManager::getActionIdentifier(actions.at(j)));
111
112 if (action < 0)
113 {
114 continue;
115 }
116
117 settings.beginGroup(actions.at(j));
118
119 const QStringList shortcuts(settings.getValue(QLatin1String("shortcuts")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts));
120 KeyboardProfile::Action definition;
121 definition.shortcuts.reserve(shortcuts.count());
122 definition.action = action;
123
124 for (int k = 0; k < shortcuts.count(); ++k)
125 {
126 const QKeySequence shortcut(QKeySequence(shortcuts.at(k)));
127
128 if (!shortcut.isEmpty())
129 {
130 definition.shortcuts.append(shortcut);
131 }
132 }
133
134 if (!definition.shortcuts.isEmpty())
135 {
136 definitions.append(definition);
137 }
138
139 settings.endGroup();
140 }
141
142 profile.setDefinitions({{ActionsManager::GenericContext, definitions}});
143 profile.save();
144
145 QFile::remove(keyboardEntries.at(i).absoluteFilePath());
146 }
147
148 const QList<QFileInfo> mouseEntries(QDir(SessionsManager::getWritableDataPath(QLatin1String("mouse"))).entryInfoList({QLatin1String("*.ini")}, QDir::Files));
149
150 for (int i = 0; i < mouseEntries.count(); ++i)
151 {
152 IniSettings settings(mouseEntries.at(i).absoluteFilePath());
153 JsonSettings jsonSettings(SessionsManager::getWritableDataPath(QLatin1String("mouse/") + mouseEntries.at(i).completeBaseName() + QLatin1String(".json")));
154 jsonSettings.setComment(settings.getComment());
155
156 const QStringList contexts(settings.getGroups());
157 QJsonArray contextsArray;
158
159 for (int j = 0; j < contexts.count(); ++j)
160 {
161 QJsonObject contextObject{{QLatin1String("context"), contexts.at(j)}};
162 QJsonArray gesturesArray;
163
164 settings.beginGroup(contexts.at(j));
165
166 const QStringList gestures(settings.getKeys());
167
168 for (int k = 0; k < gestures.count(); ++k)
169 {
170 gesturesArray.append(QJsonObject{{QLatin1String("action"), settings.getValue(gestures.at(k)).toString()},{QLatin1String("steps"), QJsonArray::fromStringList(gestures.at(k).split(','))}});
171 }
172
173 contextObject.insert(QLatin1String("gestures"), gesturesArray);
174
175 contextsArray.append(contextObject);
176
177 settings.endGroup();
178 }
179
180 jsonSettings.setArray(contextsArray);
181 jsonSettings.save();
182
183 QFile::remove(mouseEntries.at(i).absoluteFilePath());
184 }
185 }
186
getName() const187 QString getName() const override
188 {
189 return QLatin1String("keyboardAndMouseProfilesIniToJson");
190 }
191
getTitle() const192 QString getTitle() const override
193 {
194 return QT_TRANSLATE_NOOP("migrations", "Keyboard and Mouse Configuration Profiles");
195 }
196
needsMigration() const197 bool needsMigration() const override
198 {
199 return (!QDir(SessionsManager::getWritableDataPath(QLatin1String("keyboard"))).entryList({QLatin1String("*.ini")}, QDir::Files).isEmpty() || !QDir(SessionsManager::getWritableDataPath(QLatin1String("mouse"))).entryList({QLatin1String("*.ini")}, QDir::Files).isEmpty());
200 }
201 };
202
203 class OptionsRenameMigration final : public Migration
204 {
205 public:
OptionsRenameMigration()206 OptionsRenameMigration() : Migration()
207 {
208 }
209
createBackup() const210 void createBackup() const override
211 {
212 const QString backupPath(createBackupPath(QLatin1String("other")));
213 const QList<QFileInfo> entries(QDir(SessionsManager::getWritableDataPath({})).entryInfoList({QLatin1String("otter.conf"), QLatin1String("override.ini")}));
214
215 for (int i = 0; i < entries.count(); ++i)
216 {
217 QFile::copy(entries.at(i).absoluteFilePath(), backupPath + entries.at(i).fileName());
218 }
219 }
220
migrate() const221 void migrate() const override
222 {
223 QMap<QString, SettingsManager::OptionIdentifier> optionsMap;
224 optionsMap[QLatin1String("Browser/DelayRestoringOfBackgroundTabs")] = SettingsManager::Sessions_DeferTabsLoadingOption;
225 optionsMap[QLatin1String("Browser/EnableFullScreen")] = SettingsManager::Permissions_EnableFullScreenOption;
226 optionsMap[QLatin1String("Browser/EnableGeolocation")] = SettingsManager::Permissions_EnableGeolocationOption;
227 optionsMap[QLatin1String("Browser/EnableImages")] = SettingsManager::Permissions_EnableImagesOption;
228 optionsMap[QLatin1String("Browser/EnableJavaScript")] = SettingsManager::Permissions_EnableJavaScriptOption;
229 optionsMap[QLatin1String("Browser/EnableLocalStorage")] = SettingsManager::Permissions_EnableLocalStorageOption;
230 optionsMap[QLatin1String("Browser/EnableMediaCaptureAudio")] = SettingsManager::Permissions_EnableMediaCaptureAudioOption;
231 optionsMap[QLatin1String("Browser/EnableMediaCaptureVideo")] = SettingsManager::Permissions_EnableMediaCaptureVideoOption;
232 optionsMap[QLatin1String("Browser/EnableMediaPlaybackAudio")] = SettingsManager::Permissions_EnableMediaPlaybackAudioOption;
233 optionsMap[QLatin1String("Browser/EnableNotifications")] = SettingsManager::Permissions_EnableNotificationsOption;
234 optionsMap[QLatin1String("Browser/EnableOfflineStorageDatabase")] = SettingsManager::Permissions_EnableOfflineStorageDatabaseOption;
235 optionsMap[QLatin1String("Browser/EnableOfflineWebApplicationCache")] = SettingsManager::Permissions_EnableOfflineWebApplicationCacheOption;
236 optionsMap[QLatin1String("Browser/EnablePlugins")] = SettingsManager::Permissions_EnablePluginsOption;
237 optionsMap[QLatin1String("Browser/EnablePointerLock")] = SettingsManager::Permissions_EnablePointerLockOption;
238 optionsMap[QLatin1String("Browser/EnableWebgl")] = SettingsManager::Permissions_EnableWebglOption;
239 optionsMap[QLatin1String("Browser/JavaScriptCanAccessClipboard")] = SettingsManager::Permissions_ScriptsCanAccessClipboardOption;
240 optionsMap[QLatin1String("Browser/JavaScriptCanChangeWindowGeometry")] = SettingsManager::Permissions_ScriptsCanChangeWindowGeometryOption;
241 optionsMap[QLatin1String("Browser/JavaScriptCanCloseWindows")] = SettingsManager::Permissions_ScriptsCanCloseWindowsOption;
242 optionsMap[QLatin1String("Browser/JavaScriptCanDisableContextMenu")] = SettingsManager::Permissions_ScriptsCanReceiveRightClicksOption;
243 optionsMap[QLatin1String("Browser/JavaScriptCanShowStatusMessages")] = SettingsManager::Permissions_ScriptsCanShowStatusMessagesOption;
244 optionsMap[QLatin1String("Browser/TabCrashingActionOption")] = SettingsManager::Interface_TabCrashingActionOption;
245 optionsMap[QLatin1String("Content/PopupsPolicy")] = SettingsManager::Permissions_ScriptsCanOpenWindowsOption;
246
247 QMap<QString, SettingsManager::OptionIdentifier>::iterator optionsIterator;
248 QSettings configuration(SettingsManager::getGlobalPath(), QSettings::IniFormat);
249 const QStringList configurationKeys(configuration.allKeys());
250
251 for (optionsIterator = optionsMap.begin(); optionsIterator != optionsMap.end(); ++optionsIterator)
252 {
253 if (configurationKeys.contains(optionsIterator.key()))
254 {
255 configuration.setValue(SettingsManager::getOptionName(optionsIterator.value()), configuration.value(optionsIterator.key()).toString());
256 configuration.remove(optionsIterator.key());
257 }
258 }
259
260 QSettings overrides(SettingsManager::getOverridePath(), QSettings::IniFormat);
261 const QStringList overridesGroups(overrides.childGroups());
262
263 for (int i = 0; i < overridesGroups.count(); ++i)
264 {
265 overrides.beginGroup(overridesGroups.at(i));
266
267 const QStringList overridesKeys(overrides.allKeys());
268
269 for (optionsIterator = optionsMap.begin(); optionsIterator != optionsMap.end(); ++optionsIterator)
270 {
271 if (overridesKeys.contains(optionsIterator.key()))
272 {
273 overrides.setValue(SettingsManager::getOptionName(optionsIterator.value()), overrides.value(optionsIterator.key()).toString());
274 overrides.remove(optionsIterator.key());
275 }
276 }
277
278 overrides.endGroup();
279 }
280
281 const QStringList sessions(SessionsManager::getSessions());
282
283 for (int i = 0; i < sessions.count(); ++i)
284 {
285 QFile file(SessionsManager::getSessionPath(sessions.at(i)));
286
287 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
288 {
289 QString data(file.readAll());
290
291 for (optionsIterator = optionsMap.begin(); optionsIterator != optionsMap.end(); ++optionsIterator)
292 {
293 data.replace(QLatin1Char('"') + optionsIterator.key() + QLatin1String("\": "), QLatin1Char('"') + SettingsManager::getOptionName(optionsIterator.value()) + QLatin1String("\": "));
294 }
295
296 file.close();
297
298 if (file.open(QIODevice::WriteOnly | QIODevice::Text))
299 {
300 QTextStream stream(&file);
301 stream.setCodec("UTF-8");
302 stream << data;
303
304 file.close();
305 }
306 }
307 }
308
309 if (configurationKeys.contains(QLatin1String("Sidebar/PanelsOption")) || configurationKeys.contains(QLatin1String("Sidebar/ShowToggleEdgeOption")) || configurationKeys.contains(QLatin1String("Sidebar/VisibleOption")))
310 {
311 ToolBarsManager::ToolBarDefinition sidebarDefiniton(ToolBarsManager::getToolBarDefinition(ToolBarsManager::SideBar));
312 sidebarDefiniton.currentPanel = configuration.value(QLatin1String("Sidebar/CurrentPanelOption")).toString();
313 sidebarDefiniton.normalVisibility = (configuration.value(QLatin1String("Sidebar/VisibleOption")).toBool() ? ToolBarsManager::AlwaysVisibleToolBar : ToolBarsManager::AlwaysHiddenToolBar);
314 sidebarDefiniton.panels = configuration.value(QLatin1String("Sidebar/PanelsOption")).toStringList();
315 sidebarDefiniton.hasToggle = configuration.value(QLatin1String("Sidebar/ShowToggleEdgeOption")).toBool();
316
317 ToolBarsManager::setToolBar(sidebarDefiniton);
318 }
319 }
320
getName() const321 QString getName() const override
322 {
323 return QLatin1String("optionsRename");
324 }
325
getTitle() const326 QString getTitle() const override
327 {
328 return QT_TRANSLATE_NOOP("migrations", "Options");
329 }
330
needsMigration() const331 bool needsMigration() const override
332 {
333 return (QFile::exists(SettingsManager::getGlobalPath()) || QFile::exists(SettingsManager::getOverridePath()));
334 }
335 };
336
337 class SearchEnginesStorageMigration final : public Migration
338 {
339 public:
SearchEnginesStorageMigration()340 SearchEnginesStorageMigration() : Migration()
341 {
342 }
343
migrate() const344 void migrate() const override
345 {
346 QDir().rename(SessionsManager::getWritableDataPath(QLatin1String("searches")), SessionsManager::getWritableDataPath(QLatin1String("searchEngines")));
347 }
348
getName() const349 QString getName() const override
350 {
351 return QLatin1String("searchEnginesStorage");
352 }
353
getTitle() const354 QString getTitle() const override
355 {
356 return QT_TRANSLATE_NOOP("migrations", "Search Engines");
357 }
358
needsBackup() const359 bool needsBackup() const override
360 {
361 return false;
362 }
363
needsMigration() const364 bool needsMigration() const override
365 {
366 return QFile::exists(SessionsManager::getWritableDataPath(QLatin1String("searches")));
367 }
368 };
369
370 class SessionsIniToJsonMigration final : public Migration
371 {
372 public:
SessionsIniToJsonMigration()373 SessionsIniToJsonMigration() : Migration()
374 {
375 }
376
createBackup() const377 void createBackup() const override
378 {
379 const QString backupPath(createBackupPath(QLatin1String("sessions")));
380 const QList<QFileInfo> entries(QDir(SessionsManager::getWritableDataPath(QLatin1String("sessions"))).entryInfoList({QLatin1String("*.ini")}));
381
382 for (int i = 0; i < entries.count(); ++i)
383 {
384 QFile::copy(entries.at(i).absoluteFilePath(), backupPath + entries.at(i).fileName());
385 }
386 }
387
migrate() const388 void migrate() const override
389 {
390 const QList<QFileInfo> entries(QDir(SessionsManager::getWritableDataPath(QLatin1String("sessions"))).entryInfoList({QLatin1String("*.ini")}, QDir::Files));
391
392 for (int i = 0; i < entries.count(); ++i)
393 {
394 QSettings sessionData(entries.at(i).absoluteFilePath(), QSettings::IniFormat);
395 sessionData.setIniCodec("UTF-8");
396
397 SessionInformation session;
398 session.path = entries.at(i).absolutePath() + QDir::separator() + entries.at(i).baseName() + QLatin1String(".json");
399 session.title = sessionData.value(QLatin1String("Session/title"), {}).toString();
400 session.index = (sessionData.value(QLatin1String("Session/index"), 1).toInt() - 1);
401 session.isClean = sessionData.value(QLatin1String("Session/clean"), true).toBool();
402
403 const int windowsAmount(sessionData.value(QLatin1String("Session/windows"), 0).toInt());
404 const int defaultZoom(SettingsManager::getOption(SettingsManager::Content_DefaultZoomOption).toInt());
405
406 for (int j = 1; j <= windowsAmount; ++j)
407 {
408 const int tabs(sessionData.value(QStringLiteral("%1/Properties/windows").arg(j), 0).toInt());
409 SessionMainWindow sessionEntry;
410 sessionEntry.geometry = QByteArray::fromBase64(sessionData.value(QStringLiteral("%1/Properties/geometry").arg(j), {}).toString().toLatin1());
411 sessionEntry.index = (sessionData.value(QStringLiteral("%1/Properties/index").arg(j), 1).toInt() - 1);
412
413 for (int k = 1; k <= tabs; ++k)
414 {
415 const QString state(sessionData.value(QStringLiteral("%1/%2/Properties/state").arg(j).arg(k), {}).toString());
416 const QString searchEngine(sessionData.value(QStringLiteral("%1/%2/Properties/searchEngine").arg(j).arg(k), {}).toString());
417 const QString userAgent(sessionData.value(QStringLiteral("%1/%2/Properties/userAgent").arg(j).arg(k), {}).toString());
418 const QStringList geometry(sessionData.value(QStringLiteral("%1/%2/Properties/geometry").arg(j).arg(k), {}).toString().split(QLatin1Char(',')));
419 const int historyAmount(sessionData.value(QStringLiteral("%1/%2/Properties/history").arg(j).arg(k), 0).toInt());
420 const int reloadTime(sessionData.value(QStringLiteral("%1/%2/Properties/reloadTime").arg(j).arg(k), -1).toInt());
421 WindowState windowState;
422 windowState.geometry = ((geometry.count() == 4) ? QRect(geometry.at(0).simplified().toInt(), geometry.at(1).simplified().toInt(), geometry.at(2).simplified().toInt(), geometry.at(3).simplified().toInt()) : QRect());
423 windowState.state = ((state == QLatin1String("maximized")) ? Qt::WindowMaximized : ((state == QLatin1String("minimized")) ? Qt::WindowMinimized : Qt::WindowNoState));
424
425 SessionWindow sessionWindow;
426 sessionWindow.state = windowState;
427 sessionWindow.parentGroup = sessionData.value(QStringLiteral("%1/%2/Properties/group").arg(j).arg(k), 0).toInt();
428 sessionWindow.historyIndex = (sessionData.value(QStringLiteral("%1/%2/Properties/index").arg(j).arg(k), 1).toInt() - 1);
429 sessionWindow.isAlwaysOnTop = sessionData.value(QStringLiteral("%1/%2/Properties/alwaysOnTop").arg(j).arg(k), false).toBool();
430 sessionWindow.isPinned = sessionData.value(QStringLiteral("%1/%2/Properties/pinned").arg(j).arg(k), false).toBool();
431
432 if (!searchEngine.isEmpty())
433 {
434 sessionWindow.options[SettingsManager::Search_DefaultSearchEngineOption] = searchEngine;
435 }
436
437 if (!userAgent.isEmpty())
438 {
439 sessionWindow.options[SettingsManager::Network_UserAgentOption] = userAgent;
440 }
441
442 if (reloadTime >= 0)
443 {
444 sessionWindow.options[SettingsManager::Content_PageReloadTimeOption] = reloadTime;
445 }
446
447 for (int l = 1; l <= historyAmount; ++l)
448 {
449 const QStringList position(sessionData.value(QStringLiteral("%1/%2/History/%3/position").arg(j).arg(k).arg(l), 1).toStringList());
450 WindowHistoryEntry historyEntry;
451 historyEntry.url = sessionData.value(QStringLiteral("%1/%2/History/%3/url").arg(j).arg(k).arg(l), {}).toString();
452 historyEntry.title = sessionData.value(QStringLiteral("%1/%2/History/%3/title").arg(j).arg(k).arg(l), {}).toString();
453 historyEntry.position = ((position.count() == 2) ? QPoint(position.at(0).simplified().toInt(), position.at(1).simplified().toInt()) : QPoint(0, 0));
454 historyEntry.zoom = sessionData.value(QStringLiteral("%1/%2/History/%3/zoom").arg(j).arg(k).arg(l), defaultZoom).toInt();
455
456 sessionWindow.history.append(historyEntry);
457 }
458
459 sessionEntry.windows.append(sessionWindow);
460 }
461
462 session.windows.append(sessionEntry);
463 }
464
465 if (SessionsManager::saveSession(session))
466 {
467 QFile::remove(entries.at(i).absoluteFilePath());
468 }
469 }
470 }
471
getName() const472 QString getName() const override
473 {
474 return QLatin1String("sessionsIniToJson");
475 }
476
getTitle() const477 QString getTitle() const override
478 {
479 return QT_TRANSLATE_NOOP("migrations", "Sessions");
480 }
481
needsMigration() const482 bool needsMigration() const override
483 {
484 return !QDir(SessionsManager::getWritableDataPath(QLatin1String("sessions"))).entryList({QLatin1String("*.ini")}, QDir::Files).isEmpty();
485 }
486 };
487
Migration()488 Migration::Migration()
489 {
490 }
491
~Migration()492 Migration::~Migration()
493 {
494 }
495
createBackup() const496 void Migration::createBackup() const
497 {
498 }
499
migrate() const500 void Migration::migrate() const
501 {
502 }
503
createBackupPath(const QString & sourcePath)504 QString Migration::createBackupPath(const QString &sourcePath)
505 {
506 QString backupPath(SessionsManager::getWritableDataPath(QLatin1String("backups") + QDir::separator() + (sourcePath.isEmpty() ? QLatin1String("other") : sourcePath)) + QDir::separator());
507 QString backupName(QDate::currentDate().toString(QLatin1String("yyyyMMdd")));
508 int i(1);
509
510 do
511 {
512 const QString path(backupPath + backupName + ((i > 1) ? QStringLiteral("-%1").arg(i) : QString()) + QDir::separator());
513
514 if (!QFile::exists(path))
515 {
516 backupPath = path;
517
518 break;
519 }
520
521 ++i;
522 }
523 while (true);
524
525 QDir().mkpath(backupPath);
526
527 return backupPath;
528 }
529
getName() const530 QString Migration::getName() const
531 {
532 return {};
533 }
534
getTitle() const535 QString Migration::getTitle() const
536 {
537 return {};
538 }
539
needsBackup() const540 bool Migration::needsBackup() const
541 {
542 return true;
543 }
544
needsMigration() const545 bool Migration::needsMigration() const
546 {
547 return false;
548 }
549
run()550 bool Migrator::run()
551 {
552 const QVector<Migration*> availableMigrations({new KeyboardAndMouseProfilesIniToJsonMigration(), new OptionsRenameMigration(), new SearchEnginesStorageMigration(), new SessionsIniToJsonMigration()});
553 QVector<Migration*> possibleMigrations;
554 QStringList processedMigrations(SettingsManager::getOption(SettingsManager::Browser_MigrationsOption).toStringList());
555
556 for (int i = 0; i < availableMigrations.count(); ++i)
557 {
558 if (!processedMigrations.contains(availableMigrations.at(i)->getName()))
559 {
560 if (Application::isFirstRun() || !availableMigrations.at(i)->needsMigration())
561 {
562 processedMigrations.append(availableMigrations.at(i)->getName());
563 }
564 else
565 {
566 possibleMigrations.append(availableMigrations.at(i));
567 }
568 }
569 }
570
571 if (possibleMigrations.isEmpty())
572 {
573 SettingsManager::setOption(SettingsManager::Browser_MigrationsOption, QVariant(processedMigrations));
574
575 return true;
576 }
577
578 QDialog dialog;
579 dialog.setWindowTitle(QCoreApplication::translate("Otter::Migrator", "Settings Migration"));
580 dialog.setLayout(new QVBoxLayout(&dialog));
581
582 QLabel *label(new QLabel(QCoreApplication::translate("Otter::Migrator", "Configuration of the components listed below needs to be updated to new version.\nDo you want to migrate it?"), &dialog));
583 label->setWordWrap(true);
584
585 ItemViewWidget *migrationsViewWidget(new ItemViewWidget(&dialog));
586 migrationsViewWidget->setModel(new QStandardItemModel(migrationsViewWidget));
587 migrationsViewWidget->setHeaderHidden(true);
588 migrationsViewWidget->header()->setStretchLastSection(true);
589
590 bool needsBackup(false);
591
592 for (int i = 0; i < possibleMigrations.count(); ++i)
593 {
594 QStandardItem *item(new QStandardItem(QCoreApplication::translate("migrations", possibleMigrations.at(i)->getTitle().toUtf8().constData())));
595 item->setFlags(Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable);
596
597 if (possibleMigrations.at(i)->needsBackup())
598 {
599 needsBackup = true;
600 }
601
602 migrationsViewWidget->insertRow({item});
603 }
604
605 QCheckBox *createBackupCheckBox(new QCheckBox(QCoreApplication::translate("Otter::Migrator", "Create backup")));
606 createBackupCheckBox->setChecked(true);
607 createBackupCheckBox->setEnabled(needsBackup);
608
609 QDialogButtonBox *buttonBox(new QDialogButtonBox(&dialog));
610 buttonBox->addButton(QDialogButtonBox::Yes);
611 buttonBox->addButton(QDialogButtonBox::No);
612 buttonBox->addButton(QDialogButtonBox::Abort);
613
614 QDialogButtonBox::StandardButton clickedButton(QDialogButtonBox::Yes);
615
616 QObject::connect(buttonBox, &QDialogButtonBox::clicked, [&](QAbstractButton *button)
617 {
618 clickedButton = buttonBox->standardButton(button);
619
620 dialog.close();
621 });
622
623 dialog.layout()->addWidget(label);
624 dialog.layout()->addWidget(migrationsViewWidget);
625 dialog.layout()->addWidget(createBackupCheckBox);
626 dialog.layout()->addWidget(buttonBox);
627 dialog.exec();
628
629 const bool canProceed(clickedButton == QDialogButtonBox::Yes);
630
631 if (canProceed || createBackupCheckBox->isChecked())
632 {
633 for (int i = 0; i < possibleMigrations.count(); ++i)
634 {
635 processedMigrations.append(possibleMigrations.at(i)->getName());
636
637 if (createBackupCheckBox->isChecked())
638 {
639 possibleMigrations.at(i)->createBackup();
640 }
641
642 if (canProceed)
643 {
644 possibleMigrations.at(i)->migrate();
645 }
646 }
647 }
648
649 qDeleteAll(availableMigrations);
650
651 if (clickedButton == QDialogButtonBox::Abort)
652 {
653 return false;
654 }
655
656 SettingsManager::setOption(SettingsManager::Browser_MigrationsOption, QVariant(processedMigrations));
657
658 return true;
659 }
660
661 }
662