feat(icons): propagate icon_custom through export / import / sync
Follow-up to 18fe13a: the custom-icon flag now survives round-trips instead
of resetting to fetched on a restore.
- Containers write icon_custom: manual export (doExport), auto-backup, sync
snapshot; parseEntriesFromJSON carries it back (default 0 when absent, e.g.
foreign CSV).
- encryptImportEntry ships it in the body (shared by bulk-import + sync
create/update).
- Server create + bulk-import INSERTs read icon_custom
(GetValue<Boolean>('icon_custom', False) → 0 default). An empty icon always
forces icon_custom=0.
Now a custom icon stays custom after export→import and across sync — it won't
be silently reclassified as cache and clobbered by Re-fetch-all on the other
device.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -333,6 +333,7 @@ var
|
||||
LTotpSec, LTotpIv, LKind, LCf, LCfIv, LIcon, LUuid,
|
||||
LTemplateEnc, LTemplateIv, LCreatedAt, LUpdatedAt,
|
||||
LSiteEnc, LSiteIv, LTitleEnc, LTitleIv, LTagsEnc, LTagsIv: string;
|
||||
LIconCustom: Boolean;
|
||||
LQ: TFDQuery;
|
||||
begin
|
||||
try
|
||||
@@ -371,6 +372,8 @@ begin
|
||||
LCf := LBody.GetValue<string>('custom_fields', '');
|
||||
LCfIv := LBody.GetValue<string>('custom_fields_iv', '');
|
||||
LIcon := LBody.GetValue<string>('icon_b64', '');
|
||||
// Icon provenance (0 = fetched cache, 1 = user upload). Absent → 0.
|
||||
LIconCustom := LBody.GetValue<Boolean>('icon_custom', False);
|
||||
LTemplateEnc := LBody.GetValue<string>('template_enc', '');
|
||||
LTemplateIv := LBody.GetValue<string>('template_iv', '');
|
||||
// Caller may bring its own UUID (sync restore / import preserving
|
||||
@@ -412,10 +415,10 @@ begin
|
||||
' site_enc, site_iv, title_enc, title_iv, tags_enc, tags_iv, ' +
|
||||
' encrypted_password, iv, encryption_method, ' +
|
||||
' folder, totp_secret, totp_iv, kind, custom_fields, custom_fields_iv,' +
|
||||
' icon_b64, template_enc, template_iv, uuid, created_at, updated_at, password_changed_at) ' +
|
||||
' icon_b64, icon_custom, template_enc, template_iv, uuid, created_at, updated_at, password_changed_at) ' +
|
||||
'VALUES (:uid, :uenc, :uiv, :senc, :siv, :tenc, :tiv2, :genc, :giv, ' +
|
||||
' :e, :i, ''client'', :f, :ts, :tiv, :k, ' +
|
||||
' :cf, :cfiv, :ic, :tplenc, :tpliv, :uuid, :c, :c2, :c)';
|
||||
' :cf, :cfiv, :ic, :iconcust, :tplenc, :tpliv, :uuid, :c, :c2, :c)';
|
||||
LQ.ParamByName('uid').AsInteger := LUserId;
|
||||
// site/username/title/tags cleartext columns dropped — only *_enc written.
|
||||
BindNullable(LQ, 'uenc', LUserEnc);
|
||||
@@ -452,6 +455,8 @@ begin
|
||||
if LCfIv = '' then LQ.ParamByName('cfiv').Clear else LQ.ParamByName('cfiv').Value := LCfIv;
|
||||
LQ.ParamByName('ic').DataType := ftMemo;
|
||||
if LIcon = '' then LQ.ParamByName('ic').Clear else LQ.ParamByName('ic').Value := LIcon;
|
||||
if (LIcon <> '') and LIconCustom then LQ.ParamByName('iconcust').AsInteger := 1
|
||||
else LQ.ParamByName('iconcust').AsInteger := 0;
|
||||
BindNullable(LQ, 'tplenc', LTemplateEnc);
|
||||
BindNullable(LQ, 'tpliv', LTemplateIv);
|
||||
LQ.ParamByName('uuid').AsString := LUuid;
|
||||
@@ -1203,6 +1208,7 @@ var
|
||||
LTotpIv, LNow, LKind, LCf, LCfIv, LIcon, LTemplateEnc, LTemplateIv, LUuid,
|
||||
LCreatedAt, LUpdatedAt,
|
||||
LSiteEnc, LSiteIv, LTitleEnc, LTitleIv, LTagsEnc, LTagsIv: string;
|
||||
LIconCustom: Boolean;
|
||||
LQ, LTomb: TFDQuery;
|
||||
begin
|
||||
try
|
||||
@@ -1259,10 +1265,10 @@ begin
|
||||
' site_enc, site_iv, title_enc, title_iv, tags_enc, tags_iv, ' +
|
||||
' encrypted_password, iv, encryption_method, ' +
|
||||
' folder, totp_secret, totp_iv, kind, custom_fields, custom_fields_iv,' +
|
||||
' icon_b64, template_enc, template_iv, uuid, created_at, updated_at) ' +
|
||||
' icon_b64, icon_custom, template_enc, template_iv, uuid, created_at, updated_at) ' +
|
||||
'VALUES (:uid, :uenc, :uiv, :senc, :siv, :tenc, :tiv2, :genc, :giv, ' +
|
||||
' :e, :i, ''client'', :f, :ts, :tiv, :k, ' +
|
||||
' :cf, :cfiv, :ic, :tplenc, :tpliv, :uuid, :c, :c2)';
|
||||
' :cf, :cfiv, :ic, :iconcust, :tplenc, :tpliv, :uuid, :c, :c2)';
|
||||
// Declare optional param types ONCE — the prepared statement is
|
||||
// reused across every imported entry, and FireDAC needs the
|
||||
// type set before the first .Clear call would otherwise fail
|
||||
@@ -1310,6 +1316,7 @@ begin
|
||||
LCf := LEntry.GetValue<string>('custom_fields', '');
|
||||
LCfIv := LEntry.GetValue<string>('custom_fields_iv', '');
|
||||
LIcon := LEntry.GetValue<string>('icon_b64', '');
|
||||
LIconCustom := LEntry.GetValue<Boolean>('icon_custom', False);
|
||||
LTemplateEnc := LEntry.GetValue<string>('template_enc', '');
|
||||
LTemplateIv := LEntry.GetValue<string>('template_iv', '');
|
||||
LUuid := Trim(LEntry.GetValue<string>('uuid', ''));
|
||||
@@ -1353,6 +1360,8 @@ begin
|
||||
if LCf = '' then LQ.ParamByName('cf').Clear else LQ.ParamByName('cf').Value := LCf;
|
||||
if LCfIv = '' then LQ.ParamByName('cfiv').Clear else LQ.ParamByName('cfiv').Value := LCfIv;
|
||||
if LIcon = '' then LQ.ParamByName('ic').Clear else LQ.ParamByName('ic').Value := LIcon;
|
||||
if (LIcon <> '') and LIconCustom then LQ.ParamByName('iconcust').AsInteger := 1
|
||||
else LQ.ParamByName('iconcust').AsInteger := 0;
|
||||
BindNullable(LQ, 'tplenc', LTemplateEnc);
|
||||
BindNullable(LQ, 'tpliv', LTemplateIv);
|
||||
LQ.ParamByName('uuid').AsString := LUuid;
|
||||
|
||||
Reference in New Issue
Block a user