Files
Rozpisky/Rozpisky/Data/JsonConfigFile.cs
T
marekandClaude Opus 5 3c0e30bf35 Uživatelská data do %APPDATA%\Rozpisky místo vedle .exe
Vše, co píše uživatel, míří přes novou třídu UzivatelskaData do profilu
uživatele: cad.json, layout.json, error.log, tisk_diag.log. Dosud se
zapisovalo vedle .exe, takže .exe v Program Files (složka jen pro čtení)
nebo přeinstalace novou verzí uživatele o data připravily.

Dodávané podklady (šablona, mustr, loga, mapovani.json) se dál čtou vedle
.exe – ty se naopak při každé instalaci obnovují. Hranice mezi tím, co se
instaluje, a tím, co patří uživateli, vede přesně tudy.

Portable režim zůstává: soubor portable.txt vedle .exe vrátí staré chování
(běh z USB, vývoj s jednou složkou).

Migrace: Migrace.PrenesStaraData() při startu přenese cad.json a layout.json
ze staré polohy, pokud v cíli ještě nejsou; originál označí příponou
.migrovano. Nic se nemaže a nic novějšího se nepřepíše.

Číselníky přijdou na řadu samostatně (rozpad na dodávanou a uživatelskou
vrstvu).

Build 0 chyb / 0 varování, 36 testů zelených.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 10:39:25 +02:00

64 lines
3.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.IO;
using System.Text;
using System.Windows;
namespace Rozpisky.Data;
/// <summary>
/// Společné zacházení s JSON konfiguračními soubory (<c>ciselniky.user.json</c>, <c>cad.json</c>,
/// <c>layout.json</c> v uživatelské složce, <c>mapovani.json</c> vedle .exe). Řeší dvě slabiny přímého
/// <c>File.WriteAllText</c>: pád uprostřed zápisu nesmí poškodit původní soubor (atomický zápis
/// přes .tmp) a poškozený soubor se nesmí tiše zahodit ručně editovaný config s překlepem by se
/// jinak bez varování přepsal výchozími hodnotami a uživatel by přišel o vlastní data.
/// </summary>
public static class JsonConfigFile
{
/// <summary>Zapíše text nejdřív do <c>cesta.tmp</c> a pak ho přejmenuje přes cíl. Pád uprostřed
/// zápisu tak nechá původní soubor netknutý (nedokončený .tmp se při dalším zápisu přepíše).</summary>
public static void ZapisAtomicky(string cesta, string obsah)
{
var tmp = cesta + ".tmp";
File.WriteAllText(tmp, obsah);
File.Move(tmp, cesta, overwrite: true);
}
/// <summary>Zavolat, když existující config nejde načíst (rozbité JSON, nečitelný soubor):
/// zkopíruje ho do <c>cesta.bak</c> (starší zálohu přepíše) a uživatele upozorní. Upozornění jde
/// do <c>error.log</c> hned a MessageBox se odloží přes Dispatcher configy se načítají už
/// v konstruktoru VM, kdy hlavní okno ještě nestojí. Kopíruje (nepřesouvá), aby soubor, který
/// nikdo nepřepisuje (mapovani.json), varoval při každém startu, dokud ho uživatel neopraví.</summary>
public static void ZalohujAOhlasPoskozeny(string cesta, Exception? duvod = null)
{
string? zaloha = cesta + ".bak";
try { File.Copy(cesta, zaloha, overwrite: true); }
catch { zaloha = null; } // i nečitelný originál → aspoň upozornit, bez zálohy
var jmeno = Path.GetFileName(cesta);
ZapisDoErrorLogu($"„{jmeno}“ je poškozený ({duvod?.Message ?? "neznámý důvod"}); " +
(zaloha is null ? "záloha se nepodařila." : $"záloha: {zaloha}"));
var zprava = $"Soubor {jmeno} je poškozený nebo nečitelný, načetly se výchozí hodnoty.\n\n" +
(zaloha is null
? "Zálohu původního souboru se nepodařilo vytvořit."
: $"Původní obsah je zálohovaný v:\n{zaloha}") +
$"\n\nPodrobnosti jsou v souboru error.log ve složce\n{UzivatelskaData.Slozka}";
// BeginInvoke: hláška se ukáže až po rozběhnutí UI smyčky (config se čte před oknem).
Application.Current?.Dispatcher.BeginInvoke(() =>
MessageBox.Show(zprava, "Poškozený konfigurační soubor",
MessageBoxButton.OK, MessageBoxImage.Warning));
}
/// <summary>Zápis do <c>error.log</c> stejným formátem jako globální záchytná síť v App.
/// Nesmí nic shodit (read-only složka apod.).</summary>
private static void ZapisDoErrorLogu(string text)
{
try
{
File.AppendAllText(UzivatelskaData.Cesta("error.log"),
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] Config: {text}{Environment.NewLine}{Environment.NewLine}",
Encoding.UTF8);
}
catch { /* error.log je jen stopa navíc MessageBox se ukáže i tak */ }
}
}