Compare commits
6
Commits
v1.7.9
..
b77bf70ff5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b77bf70ff5 | ||
|
|
edc820051e | ||
|
|
2260a451a5 | ||
|
|
efbbeb7306 | ||
|
|
54f8848e30 | ||
|
|
8a446509c6 |
No files matched your search
+1
-1
@@ -5,7 +5,7 @@
|
||||
*.json
|
||||
!lang/*.json
|
||||
!keywords/*.json
|
||||
!skus/*.json
|
||||
!classification/*.json
|
||||
!package*.json
|
||||
|
||||
# Be explicit about the most sensitive files
|
||||
|
||||
File renamed without changes.
File renamed without changes.
+2
-2
@@ -106,7 +106,7 @@ except ImportError:
|
||||
MASK_AVAILABLE = False
|
||||
|
||||
try:
|
||||
import fitz as _fitz # PyMuPDF — for secure (sanitised) PDF redaction
|
||||
import pymupdf as _fitz # PyMuPDF — for secure (sanitised) PDF redaction
|
||||
PYMUPDF_AVAILABLE = True
|
||||
except ImportError:
|
||||
PYMUPDF_AVAILABLE = False
|
||||
@@ -2727,7 +2727,7 @@ def count_faces_in_file(path, poppler_path=None, neighbors: int = 4) -> int:
|
||||
|
||||
elif ext == ".pdf":
|
||||
if PYMUPDF_AVAILABLE:
|
||||
import fitz as _fitz
|
||||
import pymupdf as _fitz
|
||||
doc = _fitz.open(str(path))
|
||||
for page_idx in range(min(5, len(doc))):
|
||||
pix = doc[page_idx].get_pixmap(dpi=100)
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ try:
|
||||
FileAttributes, FilePipePrinterAccessMask, ShareAccess,
|
||||
ImpersonationLevel,
|
||||
)
|
||||
from smbprotocol.query_info import FileDirectoryInformation
|
||||
from smbprotocol.file_info import FileDirectoryInformation
|
||||
SMB_OK = True
|
||||
except ImportError:
|
||||
SMB_OK = False
|
||||
|
||||
+10
-2
@@ -31,6 +31,8 @@ import threading
|
||||
from pathlib import Path
|
||||
from typing import Iterator, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ── google-auth / google-api-python-client ────────────────────────────────────
|
||||
try:
|
||||
from google.oauth2 import service_account
|
||||
@@ -93,15 +95,21 @@ _MAX_EXPORT_BYTES = 20 * 1024 * 1024 # 20 MB
|
||||
_OU_ROLES_PATH = Path(__file__).parent / "classification" / "google_ou_roles.json"
|
||||
|
||||
def _load_ou_roles() -> tuple[list, list]:
|
||||
"""Load student/staff OU prefix lists from skus/google_ou_roles.json.
|
||||
"""Load student/staff OU prefix lists from classification/google_ou_roles.json.
|
||||
Returns (student_prefixes, staff_prefixes) — both lowercased."""
|
||||
try:
|
||||
import json as _j
|
||||
data = _j.loads(_OU_ROLES_PATH.read_text(encoding="utf-8"))
|
||||
students = [p.lower() for p in data.get("student_ou_prefixes", [])]
|
||||
staff = [p.lower() for p in data.get("staff_ou_prefixes", [])]
|
||||
logger.info("[ou-roles] Loaded %d student + %d staff OU prefixes from classification/google_ou_roles.json",
|
||||
len(students), len(staff))
|
||||
return students, staff
|
||||
except Exception:
|
||||
except FileNotFoundError:
|
||||
logger.warning("[ou-roles] classification/google_ou_roles.json not found — using hardcoded fallback prefixes only")
|
||||
return ["/elever", "/students"], ["/personale", "/staff", "/lærere", "/ansatte"]
|
||||
except Exception as e:
|
||||
logger.error("[ou-roles] Failed to load classification/google_ou_roles.json: %s — using hardcoded fallback prefixes only", e)
|
||||
return ["/elever", "/students"], ["/personale", "/staff", "/lærere", "/ansatte"]
|
||||
|
||||
def classify_ou_role(org_unit_path: str) -> str:
|
||||
|
||||
@@ -552,6 +552,8 @@ class M365Connector:
|
||||
r.raise_for_status()
|
||||
return True # 204 No Content = success
|
||||
raise _requests.exceptions.RetryError(f"Gave up after {self._MAX_RETRIES} attempts: {url}")
|
||||
|
||||
def delete_message(self, user_id: str, message_id: str) -> bool:
|
||||
"""Move an email to Deleted Items (soft delete)."""
|
||||
base = "/me" if (not user_id or user_id == "me") else f"/users/{user_id}"
|
||||
try:
|
||||
|
||||
@@ -114,6 +114,9 @@ def scan_start():
|
||||
try:
|
||||
run_scan(options)
|
||||
_maybe_send_auto_email()
|
||||
except Exception:
|
||||
_log.exception("[scan] background scan thread crashed")
|
||||
_audit("scan_error", "background scan thread crashed", ip="")
|
||||
finally:
|
||||
state._scan_lock.release()
|
||||
threading.Thread(target=_run, daemon=True).start()
|
||||
|
||||
@@ -48,6 +48,7 @@ Never revert to `!!window._googleConnected` / `_fileSources.length > 0` — thos
|
||||
- **Cache invalidation** — `invalidateHistoryCache()` clears `_sessions` and `_latestRefScanId`. All three `*_done` SSE handlers call `window.invalidateHistoryCache?.()`.
|
||||
- **Re-scan diff** — items present in the previous session but absent from the current one are tagged `_resolved: true`, rendered with `.card-resolved` and a green ✓ badge, and NOT added to `S.flaggedData` (grid-only, cannot be bulk-selected or exported).
|
||||
- **Mode transitions** — `startScan()` calls `window.exitHistoryMode?.()` before clearing the grid.
|
||||
- **`renderGrid(files)` hides the landing cards** — whenever `files.length > 0` it hides `#emptyState` and `#lastScanSummary` and shows `#grid`. This is centralised here because the live `scan_file_flagged` handler (`scan.js`) shows the grid but does NOT clear those panels, so results would render *underneath* a still-visible landing/last-scan card until a manual refresh. Do not move this hiding back into individual callers — every render path (live SSE, `loadOpenItems`, history, filters) must clear the landing. The empty case (`files.length === 0`) is left untouched so callers still control the empty/landing state.
|
||||
|
||||
## Card user/group badge — results.js
|
||||
|
||||
|
||||
@@ -122,6 +122,17 @@ function renderGrid(files) {
|
||||
const grid = document.getElementById('grid');
|
||||
grid.innerHTML = '';
|
||||
files.forEach(f => appendCard(f));
|
||||
// Whenever results are rendered, the landing/last-scan cards must be hidden —
|
||||
// the live scan_file_flagged path shows the grid but does not clear them, so
|
||||
// results would otherwise appear underneath the still-visible landing page
|
||||
// until a manual refresh. Centralised here so every render path is covered.
|
||||
if (files && files.length) {
|
||||
const es = document.getElementById('emptyState');
|
||||
if (es) es.style.display = 'none';
|
||||
const ls = document.getElementById('lastScanSummary');
|
||||
if (ls) ls.style.display = 'none';
|
||||
if (grid) grid.style.display = S.isListView ? 'block' : 'grid';
|
||||
}
|
||||
_updateBulkBar();
|
||||
updateDispositionStats();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user