[general]
name=RecoverLand
qgisMinimumVersion=3.40
description=Local GIS data audit and recovery: automatic capture, search and restoration of modifications
about=RecoverLand automatically captures every modification (attribute, geometry, deletion) in a local SQLite journal, then allows you to search, compare and restore previous states of features.
version=6.0.0
changelog=6.0.0 Journal schema v6 and a rewind that stops lying. SCHEMA: the datasource fingerprint was wrong in three ways -- DB providers captured the SCHEMA where they meant the table (every table of a schema shared one identity, so an INSERT in one and a DELETE in another cancelled out and neither was restored), a display filter appended |subset= and split one file's history in two, and spatialite/delimitedtext ran abspath on something that is not a path. Fixed through QgsDataSourceUri, and the past is reconciled WITHOUT REWRITING ANYTHING: migration v6 records old->canonical rows in datasource_alias (a table present since v4 with no caller) and the read paths expand a fingerprint into its historical forms. No audit_event row is touched; deleting the alias rows restores the previous state exactly. Ambiguity is never guessed: when an old DB fingerprint would have to split into several, no alias is written and the source is NAMED to the user before a rewind. Capture never depends on a journal row -- the pre-v6 normaliser is kept so a tracked-layer selection saved by any version keeps designating the same layers. A read-only journal still opens. DATA LOSS FIXED: the rewind no longer writes on a target it cannot prove, on either engine (a repacked shapefile made it overwrite an unrelated feature and report success); a datetime no longer loses the local offset on every write (it drifted cumulatively); a binary field is no longer restored as the ASCII of its own base64 label; gid/username stop being classed as audit noise, so a re-inserted feature keeps its primary key; the literal text 'NULL' is no longer erased at capture; an entity deleted then re-created comes back with ALL its fields, and a chain of more than ten edits reverts every field it touched, not just the oldest. SILENT LOSS FIXED: WriteQueue no longer acknowledges events with no live writer and survives a non-SQLite exception; pending events are actually re-integrated (they never were, not once); a journal on a path holding '#' no longer opens EMPTY on read while capture writes fine; the excess purge no longer cuts a commit in half; capture reads PROVIDER fields, so a joined layer stops producing phantom attribute changes; the capture lock is released when a restore dies. TRUTH: a rewind that had to abandon fields now says so and NAMES them instead of reporting a complete restore; undoing an INSERT whose feature is already gone is a skip, not a failure. REVIEW: the lens ran on the local wall clock while the journal is UTC -- both modes now denote the same instant. PACKAGING: supportsQt6, without which Qt6 builds of QGIS 3.x refused to load the plugin at all. VALIDATION: 60+ runtime scenarios versioned and run headless by CI, on a Qt6 image the matrix never covered. LIMITS: on a layer with no primary key the reliable recovery window is days, not months -- see the Limits page. 5.0.1 5.0.1 Fix: Review Snapshot activation no longer freezes the UI. Legend construction is deferred during overlay creation (disabling ShowLegend flag on the layer tree model) and rebuilt chunk-by-chunk afterwards, preventing ~10s main-thread gaps caused by SVG icon probes. Also fixes empty storage_crs fallback for memory overlay layers created from sources with invalid CRS. 5.0.0 Preview module: interactive temporal lens and snapshot review (GeoGit) with date-bar canvas overlay, snapshot reconstruction engine, and dedicated lens dock with rectangle/polygon map tools. Declared minimum QGIS version corrected to 3.40 to match the compatibility baseline declared in compat.py (no code change; the cross-version resolvers in compat.py remain defensive for older builds). 4.9.0 First plugins.qgis.org release. Qt6/QGIS4 fix: closeEvent now uses QgisCompat.MSG_YES/MSG_NO instead of QMessageBox.Yes/.No (was crashing on Qt6 with AttributeError: type object 'QMessageBox' has no attribute 'Yes'). Pre-upload hardening for the QGIS plugin scanner: SQL-fragment whitelist tightened in _cutoff_where (assert_safe_fragment on ds_cond/cutoff_col/op), trace_clause / user_clause now built with str.join() instead of `+` to silence Bandit B608 false positive (the `# nosec B608` line marker did not propagate across multi-line implicit string concatenation), 20 flake8 warnings fixed across 10 files (W503 operators moved to end-of-line in event_stream_repository, integrity, write_queue, search_service, schema_drift, support_policy, restore_service, rewind_dedup, observability, qgs_task_support; E302 in support_policy, E226 in write_queue). Reproducible packaging via scripts/build_release.py (sources from git ls-files + defense-in-depth blacklist with dotfile whitelist, single RecoverLand/ top-level, stable dist/RecoverLand.zip). docs: threading-model diagram decluttered (architecture.html). Same SQLite journal, same rewind engine, same trace lifecycle as 4.8.2. 4.8.2 Geometry diagnostics: provider.reloadData after commit (stale provider cache on GPKG/PG), detailed logs in _buffer_update to expose silent failures (target_fid, attr_ok/fail, wkb_len, geom rejected). 4.8.1 Critical fix: canvas refresh after Rewind/Undo via refreshAllLayers (cache invalidation). 4.8.0 UX hardening: reentrant tracker suppress (fix bug where Rewind captured its own edits), visual feedback on restore cancel, visible auto-undo failure logs, tracking disable confirmation, accurate STRICT cancel message, tracking toggle log. 4.7.0 QA hardening: strict atomic temporal restore, pre-check geom capability before attr write, accurate cancel state, real-layer preflight, write queue explicit reject, journal lock degraded mode, Python 3.14 XMLParser fix. 4.6.0 Perf: async stats and smart bar, BLOB-free search, strict restore chunked by action, lightweight commit capture (NoGeometry). 4.5.0 qgisMinimumVersion lowered from 3.44 to 3.40 LTR. compat.py: cross-version resolvers for Qgis.GeometryType, Qgis.WkbType.NoGeometry, plus qgis_version_info() and is_qt6() helpers. geometry_preview.py: QgsRubberBand uses QgisCompat.GEOM_* (no more raw ints, fixes Qt6 strict TypeError). geometry_utils.py: NoGeometry detection covers Qgis.WkbType and QgsWkbTypes paths. CI matrix uses floating tags (ltr, latest, 4.0-trixie). 15 compat regression tests added. 4.4.0 Fix: dashboard showed 0 on PG/DB layers (timezone drift in date filter). Fix: project switch kept stale journal data. Dashboard now refreshes in real time after each commit. 4.3.1 Datasource alias table for moved layers. Writer lock (PID-based, Windows-safe). Journal audit service. Public API (journal_path, log_path, stats). Rotating log handler. Password scrubbing via QgsDataSourceUri. Dashboard shows full journal span on open. 4.2.0 Security hardening (defusedxml, SQL safety, log sanitization). DB restore: PostgreSQL, MSSQL, Oracle via saved connections. Removed legacy PG code (~930 lines). 4.1.3 Temporal restore, time slider, schema v2, preflight checks, QGIS 3.44-4.x compat. 4.0.0 Local SQLite audit, automatic capture, paginated search, schema drift, retention.
author=Youcef ADDA
email=youcef.geodesien@gmail.com
category=Data Management
tags=audit,recovery,restore,sketching,versioning,local,sqlite,temporal,rollback,health,preview
icon=icon.svg
qgisMaximumVersion=4.99
# Qt6 builds of QGIS 3.x refuse to load a plugin without this flag:
# pyplugin_installer/installer_data.py marks it error="incompatible" when
# QT_VERSION_STR major == 6 and supportsQt6 is not TRUE/YES. QGIS 4.x dropped
# the check (it is Qt6 by construction), so the flag is inert there.
supportsQt6=True
experimental=False
deprecated=False
homepage=https://github.com/yadda07/recoverland/
tracker=https://github.com/yadda07/recoverland/issues/
repository=https://github.com/yadda07/recoverland/
