File indexing completed on 2025-01-19 03:53:49
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-11-14 0007 * Description : database settings widget 0008 * 0009 * SPDX-FileCopyrightText: 2009-2010 by Holger Foerster <Hamsi2k at freenet dot de> 0010 * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "dbsettingswidget_p.h" 0017 0018 namespace Digikam 0019 { 0020 0021 DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* const parent) 0022 : QWidget(parent), 0023 d (new Private) 0024 { 0025 setupMainArea(); 0026 } 0027 0028 DatabaseSettingsWidget::~DatabaseSettingsWidget() 0029 { 0030 delete d; 0031 } 0032 0033 void DatabaseSettingsWidget::setupMainArea() 0034 { 0035 QVBoxLayout* const layout = new QVBoxLayout(); 0036 setLayout(layout); 0037 0038 // -------------------------------------------------------- 0039 0040 const int spacing = qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0041 QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); 0042 0043 QGroupBox* const dbConfigBox = new QGroupBox(i18n("Database Configuration"), this); 0044 QVBoxLayout* const vlay = new QVBoxLayout(dbConfigBox); 0045 0046 DHBox* const typeHbox = new DHBox(); 0047 QLabel* const databaseTypeLabel = new QLabel(typeHbox); 0048 d->dbType = new QComboBox(typeHbox); 0049 databaseTypeLabel->setText(i18n("Type:")); 0050 0051 // --------- fill with default values --------------------- 0052 0053 int dbTypeIdx = 0; 0054 d->dbType->addItem(i18n("SQLite"), SQlite); 0055 d->dbTypeMap[SQlite] = dbTypeIdx++; 0056 0057 #ifdef HAVE_MYSQLSUPPORT 0058 0059 # ifdef HAVE_INTERNALMYSQL 0060 0061 d->dbType->addItem(i18n("Mysql Internal"), MysqlInternal); 0062 d->dbTypeMap[MysqlInternal] = dbTypeIdx++; 0063 0064 # endif 0065 0066 d->dbType->addItem(i18n("MySQL Server"), MysqlServer); 0067 d->dbTypeMap[MysqlServer] = dbTypeIdx++; 0068 0069 #endif 0070 0071 QString tip = i18n("<p>Select here the type of database backend.</p>" 0072 "<p><b>SQlite</b> backend is for local database storage with a small or medium collection sizes. " 0073 "It is the default and recommended backend for collections with less than 100K items.</p>"); 0074 0075 0076 #ifdef HAVE_MYSQLSUPPORT 0077 0078 # ifdef HAVE_INTERNALMYSQL 0079 0080 tip.append(i18n("<p><b>MySQL Internal</b> backend is for local database storage with huge collection sizes. " 0081 "This backend is recommend for local collections with more than 100K items.</p>")); 0082 0083 # endif 0084 0085 tip.append(i18n("<p><b>MySQL Server</b> backend is a more robust solution especially for remote and shared database storage. " 0086 "It is also more efficient to manage huge collection sizes with more than 100K items.</p>")); 0087 0088 #endif 0089 0090 d->dbType->setToolTip(tip); 0091 0092 // -------------------------------------------------------- 0093 0094 d->dbPathLabel = new QLabel(i18n("<p>Set here the location where the database files will be stored on your system. " 0095 "There are four databases: " 0096 "one for all collections properties, " 0097 "one to store compressed thumbnails, " 0098 "one to store faces recognition metadata, " 0099 "and one to store similarity fingerprints.<br/>" 0100 "Write access is required to be able to edit image properties.</p>" 0101 "<p>Databases are digiKam core engines. Take care to use a place hosted by fast " 0102 "hardware (eg. SSD or NVMe) with enough free space especially for thumbnails database.</p>" 0103 "<p>Note: a remote file system such as NFS, cannot be used here. " 0104 "For performance reasons, it is also recommended not to use network storage media.</p>" 0105 "<p></p>"), dbConfigBox); 0106 d->dbPathLabel->setWordWrap(true); 0107 d->dbPathEdit = new DFileSelector(dbConfigBox); 0108 d->dbPathEdit->setFileDlgMode(QFileDialog::Directory); 0109 d->dbPathEdit->setFileDlgOptions(QFileDialog::ShowDirsOnly); 0110 0111 // -------------------------------------------------------- 0112 0113 d->walModeCheck = new QCheckBox(i18n("Enable WAL mode for the databases"), dbConfigBox); 0114 d->walModeCheck->setToolTip(i18n("The WAL (Write-Ahead Log) mode is significantly " 0115 "faster in most scenarios on supported systems.")); 0116 0117 d->walLabel = new QLabel(i18n("Write-Ahead Log is a mode to use a roll-forward journal that records transactions " 0118 "that have been committed but not yet applied to the databases. It uses an auxiliary " 0119 "journalized file to host structures for recovery transactions during a crash. The changes " 0120 "are first recorded in the log, before the changes are written to the database. This made " 0121 "database requests atomic and robust in extensive and critical use cases."), 0122 dbConfigBox); 0123 d->walLabel->setWordWrap(true); 0124 0125 // -------------------------------------------------------- 0126 0127 d->mysqlCmdBox = new DVBox(dbConfigBox); 0128 d->mysqlCmdBox->layout()->setContentsMargins(QMargins()); 0129 0130 new DLineWidget(Qt::Horizontal, d->mysqlCmdBox); 0131 QLabel* const mysqlBinariesLabel = new QLabel(i18n("<p>Here you can configure locations where MySQL binary tools are located. " 0132 "digiKam will try to find these binaries automatically if they are " 0133 "already installed on your computer.</p>"), 0134 d->mysqlCmdBox); 0135 mysqlBinariesLabel->setWordWrap(true); 0136 0137 QGroupBox* const binaryBox = new QGroupBox(d->mysqlCmdBox); 0138 QGridLayout* const binaryLayout = new QGridLayout; 0139 binaryBox->setLayout(binaryLayout); 0140 binaryBox->setTitle(i18nc("@title:group", "MySQL Binaries")); 0141 d->dbBinariesWidget = new DBinarySearch(binaryBox); 0142 d->dbBinariesWidget->header()->setSectionHidden(2, true); 0143 0144 d->dbBinariesWidget->addBinary(d->mysqlServerBin); 0145 d->dbBinariesWidget->addBinary(d->mysqlAdminBin); 0146 d->dbBinariesWidget->addBinary(d->mysqlUpgradeBin); 0147 d->dbBinariesWidget->addBinary(d->mysqlInitBin); 0148 0149 #ifdef Q_OS_LINUX 0150 0151 d->dbBinariesWidget->addDirectory(QLatin1String("/usr/bin")); 0152 d->dbBinariesWidget->addDirectory(QLatin1String("/usr/sbin")); 0153 0154 #endif 0155 0156 #ifdef Q_OS_MACOS 0157 0158 // Std Macports install 0159 d->dbBinariesWidget->addDirectory(QLatin1String("/opt/local/bin")); 0160 d->dbBinariesWidget->addDirectory(QLatin1String("/opt/local/sbin")); 0161 d->dbBinariesWidget->addDirectory(QLatin1String("/opt/local/lib/mariadb/bin")); 0162 0163 // digiKam Bundle PKG install 0164 d->dbBinariesWidget->addDirectory(macOSBundlePrefix() + QLatin1String("lib/mariadb/bin")); 0165 0166 #endif 0167 0168 #ifdef Q_OS_WIN 0169 0170 d->dbBinariesWidget->addDirectory(QLatin1String("C:/Program Files/MariaDB 10.5/bin")); 0171 d->dbBinariesWidget->addDirectory(QLatin1String("C:/Program Files (x86)/MariaDB 10.5/bin")); 0172 0173 #endif 0174 0175 d->dbBinariesWidget->allBinariesFound(); 0176 0177 // -------------------------------------------------------- 0178 0179 d->tab = new QTabWidget(this); 0180 0181 QLabel* const hostNameLabel = new QLabel(i18n("Host Name:")); 0182 d->hostName = new QLineEdit(); 0183 d->hostName->setPlaceholderText(i18n("Set the host computer name")); 0184 d->hostName->setToolTip(i18n("This is the computer name running MySQL server.\n" 0185 "This can be \"localhost\" for a local server, " 0186 "or the network computer\n" 0187 "name (or IP address) in case of remote computer.")); 0188 0189 QLabel* const connectOptsLabel = new QLabel(i18n("<a href=\"https://doc.qt.io/qt-5/" 0190 "qsqldatabase.html#setConnectOptions\">Connect options:</a>")); 0191 connectOptsLabel->setOpenExternalLinks(true); 0192 d->connectOpts = new QLineEdit(); 0193 d->connectOpts->setPlaceholderText(i18n("Set the database connection options")); 0194 d->connectOpts->setToolTip(i18n("Set the MySQL server connection options.\n" 0195 "For advanced users only.")); 0196 0197 QLabel* const userNameLabel = new QLabel(i18n("User:")); 0198 d->userName = new QLineEdit(); 0199 d->userName->setPlaceholderText(i18n("Set the database account name")); 0200 d->userName->setToolTip(i18n("Set the MySQL server account name used\n" 0201 "by digiKam to be connected to the server.\n" 0202 "This account must be available on the remote\n" 0203 "MySQL server when database have been created.")); 0204 0205 QLabel* const passwordLabel = new QLabel(i18n("Password:")); 0206 d->password = new QLineEdit(); 0207 d->password->setToolTip(i18n("Set the MySQL server account password used\n" 0208 "by digiKam to be connected to the server.\n" 0209 "You can left this field empty to use an account set without password.")); 0210 d->password->setEchoMode(QLineEdit::Password); 0211 0212 DHBox* const phbox = new DHBox(); 0213 QLabel* const hostPortLabel = new QLabel(i18n("Host Port:")); 0214 d->hostPort = new QSpinBox(phbox); 0215 d->hostPort->setToolTip(i18n("Set the host computer port.\nUsually, MySQL server use port number 3306 by default")); 0216 d->hostPort->setMaximum(65535); 0217 d->hostPort->setValue(3306); 0218 QWidget* const space = new QWidget(phbox); 0219 phbox->setStretchFactor(space, 10); 0220 QPushButton* const checkDBConnectBtn = new QPushButton(i18n("Check Connection"), phbox); 0221 checkDBConnectBtn->setToolTip(i18n("Run a basic database connection to see if current MySQL server settings is suitable.")); 0222 0223 // Only accept printable Ascii char for database names. 0224 0225 QRegularExpression asciiRx(QLatin1String("[\x20-\x7F]+$")); 0226 QValidator* const asciiValidator = new QRegularExpressionValidator(asciiRx, this); 0227 0228 QLabel* const dbNameCoreLabel = new QLabel(i18n("Core Db Name:")); 0229 d->dbNameCore = new QLineEdit(); 0230 d->dbNameCore->setPlaceholderText(i18n("Set the core database name")); 0231 d->dbNameCore->setToolTip(i18n("The core database is lead digiKam container used to store\n" 0232 "albums, items, and searches metadata.")); 0233 d->dbNameCore->setValidator(asciiValidator); 0234 0235 d->dbThumbsLabel = new QLabel(i18n("Thumbs Db Name:")); 0236 d->dbNameThumbs = new DFileSelector(); 0237 d->dbNameThumbs->setFileDlgMode(QFileDialog::Directory); 0238 d->dbNameThumbs->setFileDlgOptions(QFileDialog::ShowDirsOnly); 0239 d->dbNameThumbs->lineEdit()->setPlaceholderText(i18n("Set the thumbnails database name or folder")); 0240 d->dbNameThumbs->setToolTip(i18n("The thumbnails database is used by digiKam to host\n" 0241 "image thumbs with wavelets compression images.\n" 0242 "This one can use quickly a lots of space,\n" 0243 "especially if you have huge collections.\n" 0244 "Choose a local folder to use a SQLite database.")); 0245 0246 QLabel* const dbNameFaceLabel = new QLabel(i18n("Face Db Name:")); 0247 d->dbNameFace = new QLineEdit(); 0248 d->dbNameFace->setPlaceholderText(i18n("Set the face database name")); 0249 d->dbNameFace->setToolTip(i18n("The face database is used by digiKam to host image histograms\n" 0250 "dedicated to faces recognition process.\n" 0251 "This one can use quickly a lots of space, especially\n" 0252 "if you a lots of image with people faces detected and tagged.")); 0253 d->dbNameFace->setValidator(asciiValidator); 0254 0255 QLabel* const dbNameSimilarityLabel = new QLabel(i18n("Similarity Db Name:")); 0256 d->dbNameSimilarity = new QLineEdit(); 0257 d->dbNameSimilarity->setPlaceholderText(i18n("Set the similarity database name")); 0258 d->dbNameSimilarity->setToolTip(i18n("The similarity database is used by digiKam to host\n" 0259 "image Haar matrix data for the similarity search.")); 0260 d->dbNameSimilarity->setValidator(asciiValidator); 0261 0262 QPushButton* const defaultValuesBtn = new QPushButton(i18n("Default Settings")); 0263 defaultValuesBtn->setToolTip(i18n("Reset database names settings to common default values.")); 0264 0265 d->expertSettings = new QGroupBox(); 0266 d->expertSettings->setFlat(true); 0267 QFormLayout* const expertSettinglayout = new QFormLayout(); 0268 d->expertSettings->setLayout(expertSettinglayout); 0269 0270 expertSettinglayout->addRow(hostNameLabel, d->hostName); 0271 expertSettinglayout->addRow(userNameLabel, d->userName); 0272 expertSettinglayout->addRow(passwordLabel, d->password); 0273 expertSettinglayout->addRow(connectOptsLabel, d->connectOpts); 0274 expertSettinglayout->addRow(hostPortLabel, phbox); 0275 expertSettinglayout->addRow(new DLineWidget(Qt::Horizontal, d->expertSettings)); 0276 expertSettinglayout->addRow(dbNameCoreLabel, d->dbNameCore); 0277 expertSettinglayout->addRow(d->dbThumbsLabel, d->dbNameThumbs); 0278 expertSettinglayout->addRow(dbNameFaceLabel, d->dbNameFace); 0279 expertSettinglayout->addRow(dbNameSimilarityLabel, d->dbNameSimilarity); 0280 expertSettinglayout->addRow(new QWidget(), defaultValuesBtn); 0281 0282 d->tab->addTab(d->expertSettings, i18n("Remote Server Settings")); 0283 0284 // -------------------------------------------------------- 0285 0286 d->dbNoticeBox = new QGroupBox(i18n("Database Server Instructions"), this); 0287 QVBoxLayout* const vlay2 = new QVBoxLayout(d->dbNoticeBox); 0288 QLabel* const notice = new QLabel(i18n("<p>digiKam expects that database is already created with a dedicated user account. " 0289 "This user name <i>digikam</i> will require full access to the database.<br/>" 0290 "If your database is not already set up, you can use the following SQL commands " 0291 "(after replacing the <b><i>password</i></b> with the correct one).</p>"), 0292 d->dbNoticeBox); 0293 notice->setWordWrap(true); 0294 0295 d->sqlInit = new QTextBrowser(d->dbNoticeBox); 0296 d->sqlInit->setOpenExternalLinks(false); 0297 d->sqlInit->setOpenLinks(false); 0298 d->sqlInit->setReadOnly(false); 0299 0300 QTextBrowser* const notice2 = new QTextBrowser(this); 0301 notice2->setText(i18n("<p>Note: with a Linux server, a database can be initialized following the commands below:</p>" 0302 "<p># su</p>" 0303 "<p># systemctl restart mysqld</p>" 0304 "<p># mysql -u root</p>" 0305 "<p>...</p>" 0306 "<p>Enter SQL code to Mysql prompt in order to init digiKam databases with grant privileges (see behind)</p>" 0307 "<p>...</p>" 0308 "<p>quit</p>" 0309 "<p>NOTE: If you have problems with a MySQL server on Ubuntu based Linux system, " 0310 "use the addition command in the mysql prompt to be able to create MySQL triggers.<br>" 0311 "SET GLOBAL log_bin_trust_function_creators=1;</p>" 0312 "<p>NOTE: If you have an enormous collection, you should start MySQL server with " 0313 "mysql --max_allowed_packet=128M OR in my.ini or ~/.my.cnf, change the settings</p>")); 0314 0315 notice2->setOpenExternalLinks(false); 0316 notice2->setOpenLinks(false); 0317 notice2->setReadOnly(true); 0318 0319 vlay2->addWidget(notice, 0); 0320 vlay2->addWidget(d->sqlInit, 10); 0321 vlay2->addWidget(notice2, 20); 0322 vlay2->setContentsMargins(spacing, spacing, spacing, spacing); 0323 vlay2->setSpacing(spacing); 0324 0325 d->tab->addTab(d->dbNoticeBox, i18n("Requirements")); 0326 0327 // -------------------------------------------------------- 0328 0329 d->dbDetailsBox = new QGroupBox(i18n("Database Server Technical Details"), this); 0330 QVBoxLayout* const vlay3 = new QVBoxLayout(d->dbDetailsBox); 0331 QLabel* const details = new QLabel(i18n("<p>Use this configuration view to set all information " 0332 "to be connected to a remote " 0333 "<a href=\"https://en.wikipedia.org/wiki/MySQL\">Mysql database server</a> " 0334 "(or <a href=\"https://en.wikipedia.org/wiki/MariaDB\">MariaDB</a>) " 0335 "through a network. " 0336 "As with Sqlite or MySQL internal server, 3 databases will be stored " 0337 "on the remote server: one for all collections properties, " 0338 "one to store compressed thumbnails, and one to store faces " 0339 "recognition metadata.</p>" 0340 "<p>Unlike Sqlite or MySQL internal server, you can customize the " 0341 "database names to simplify your backups.</p>" 0342 "<p>Databases are digiKam core engines. To prevent performance issues, " 0343 "take a care to use a fast network link between the client and the server " 0344 "computers. It is also recommended to host database files on " 0345 "fast hardware (as <a href=\"https://en.wikipedia.org/wiki/Solid-state_drive\">SSD</a>) " 0346 "with enough free space, especially for thumbnails database, even if data are compressed using wavelets image format <a href=\"https://en.wikipedia.org/wiki/Progressive_Graphics_File\">" 0347 "PGF</a>.</p>" 0348 "<p>The databases must be created previously on the remote server by the administrator. " 0349 "Look in <b>Requirements</b> tab for details.</p>"), 0350 d->dbDetailsBox); 0351 details->setWordWrap(true); 0352 0353 vlay3->addWidget(details); 0354 vlay3->setContentsMargins(spacing, spacing, spacing, spacing); 0355 vlay3->setSpacing(spacing); 0356 0357 d->tab->addTab(d->dbDetailsBox, i18n("Documentation")); 0358 0359 // -------------------------------------------------------- 0360 0361 vlay->addWidget(typeHbox); 0362 vlay->addWidget(new DLineWidget(Qt::Horizontal)); 0363 vlay->addWidget(d->dbPathLabel); 0364 vlay->addWidget(d->dbPathEdit); 0365 vlay->addWidget(d->mysqlCmdBox); 0366 vlay->addWidget(d->tab); 0367 vlay->addWidget(d->walModeCheck); 0368 vlay->addWidget(d->walLabel); 0369 vlay->addStretch(10); 0370 vlay->setContentsMargins(spacing, spacing, spacing, spacing); 0371 vlay->setSpacing(spacing); 0372 0373 // -------------------------------------------------------- 0374 0375 layout->setContentsMargins(QMargins()); 0376 layout->setSpacing(spacing); 0377 layout->addWidget(dbConfigBox); 0378 0379 // -------------------------------------------------------- 0380 0381 connect(d->dbType, SIGNAL(currentIndexChanged(int)), 0382 this, SLOT(slotHandleDBTypeIndexChanged(int))); 0383 0384 connect(checkDBConnectBtn, SIGNAL(clicked()), 0385 this, SLOT(slotCheckMysqlServerConnection())); 0386 0387 connect(defaultValuesBtn, SIGNAL(clicked()), 0388 this, SLOT(slotResetMysqlServerDBNames())); 0389 0390 connect(d->dbNameCore, SIGNAL(textChanged(QString)), 0391 this, SLOT(slotUpdateSqlInit())); 0392 0393 connect(d->dbNameThumbs->lineEdit(), SIGNAL(textChanged(QString)), 0394 this, SLOT(slotUpdateSqlInit())); 0395 0396 connect(d->dbNameFace, SIGNAL(textChanged(QString)), 0397 this, SLOT(slotUpdateSqlInit())); 0398 0399 connect(d->dbNameSimilarity, SIGNAL(textChanged(QString)), 0400 this, SLOT(slotUpdateSqlInit())); 0401 0402 connect(d->userName, SIGNAL(textChanged(QString)), 0403 this, SLOT(slotUpdateSqlInit())); 0404 0405 slotHandleDBTypeIndexChanged(d->dbType->currentIndex()); 0406 } 0407 0408 int DatabaseSettingsWidget::databaseType() const 0409 { 0410 return d->dbType->currentData().toInt(); 0411 } 0412 0413 QString DatabaseSettingsWidget::databasePath() const 0414 { 0415 return d->dbPathEdit->fileDlgPath(); 0416 } 0417 0418 void DatabaseSettingsWidget::setDatabasePath(const QString& path) 0419 { 0420 d->dbPathEdit->setFileDlgPath(path); 0421 } 0422 0423 DbEngineParameters DatabaseSettingsWidget::orgDatabasePrm() const 0424 { 0425 return d->orgPrms; 0426 } 0427 0428 QString DatabaseSettingsWidget::databaseBackend() const 0429 { 0430 switch (databaseType()) 0431 { 0432 case MysqlInternal: 0433 case MysqlServer: 0434 { 0435 return DbEngineParameters::MySQLDatabaseType(); 0436 } 0437 0438 default: // SQlite 0439 { 0440 return DbEngineParameters::SQLiteDatabaseType(); 0441 } 0442 } 0443 } 0444 0445 void DatabaseSettingsWidget::slotResetMysqlServerDBNames() 0446 { 0447 d->dbNameCore->setText(QLatin1String("digikam")); 0448 d->dbNameThumbs->setFileDlgPath(QLatin1String("digikam")); 0449 d->dbNameFace->setText(QLatin1String("digikam")); 0450 d->dbNameSimilarity->setText(QLatin1String("digikam")); 0451 } 0452 0453 void DatabaseSettingsWidget::slotHandleDBTypeIndexChanged(int index) 0454 { 0455 int dbType = d->dbType->itemData(index).toInt(); 0456 setDatabaseInputFields(dbType); 0457 handleInternalServer(dbType); 0458 slotUpdateSqlInit(); 0459 } 0460 0461 void DatabaseSettingsWidget::setDatabaseInputFields(int index) 0462 { 0463 switch (index) 0464 { 0465 case SQlite: 0466 { 0467 d->dbPathLabel->setVisible(true); 0468 d->dbPathEdit->setVisible(true); 0469 d->walModeCheck->setVisible(true); 0470 d->walLabel->setVisible(true); 0471 d->mysqlCmdBox->setVisible(false); 0472 d->tab->setVisible(false); 0473 0474 connect(d->dbPathEdit->lineEdit(), SIGNAL(textChanged(QString)), 0475 this, SLOT(slotDatabasePathEditedDelayed())); 0476 0477 break; 0478 } 0479 0480 case MysqlInternal: 0481 { 0482 d->dbPathLabel->setVisible(true); 0483 d->dbPathEdit->setVisible(true); 0484 d->walModeCheck->setVisible(false); 0485 d->walLabel->setVisible(false); 0486 d->mysqlCmdBox->setVisible(true); 0487 d->tab->setVisible(false); 0488 0489 connect(d->dbPathEdit->lineEdit(), SIGNAL(textChanged(QString)), 0490 this, SLOT(slotDatabasePathEditedDelayed())); 0491 0492 break; 0493 } 0494 0495 default: // MysqlServer 0496 { 0497 d->dbPathLabel->setVisible(false); 0498 d->dbPathEdit->setVisible(false); 0499 d->walModeCheck->setVisible(false); 0500 d->walLabel->setVisible(false); 0501 d->mysqlCmdBox->setVisible(false); 0502 d->tab->setVisible(true); 0503 0504 disconnect(d->dbPathEdit->lineEdit(), SIGNAL(textChanged(QString)), 0505 this, SLOT(slotDatabasePathEditedDelayed())); 0506 break; 0507 } 0508 } 0509 } 0510 0511 void DatabaseSettingsWidget::handleInternalServer(int index) 0512 { 0513 bool internal = (index == MysqlInternal); 0514 0515 d->hostName->setDisabled(internal); 0516 d->hostPort->setDisabled(internal); 0517 d->dbNameCore->setDisabled(internal); 0518 d->dbNameThumbs->setDisabled(internal); 0519 d->dbNameFace->setDisabled(internal); 0520 d->dbNameSimilarity->setDisabled(internal); 0521 d->userName->setDisabled(internal); 0522 d->password->setDisabled(internal); 0523 d->connectOpts->setDisabled(internal); 0524 } 0525 0526 void DatabaseSettingsWidget::slotUpdateSqlInit() 0527 { 0528 QString sql = QString::fromLatin1("CREATE USER \'%1\'@\'%2\' IDENTIFIED BY \'<b>password</b>\';<br>") 0529 .arg(d->userName->text()) 0530 .arg(d->hostName->text()); 0531 0532 sql += QString::fromLatin1("GRANT ALL ON *.* TO \'%1\'@\'%2\' IDENTIFIED BY \'<b>password</b>\';<br>") 0533 .arg(d->userName->text()) 0534 .arg(d->hostName->text()); 0535 0536 sql += QString::fromLatin1("CREATE DATABASE `%1`;<br>" 0537 "GRANT ALL PRIVILEGES ON `%2`.* TO \'%3\'@\'%4\';<br>") 0538 .arg(d->dbNameCore->text()) 0539 .arg(d->dbNameCore->text()) 0540 .arg(d->userName->text()) 0541 .arg(d->hostName->text()); 0542 0543 if (isNotEqualToThumbName(d->dbNameCore->text())) 0544 { 0545 sql += QString::fromLatin1("CREATE DATABASE `%1`;<br>" 0546 "GRANT ALL PRIVILEGES ON `%2`.* TO \'%3\'@\'%4\';<br>") 0547 .arg(d->dbNameThumbs->fileDlgPath()) 0548 .arg(d->dbNameThumbs->fileDlgPath()) 0549 .arg(d->userName->text()) 0550 .arg(d->hostName->text()); 0551 } 0552 0553 if (isNotEqualToThumbName(d->dbNameFace->text()) && 0554 (d->dbNameFace->text() != d->dbNameCore->text())) 0555 { 0556 sql += QString::fromLatin1("CREATE DATABASE `%1`;<br>" 0557 "GRANT ALL PRIVILEGES ON `%2`.* TO \'%3\'@\'%4\';<br>") 0558 .arg(d->dbNameFace->text()) 0559 .arg(d->dbNameFace->text()) 0560 .arg(d->userName->text()) 0561 .arg(d->hostName->text()); 0562 } 0563 0564 if (isNotEqualToThumbName(d->dbNameSimilarity->text()) && 0565 (d->dbNameSimilarity->text() != d->dbNameCore->text()) && 0566 (d->dbNameSimilarity->text() != d->dbNameFace->text())) 0567 { 0568 sql += QString::fromLatin1("CREATE DATABASE `%1`;<br>" 0569 "GRANT ALL PRIVILEGES ON `%2`.* TO \'%3\'@\'%4\';<br>") 0570 .arg(d->dbNameSimilarity->text()) 0571 .arg(d->dbNameSimilarity->text()) 0572 .arg(d->userName->text()) 0573 .arg(d->hostName->text()); 0574 } 0575 0576 sql += QLatin1String("FLUSH PRIVILEGES;<br>"); 0577 0578 d->sqlInit->setText(sql); 0579 0580 QFileInfo thumbDB(d->dbNameThumbs->fileDlgPath()); 0581 0582 if (thumbDB.exists() && thumbDB.isDir()) 0583 { 0584 d->dbThumbsLabel->setText(i18n("Thumbs Db Folder:")); 0585 } 0586 else 0587 { 0588 d->dbThumbsLabel->setText(i18n("Thumbs Db Name:")); 0589 } 0590 } 0591 0592 void DatabaseSettingsWidget::slotCheckMysqlServerConnection() 0593 { 0594 QString error; 0595 0596 if (checkMysqlServerConnection(error)) 0597 { 0598 QMessageBox::information(qApp->activeWindow(), i18nc("@title:window", "Database Connection Test"), 0599 i18n("Database connection test successful.")); 0600 } 0601 else 0602 { 0603 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Database Connection Test"), 0604 i18n("Database connection test was not successful. <p>Error was: %1</p>", 0605 error)); 0606 } 0607 } 0608 0609 bool DatabaseSettingsWidget::checkMysqlServerConnectionConfig(QString& error) 0610 { 0611 if (d->hostName->text().isEmpty()) 0612 { 0613 error = i18n("The server hostname is empty"); 0614 return false; 0615 } 0616 0617 if (d->userName->text().isEmpty()) 0618 { 0619 error = i18n("The server user name is empty"); 0620 return false; 0621 } 0622 0623 return true; 0624 } 0625 0626 bool DatabaseSettingsWidget::checkMysqlServerDbNamesConfig(QString& error) 0627 { 0628 if (d->dbNameCore->text().isEmpty()) 0629 { 0630 error = i18n("The core database name is empty"); 0631 return false; 0632 } 0633 0634 if (d->dbNameThumbs->fileDlgPath().isEmpty()) 0635 { 0636 error = i18n("The thumbnails database name is empty"); 0637 return false; 0638 } 0639 0640 if (d->dbNameFace->text().isEmpty()) 0641 { 0642 error = i18n("The face database name is empty"); 0643 return false; 0644 } 0645 0646 if (d->dbNameSimilarity->text().isEmpty()) 0647 { 0648 error = i18n("The similarity database name is empty"); 0649 return false; 0650 } 0651 0652 return true; 0653 } 0654 0655 bool DatabaseSettingsWidget::checkMysqlServerConnection(QString& error) 0656 { 0657 if (!checkMysqlServerConnectionConfig(error)) 0658 { 0659 return false; 0660 } 0661 0662 bool result = false; 0663 0664 qApp->setOverrideCursor(Qt::WaitCursor); 0665 0666 QString databaseID(QLatin1String("ConnectionTest")); 0667 0668 { 0669 QSqlDatabase testDatabase = QSqlDatabase::addDatabase(databaseBackend(), databaseID); 0670 0671 DbEngineParameters prm = getDbEngineParameters(); 0672 qCDebug(DIGIKAM_DATABASE_LOG) << "Testing DB connection (" << databaseID << ") with these settings:"; 0673 qCDebug(DIGIKAM_DATABASE_LOG) << prm; 0674 0675 testDatabase.setHostName(prm.hostName); 0676 testDatabase.setPort(prm.port); 0677 testDatabase.setUserName(prm.userName); 0678 testDatabase.setPassword(prm.password); 0679 testDatabase.setConnectOptions(prm.connectOptions); 0680 0681 result = testDatabase.open(); 0682 error = testDatabase.lastError().text(); 0683 testDatabase.close(); 0684 } 0685 0686 QSqlDatabase::removeDatabase(databaseID); 0687 0688 qApp->restoreOverrideCursor(); 0689 0690 return result; 0691 } 0692 0693 void DatabaseSettingsWidget::setParametersFromSettings(const ApplicationSettings* const settings, 0694 const bool& migration) 0695 { 0696 d->orgPrms = settings->getDbEngineParameters(); 0697 0698 if (d->orgPrms.databaseType == DbEngineParameters::SQLiteDatabaseType()) 0699 { 0700 d->dbPathEdit->setFileDlgPath(d->orgPrms.getCoreDatabaseNameOrDir()); 0701 d->dbType->setCurrentIndex(d->dbTypeMap[SQlite]); 0702 d->walModeCheck->setChecked(d->orgPrms.walMode); 0703 slotResetMysqlServerDBNames(); 0704 0705 if (settings->getDatabaseDirSetAtCmd() && !migration) 0706 { 0707 d->dbType->setEnabled(false); 0708 d->dbPathEdit->setEnabled(false); 0709 d->dbPathLabel->setText(d->dbPathLabel->text() + 0710 i18n("This path was set as a command line" 0711 "option (--database-directory).")); 0712 } 0713 } 0714 0715 #ifdef HAVE_MYSQLSUPPORT 0716 0717 # ifdef HAVE_INTERNALMYSQL 0718 0719 else if ((d->orgPrms.databaseType == DbEngineParameters::MySQLDatabaseType()) && d->orgPrms.internalServer) 0720 { 0721 d->dbPathEdit->setFileDlgPath(d->orgPrms.internalServerPath()); 0722 d->dbType->setCurrentIndex(d->dbTypeMap[MysqlInternal]); 0723 d->mysqlUpgradeBin.setup(QFileInfo(d->orgPrms.internalServerMysqlUpgradeCmd).absoluteFilePath()); 0724 d->mysqlServerBin.setup(QFileInfo(d->orgPrms.internalServerMysqlServerCmd).absoluteFilePath()); 0725 d->mysqlAdminBin.setup(QFileInfo(d->orgPrms.internalServerMysqlAdminCmd).absoluteFilePath()); 0726 d->mysqlInitBin.setup(QFileInfo(d->orgPrms.internalServerMysqlInitCmd).absoluteFilePath()); 0727 d->dbBinariesWidget->allBinariesFound(); 0728 d->walModeCheck->setChecked(false); 0729 slotResetMysqlServerDBNames(); 0730 } 0731 0732 # endif 0733 0734 else 0735 { 0736 d->dbType->setCurrentIndex(d->dbTypeMap[MysqlServer]); 0737 d->dbNameCore->setText(d->orgPrms.databaseNameCore); 0738 d->dbNameThumbs->setFileDlgPath(d->orgPrms.databaseNameThumbnails); 0739 d->dbNameFace->setText(d->orgPrms.databaseNameFace); 0740 d->dbNameSimilarity->setText(d->orgPrms.databaseNameSimilarity); 0741 d->hostName->setText(d->orgPrms.hostName); 0742 d->hostPort->setValue((d->orgPrms.port == -1) ? 3306 : d->orgPrms.port); 0743 d->connectOpts->setText(d->orgPrms.connectOptions); 0744 d->userName->setText(d->orgPrms.userName); 0745 d->password->setText(d->orgPrms.password); 0746 d->walModeCheck->setChecked(false); 0747 } 0748 0749 #endif 0750 0751 slotHandleDBTypeIndexChanged(d->dbType->currentIndex()); 0752 } 0753 0754 DbEngineParameters DatabaseSettingsWidget::getDbEngineParameters() const 0755 { 0756 DbEngineParameters prm; 0757 0758 switch (databaseType()) 0759 { 0760 case SQlite: 0761 { 0762 prm = DbEngineParameters::parametersForSQLiteDefaultFile(databasePath()); 0763 prm.walMode = d->walModeCheck->isChecked(); 0764 break; 0765 } 0766 0767 case MysqlInternal: 0768 { 0769 prm = DbEngineParameters::defaultParameters(databaseBackend()); 0770 prm.setInternalServerPath(databasePath()); 0771 prm.internalServerMysqlUpgradeCmd = d->mysqlUpgradeBin.path(); 0772 prm.internalServerMysqlServerCmd = d->mysqlServerBin.path(); 0773 prm.internalServerMysqlAdminCmd = d->mysqlAdminBin.path(); 0774 prm.internalServerMysqlInitCmd = d->mysqlInitBin.path(); 0775 break; 0776 } 0777 0778 default: // MysqlServer 0779 { 0780 prm.internalServer = false; 0781 prm.databaseType = databaseBackend(); 0782 prm.databaseNameCore = d->dbNameCore->text(); 0783 prm.databaseNameThumbnails = d->dbNameThumbs->fileDlgPath(); 0784 prm.databaseNameFace = d->dbNameFace->text(); 0785 prm.databaseNameSimilarity = d->dbNameSimilarity->text(); 0786 prm.connectOptions = d->connectOpts->text(); 0787 prm.hostName = d->hostName->text(); 0788 prm.port = d->hostPort->value(); 0789 prm.userName = d->userName->text(); 0790 prm.password = d->password->text(); 0791 break; 0792 } 0793 } 0794 0795 return prm; 0796 } 0797 0798 void DatabaseSettingsWidget::slotDatabasePathEditedDelayed() 0799 { 0800 QTimer::singleShot(300, this, SLOT(slotDatabasePathEdited())); 0801 } 0802 0803 void DatabaseSettingsWidget::slotDatabasePathEdited() 0804 { 0805 QString newPath = databasePath(); 0806 0807 #ifndef Q_OS_WIN 0808 0809 if (!newPath.isEmpty() && !QDir::isAbsolutePath(newPath)) 0810 { 0811 d->dbPathEdit->setFileDlgPath(QDir::homePath() + QLatin1Char('/') + newPath); 0812 } 0813 0814 #endif 0815 0816 d->dbPathEdit->setFileDlgPath(newPath); 0817 } 0818 0819 bool DatabaseSettingsWidget::checkDatabaseSettings() 0820 { 0821 switch (databaseType()) 0822 { 0823 case SQlite: 0824 { 0825 return checkDatabasePath(); 0826 } 0827 0828 case MysqlInternal: 0829 { 0830 if (!checkDatabasePath()) 0831 { 0832 return false; 0833 } 0834 0835 if (!d->dbBinariesWidget->allBinariesFound()) 0836 { 0837 return false; 0838 } 0839 0840 return true; 0841 } 0842 0843 default: // MysqlServer 0844 { 0845 QString error; 0846 0847 if (!checkMysqlServerDbNamesConfig(error)) 0848 { 0849 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Database Configuration"), 0850 i18n("The database names configuration is not valid. Error is <br/><p>%1</p><br/>" 0851 "Please check your configuration.", 0852 error)); 0853 return false; 0854 } 0855 0856 if (!checkMysqlServerConnection(error)) 0857 { 0858 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Database Connection Test"), 0859 i18n("Testing database connection has failed with error<br/><p>%1</p><br/>" 0860 "Please check your configuration.", 0861 error)); 0862 return false; 0863 } 0864 } 0865 } 0866 0867 return true; 0868 } 0869 0870 bool DatabaseSettingsWidget::checkDatabasePath() 0871 { 0872 QString dbFolder = databasePath(); 0873 qCDebug(DIGIKAM_DATABASE_LOG) << "Database directory is : " << dbFolder; 0874 0875 if (dbFolder.isEmpty()) 0876 { 0877 QMessageBox::information(qApp->activeWindow(), qApp->applicationName(), 0878 i18n("You must select a folder for digiKam to " 0879 "store information and metadata in a database file.")); 0880 return false; 0881 } 0882 0883 QDir targetPath(dbFolder); 0884 0885 if (!targetPath.exists()) 0886 { 0887 int rc = QMessageBox::question(qApp->activeWindow(), i18nc("@title:window", "Create Database Folder?"), 0888 i18n("<p>The folder to put your database in does not seem to exist:</p>" 0889 "<p><b>%1</b></p>" 0890 "Would you like digiKam to create it for you?", dbFolder)); 0891 0892 if (rc == QMessageBox::No) 0893 { 0894 return false; 0895 } 0896 0897 if (!targetPath.mkpath(dbFolder)) 0898 { 0899 QMessageBox::information(qApp->activeWindow(), i18nc("@title:window", "Create Database Folder Failed"), 0900 i18n("<p>digiKam could not create the folder to host your database file.\n" 0901 "Please select a different location.</p>" 0902 "<p><b>%1</b></p>", dbFolder)); 0903 return false; 0904 } 0905 } 0906 0907 QFileInfo path(dbFolder); 0908 0909 #ifdef Q_OS_WIN 0910 0911 // Work around bug #189168 0912 0913 QTemporaryFile temp; 0914 temp.setFileTemplate(path.filePath() + QLatin1String("/XXXXXX")); 0915 0916 if (!temp.open()) 0917 0918 #else 0919 0920 if (!path.isWritable()) 0921 0922 #endif 0923 0924 { 0925 QMessageBox::information(qApp->activeWindow(), i18nc("@title:window", "No Database Write Access"), 0926 i18n("<p>You do not seem to have write access " 0927 "for the folder to host the database file.<br/>" 0928 "Please select a different location.</p>" 0929 "<p><b>%1</b></p>", dbFolder)); 0930 return false; 0931 } 0932 0933 return true; 0934 } 0935 0936 bool DatabaseSettingsWidget::isNotEqualToThumbName(const QString& name) 0937 { 0938 QFileInfo thumbDB(d->dbNameThumbs->fileDlgPath()); 0939 bool isDir = (thumbDB.exists() && thumbDB.isDir() && thumbDB.isAbsolute()); 0940 0941 return (!isDir && (d->dbNameThumbs->fileDlgPath() != name)); 0942 } 0943 0944 } // namespace Digikam 0945 0946 #include "moc_dbsettingswidget.cpp"