Compare commits
4 commits
3e6a08231c
...
cc9453997e
| Author | SHA1 | Date | |
|---|---|---|---|
| cc9453997e | |||
| 3db384e7e4 | |||
| f778afd268 | |||
| 0caf0efeef |
6 changed files with 74 additions and 6 deletions
9
CHANGELOG.md
Normal file
9
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# 0.1.1
|
||||
|
||||
* Add ability to change the key
|
||||
* Add ability to jump to today's date
|
||||
* Add shortcut for Settings (Ctrl+E) so as not to collide with Ctrl+S (Save)
|
||||
|
||||
# 0.1.0
|
||||
|
||||
* Initial release.
|
||||
|
|
@ -31,7 +31,6 @@ There is deliberately no network connectivity or syncing intended.
|
|||
|
||||
* Search
|
||||
* Taxonomy/tagging
|
||||
* Ability to change the SQLCipher key
|
||||
* Export to other formats (plaintext, json, sql etc)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,25 @@ class DBManager:
|
|||
cur.execute("PRAGMA user_version = 1;")
|
||||
self.conn.commit()
|
||||
|
||||
def rekey(self, new_key: str) -> None:
|
||||
"""
|
||||
Change the SQLCipher passphrase in-place, then reopen the connection
|
||||
with the new key to verify.
|
||||
"""
|
||||
if self.conn is None:
|
||||
raise RuntimeError("Database is not connected")
|
||||
cur = self.conn.cursor()
|
||||
# Change the encryption key of the currently open database
|
||||
cur.execute(f"PRAGMA rekey = '{new_key}';")
|
||||
self.conn.commit()
|
||||
|
||||
# Close and reopen with the new key to verify and restore PRAGMAs
|
||||
self.conn.close()
|
||||
self.conn = None
|
||||
self.cfg.key = new_key
|
||||
if not self.connect():
|
||||
raise sqlite.Error("Re-open failed after rekey")
|
||||
|
||||
def get_entry(self, date_iso: str) -> str:
|
||||
cur = self.conn.cursor()
|
||||
cur.execute("SELECT content FROM entries WHERE date = ?;", (date_iso,))
|
||||
|
|
|
|||
|
|
@ -72,7 +72,8 @@ class MainWindow(QMainWindow):
|
|||
act_save.setShortcut("Ctrl+S")
|
||||
act_save.triggered.connect(lambda: self._save_current(explicit=True))
|
||||
file_menu.addAction(act_save)
|
||||
act_settings = QAction("&Settings", self)
|
||||
act_settings = QAction("S&ettings", self)
|
||||
act_save.setShortcut("Ctrl+E")
|
||||
act_settings.triggered.connect(self._open_settings)
|
||||
file_menu.addAction(act_settings)
|
||||
file_menu.addSeparator()
|
||||
|
|
@ -97,6 +98,13 @@ class MainWindow(QMainWindow):
|
|||
nav_menu.addAction(act_next)
|
||||
self.addAction(act_next)
|
||||
|
||||
act_today = QAction("Today", self)
|
||||
act_today.setShortcut("Ctrl+T")
|
||||
act_today.setShortcutContext(Qt.ApplicationShortcut)
|
||||
act_today.triggered.connect(self._adjust_today)
|
||||
nav_menu.addAction(act_today)
|
||||
self.addAction(act_today)
|
||||
|
||||
# Autosave
|
||||
self._dirty = False
|
||||
self._save_timer = QTimer(self)
|
||||
|
|
@ -176,6 +184,11 @@ class MainWindow(QMainWindow):
|
|||
d = self.calendar.selectedDate().addDays(delta)
|
||||
self.calendar.setSelectedDate(d)
|
||||
|
||||
def _adjust_today(self):
|
||||
"""Jump to today."""
|
||||
today = QDate.currentDate()
|
||||
self.calendar.setSelectedDate(today)
|
||||
|
||||
def _on_date_changed(self):
|
||||
"""
|
||||
When the calendar selection changes, save the previous day's note if dirty,
|
||||
|
|
@ -219,7 +232,7 @@ class MainWindow(QMainWindow):
|
|||
self._save_date(self._current_date_iso(), explicit)
|
||||
|
||||
def _open_settings(self):
|
||||
dlg = SettingsDialog(self.cfg, self)
|
||||
dlg = SettingsDialog(self.cfg, self.db, self)
|
||||
if dlg.exec() == QDialog.Accepted:
|
||||
new_cfg = dlg.config
|
||||
if new_cfg.path != self.cfg.path:
|
||||
|
|
|
|||
|
|
@ -13,17 +13,20 @@ from PySide6.QtWidgets import (
|
|||
QFileDialog,
|
||||
QDialogButtonBox,
|
||||
QSizePolicy,
|
||||
QMessageBox,
|
||||
)
|
||||
|
||||
from .db import DBConfig
|
||||
from .db import DBConfig, DBManager
|
||||
from .settings import save_db_config
|
||||
from .key_prompt import KeyPrompt
|
||||
|
||||
|
||||
class SettingsDialog(QDialog):
|
||||
def __init__(self, cfg: DBConfig, parent=None):
|
||||
def __init__(self, cfg: DBConfig, db: DBManager, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Settings")
|
||||
self._cfg = DBConfig(path=cfg.path, key="")
|
||||
self._db = db
|
||||
|
||||
form = QFormLayout()
|
||||
form.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
|
||||
|
|
@ -44,12 +47,17 @@ class SettingsDialog(QDialog):
|
|||
h.setStretch(1, 0)
|
||||
form.addRow("Database path", path_row)
|
||||
|
||||
# Change key button
|
||||
self.rekey_btn = QPushButton("Change key")
|
||||
self.rekey_btn.clicked.connect(self._change_key)
|
||||
|
||||
bb = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
|
||||
bb.accepted.connect(self._save)
|
||||
bb.rejected.connect(self.reject)
|
||||
|
||||
v = QVBoxLayout(self)
|
||||
v.addLayout(form)
|
||||
v.addWidget(self.rekey_btn)
|
||||
v.addWidget(bb)
|
||||
|
||||
def _browse(self):
|
||||
|
|
@ -67,6 +75,26 @@ class SettingsDialog(QDialog):
|
|||
save_db_config(self._cfg)
|
||||
self.accept()
|
||||
|
||||
def _change_key(self):
|
||||
p1 = KeyPrompt(self, title="Change key", message="Enter new key")
|
||||
if p1.exec() != QDialog.Accepted:
|
||||
return
|
||||
new_key = p1.key()
|
||||
p2 = KeyPrompt(self, title="Change key", message="Re-enter new key")
|
||||
if p2.exec() != QDialog.Accepted:
|
||||
return
|
||||
if new_key != p2.key():
|
||||
QMessageBox.warning(self, "Key mismatch", "The two entries did not match.")
|
||||
return
|
||||
if not new_key:
|
||||
QMessageBox.warning(self, "Empty key", "Key cannot be empty.")
|
||||
return
|
||||
try:
|
||||
self._db.rekey(new_key)
|
||||
QMessageBox.information(self, "Key changed", "The database key was updated.")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Could not change key:\n{e}")
|
||||
|
||||
@property
|
||||
def config(self) -> DBConfig:
|
||||
return self._cfg
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "bouquin"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
description = "Bouquin is a simple, opinionated notebook application written in Python, PyQt and SQLCipher."
|
||||
authors = ["Miguel Jacq <mig@mig5.net>"]
|
||||
readme = "README.md"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue