Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/doc/autoredactdialog.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 */
9
10
#include <autoredactdialog.hxx>
11
12
#include <sfx2/filedlghelper.hxx>
13
#include <sfx2/sfxresid.hxx>
14
#include <sfx2/strings.hrc>
15
#include <o3tl/deleter.hxx>
16
#include <osl/file.hxx>
17
#include <sal/log.hxx>
18
#include <vcl/svapp.hxx>
19
#include <vcl/vclenum.hxx>
20
#include <vcl/weld/Builder.hxx>
21
#include <vcl/weld/Dialog.hxx>
22
#include <vcl/weld/MessageDialog.hxx>
23
#include <vcl/weld/weld.hxx>
24
#include <unotools/viewoptions.hxx>
25
#include <o3tl/string_view.hxx>
26
27
#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
28
29
#include <boost/property_tree/json_parser.hpp>
30
31
constexpr OUStringLiteral FILEDIALOG_FILTER_JSON = u"*.json";
32
33
int TargetsTable::GetRowByTargetName(std::u16string_view sName)
34
0
{
35
0
    for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i)
36
0
    {
37
0
        RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xControl->get_id(i));
38
0
        if (pTarget->sName == sName)
39
0
        {
40
0
            return i;
41
0
        }
42
0
    }
43
0
    return -1;
44
0
}
45
46
bool TargetsTable::HasTargetType(RedactionTargetType aTargetType)
47
0
{
48
0
    for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i)
49
0
    {
50
0
        RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xControl->get_id(i));
51
0
        if (pTarget->sType == aTargetType)
52
0
        {
53
0
            return true;
54
0
        }
55
0
    }
56
0
    return false;
57
0
}
58
59
TargetsTable::TargetsTable(std::unique_ptr<weld::TreeView> xControl)
60
0
    : m_xControl(std::move(xControl))
61
0
{
62
0
    m_xControl->set_size_request(555, 250);
63
0
    std::vector<int> aWidths{ 100, 50, 200, 105, 105 };
64
0
    m_xControl->set_column_fixed_widths(aWidths);
65
0
    m_xControl->set_selection_mode(SelectionMode::Multiple);
66
0
}
67
68
namespace
69
{
70
OUString getTypeName(RedactionTargetType nType)
71
0
{
72
0
    OUString sTypeName(SfxResId(STR_REDACTION_TARGET_TYPE_UNKNOWN));
73
74
0
    switch (nType)
75
0
    {
76
0
        case RedactionTargetType::REDACTION_TARGET_TEXT:
77
0
            sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_TEXT);
78
0
            break;
79
0
        case RedactionTargetType::REDACTION_TARGET_REGEX:
80
0
            sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_REGEX);
81
0
            break;
82
0
        case RedactionTargetType::REDACTION_TARGET_PREDEFINED:
83
0
            sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_PREDEF);
84
0
            break;
85
0
        case RedactionTargetType::REDACTION_TARGET_IMAGE:
86
0
            sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_IMAGE);
87
0
            break;
88
0
        case RedactionTargetType::REDACTION_TARGET_UNKNOWN:
89
0
            sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_UNKNOWN);
90
0
            break;
91
0
    }
92
93
0
    return sTypeName;
94
0
}
95
96
/// Returns TypeID to be used in the add/edit target dialog
97
OUString getTypeID(RedactionTargetType nType)
98
0
{
99
0
    OUString sTypeID(u"unknown"_ustr);
100
101
0
    switch (nType)
102
0
    {
103
0
        case RedactionTargetType::REDACTION_TARGET_TEXT:
104
0
            sTypeID = "text";
105
0
            break;
106
0
        case RedactionTargetType::REDACTION_TARGET_REGEX:
107
0
            sTypeID = "regex";
108
0
            break;
109
0
        case RedactionTargetType::REDACTION_TARGET_PREDEFINED:
110
0
            sTypeID = "predefined";
111
0
            break;
112
0
        case RedactionTargetType::REDACTION_TARGET_IMAGE:
113
0
            sTypeID = "image";
114
0
            break;
115
0
        case RedactionTargetType::REDACTION_TARGET_UNKNOWN:
116
0
            sTypeID = "unknown";
117
0
            break;
118
0
    }
119
120
0
    return sTypeID;
121
0
}
122
}
123
124
void TargetsTable::InsertTarget(RedactionTarget* pTarget)
125
0
{
126
0
    if (!pTarget)
127
0
    {
128
0
        SAL_WARN("sfx.doc", "pTarget is null in TargetsTable::InsertTarget()");
129
0
        return;
130
0
    }
131
132
    // Check if the name is empty or invalid (clashing with another entry's name)
133
0
    if (pTarget->sName.isEmpty() || GetRowByTargetName(pTarget->sName) != -1)
134
0
    {
135
0
        pTarget->sName = GetNameProposal();
136
0
    }
137
138
0
    OUString sContent = pTarget->sContent;
139
140
0
    if (pTarget->sType == RedactionTargetType::REDACTION_TARGET_PREDEFINED)
141
0
    {
142
        //selection_num;selection_name
143
0
        sContent = sContent.getToken(1, ';');
144
0
    }
145
146
    // Add to the end
147
0
    int nRow = m_xControl->n_children();
148
0
    m_xControl->append(weld::toId(pTarget), pTarget->sName);
149
0
    m_xControl->set_text(nRow, getTypeName(pTarget->sType), 1);
150
0
    m_xControl->set_text(nRow, sContent, 2);
151
0
    m_xControl->set_text(
152
0
        nRow, pTarget->bCaseSensitive ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO),
153
0
        3);
154
0
    m_xControl->set_text(
155
0
        nRow, pTarget->bWholeWords ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO), 4);
156
0
}
157
158
RedactionTarget* TargetsTable::GetTargetByName(std::u16string_view sName)
159
0
{
160
0
    int nEntry = GetRowByTargetName(sName);
161
0
    if (nEntry == -1)
162
0
        return nullptr;
163
164
0
    return weld::fromId<RedactionTarget*>(m_xControl->get_id(nEntry));
165
0
}
166
167
OUString TargetsTable::GetNameProposal() const
168
0
{
169
0
    OUString sDefaultTargetName(SfxResId(STR_REDACTION_TARGET));
170
0
    sal_Int32 nHighestTargetId = 0;
171
0
    for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i)
172
0
    {
173
0
        RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xControl->get_id(i));
174
0
        const OUString& sName = pTarget->sName;
175
0
        sal_Int32 nIndex = 0;
176
0
        if (o3tl::getToken(sName, 0, ' ', nIndex) == sDefaultTargetName)
177
0
        {
178
0
            sal_Int32 nCurrTargetId = o3tl::toInt32(o3tl::getToken(sName, 0, ' ', nIndex));
179
0
            nHighestTargetId = std::max<sal_Int32>(nHighestTargetId, nCurrTargetId);
180
0
        }
181
0
    }
182
0
    return sDefaultTargetName + " " + OUString::number(nHighestTargetId + 1);
183
0
}
184
185
void TargetsTable::setRowData(int nRowIndex, const RedactionTarget* pTarget)
186
0
{
187
0
    OUString sContent = pTarget->sContent;
188
189
0
    if (pTarget->sType == RedactionTargetType::REDACTION_TARGET_PREDEFINED)
190
0
    {
191
        //selection_num;selection_name
192
0
        sContent = sContent.getToken(1, ';');
193
0
    }
194
195
0
    m_xControl->set_text(nRowIndex, pTarget->sName, 0);
196
0
    m_xControl->set_text(nRowIndex, getTypeName(pTarget->sType), 1);
197
0
    m_xControl->set_text(nRowIndex, sContent, 2);
198
0
    m_xControl->set_text(
199
0
        nRowIndex,
200
0
        pTarget->bCaseSensitive ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO), 3);
201
0
    m_xControl->set_text(
202
0
        nRowIndex, pTarget->bWholeWords ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO),
203
0
        4);
204
0
}
205
206
IMPL_LINK_NOARG(SfxAutoRedactDialog, Load, weld::Button&, void)
207
0
{
208
    //Load a targets list from a previously saved file (a json file?)
209
    // ask for filename, where we should load the new config data from
210
0
    StartFileDialog(StartFileDialogType::Open, SfxResId(STR_REDACTION_LOAD_TARGETS));
211
0
}
212
213
IMPL_LINK_NOARG(SfxAutoRedactDialog, Save, weld::Button&, void)
214
0
{
215
    //Allow saving the targets into a file
216
0
    StartFileDialog(StartFileDialogType::SaveAs, SfxResId(STR_REDACTION_SAVE_TARGETS));
217
0
}
218
219
IMPL_LINK_NOARG(SfxAutoRedactDialog, AddHdl, weld::Button&, void)
220
0
{
221
    // Open the Add Target dialog, create a new target and insert into the targets vector and the listbox
222
0
    SfxAddTargetDialog aAddTargetDialog(getDialog(), m_aTargetsBox.GetNameProposal());
223
224
0
    bool bIncomplete;
225
0
    do
226
0
    {
227
0
        bIncomplete = false;
228
229
0
        if (aAddTargetDialog.run() != RET_OK)
230
0
            return;
231
232
0
        if (aAddTargetDialog.getName().isEmpty()
233
0
            || aAddTargetDialog.getType() == RedactionTargetType::REDACTION_TARGET_UNKNOWN
234
0
            || aAddTargetDialog.getContent().isEmpty())
235
0
        {
236
0
            bIncomplete = true;
237
0
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
238
0
                getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
239
0
                SfxResId(STR_REDACTION_FIELDS_REQUIRED)));
240
0
            xBox->run();
241
0
        }
242
0
        else if (m_aTargetsBox.GetTargetByName(aAddTargetDialog.getName()))
243
0
        {
244
0
            bIncomplete = true;
245
0
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
246
0
                getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
247
0
                SfxResId(STR_REDACTION_TARGET_NAME_CLASH)));
248
0
            xBox->run();
249
0
        }
250
0
        else if (aAddTargetDialog.getType() == REDACTION_TARGET_IMAGE
251
0
                 && m_aTargetsBox.HasTargetType(REDACTION_TARGET_IMAGE))
252
0
        {
253
0
            bIncomplete = true;
254
0
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
255
0
                getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
256
0
                SfxResId(STR_REDACTION_MULTI_IMAGE_TARGETS)));
257
0
            xBox->run();
258
0
        }
259
260
0
    } while (bIncomplete);
261
262
    //Alright, we now have everything we need to construct a new target
263
0
    RedactionTarget* redactiontarget = new RedactionTarget(
264
0
        { aAddTargetDialog.getName(), aAddTargetDialog.getType(), aAddTargetDialog.getContent(),
265
0
          aAddTargetDialog.isCaseSensitive(), aAddTargetDialog.isWholeWords(), 0 });
266
267
    // Only the visual/display part
268
0
    m_aTargetsBox.InsertTarget(redactiontarget);
269
270
    // Actually add to the targets vector
271
0
    if (m_aTargetsBox.GetTargetByName(redactiontarget->sName))
272
0
        m_aTableTargets.emplace_back(redactiontarget, redactiontarget->sName);
273
0
    else
274
0
    {
275
0
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
276
0
            getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
277
0
            SfxResId(STR_REDACTION_TARGET_ADD_ERROR)));
278
0
        xBox->run();
279
0
        delete redactiontarget;
280
0
    }
281
0
}
282
283
IMPL_LINK_NOARG(SfxAutoRedactDialog, EditHdl, weld::Button&, void)
284
0
{
285
0
    sal_Int32 nSelectedRow = m_aTargetsBox.get_selected_index();
286
287
    // No selection, nothing to edit
288
0
    if (nSelectedRow < 0)
289
0
        return;
290
291
    // Only one entry should be selected for editing
292
0
    if (m_aTargetsBox.get_selected_rows().size() > 1)
293
0
    {
294
        //Warn the user about multiple selections
295
0
        std::unique_ptr<weld::MessageDialog> xBox(
296
0
            Application::CreateMessageDialog(getDialog(), VclMessageType::Error, VclButtonsType::Ok,
297
0
                                             SfxResId(STR_REDACTION_MULTI_EDIT)));
298
0
        xBox->run();
299
0
        return;
300
0
    }
301
302
    // Get the redaction target to be edited
303
0
    RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_aTargetsBox.get_id(nSelectedRow));
304
305
    // Construct and run the edit target dialog
306
0
    SfxAddTargetDialog aEditTargetDialog(getDialog(), pTarget->sName, pTarget->sType,
307
0
                                         pTarget->sContent, pTarget->bCaseSensitive,
308
0
                                         pTarget->bWholeWords);
309
310
0
    bool bIncomplete;
311
0
    do
312
0
    {
313
0
        bIncomplete = false;
314
315
0
        if (aEditTargetDialog.run() != RET_OK)
316
0
            return;
317
318
0
        if (aEditTargetDialog.getName().isEmpty()
319
0
            || aEditTargetDialog.getType() == RedactionTargetType::REDACTION_TARGET_UNKNOWN
320
0
            || aEditTargetDialog.getContent().isEmpty())
321
0
        {
322
0
            bIncomplete = true;
323
0
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
324
0
                getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
325
0
                SfxResId(STR_REDACTION_FIELDS_REQUIRED)));
326
0
            xBox->run();
327
0
        }
328
0
        else if (aEditTargetDialog.getName() != pTarget->sName
329
0
                 && m_aTargetsBox.GetTargetByName(aEditTargetDialog.getName()))
330
0
        {
331
0
            bIncomplete = true;
332
0
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
333
0
                getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
334
0
                SfxResId(STR_REDACTION_TARGET_NAME_CLASH)));
335
0
            xBox->run();
336
0
        }
337
0
        else if (pTarget->sType != REDACTION_TARGET_IMAGE
338
0
                 && aEditTargetDialog.getType() == REDACTION_TARGET_IMAGE
339
0
                 && m_aTargetsBox.HasTargetType(REDACTION_TARGET_IMAGE))
340
0
        {
341
0
            bIncomplete = true;
342
0
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
343
0
                getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
344
0
                SfxResId(STR_REDACTION_MULTI_IMAGE_TARGETS)));
345
0
            xBox->run();
346
0
        }
347
348
0
    } while (bIncomplete);
349
350
    // Update the redaction target
351
0
    pTarget->sName = aEditTargetDialog.getName();
352
0
    pTarget->sType = aEditTargetDialog.getType();
353
0
    pTarget->sContent = aEditTargetDialog.getContent();
354
0
    if (pTarget->sType == REDACTION_TARGET_IMAGE)
355
0
    {
356
0
        pTarget->bCaseSensitive = false;
357
0
        pTarget->bWholeWords = false;
358
0
    }
359
0
    else
360
0
    {
361
0
        pTarget->bCaseSensitive = aEditTargetDialog.isCaseSensitive();
362
0
        pTarget->bWholeWords = aEditTargetDialog.isWholeWords();
363
0
    }
364
365
    // And sync the targets box row with the actual target data
366
0
    m_aTargetsBox.setRowData(nSelectedRow, pTarget);
367
0
}
368
IMPL_LINK_NOARG(SfxAutoRedactDialog, DoubleClickEditHdl, const weld::TreeIter&, bool)
369
0
{
370
0
    if (m_xEditBtn->get_sensitive())
371
0
        m_xEditBtn->clicked();
372
0
    return true;
373
0
}
374
IMPL_LINK_NOARG(SfxAutoRedactDialog, DeleteHdl, weld::Button&, void)
375
0
{
376
0
    std::vector<int> aSelectedRows = m_aTargetsBox.get_selected_rows();
377
378
    //No selection, so nothing to delete
379
0
    if (aSelectedRows.empty())
380
0
        return;
381
382
0
    if (aSelectedRows.size() > 1)
383
0
    {
384
0
        OUString sMsg(SfxResId(STR_REDACTION_MULTI_DELETE)
385
0
                          .replaceFirst("$(TARGETSCOUNT)", OUString::number(aSelectedRows.size())));
386
        //Warn the user about multiple deletions
387
0
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
388
0
            getDialog(), VclMessageType::Question, VclButtonsType::OkCancel, sMsg));
389
0
        if (xBox->run() == RET_CANCEL)
390
0
            return;
391
0
    }
392
393
    // After each delete, the indexes of the following items decrease by one.
394
0
    int delta = 0;
395
0
    for (const auto& i : aSelectedRows)
396
0
    {
397
0
        m_aTableTargets.erase(m_aTableTargets.begin() + (i - delta));
398
0
        m_aTargetsBox.remove(i - delta++);
399
0
    }
400
0
}
401
402
namespace
403
{
404
boost::property_tree::ptree redactionTargetToJSON(const RedactionTarget* pTarget)
405
0
{
406
0
    boost::property_tree::ptree aNode;
407
0
    aNode.put("sName", pTarget->sName.toUtf8().getStr());
408
0
    aNode.put("eType", pTarget->sType);
409
0
    aNode.put("sContent", pTarget->sContent.toUtf8().getStr());
410
0
    aNode.put("bWholeWords", pTarget->bWholeWords);
411
0
    aNode.put("bCaseSensitive", pTarget->bCaseSensitive);
412
0
    aNode.put("nID", pTarget->nID);
413
414
0
    return aNode;
415
0
}
416
417
std::unique_ptr<RedactionTarget>
418
JSONtoRedactionTarget(const boost::property_tree::ptree::value_type& rValue)
419
0
{
420
0
    OUString sName = OUString::fromUtf8(rValue.second.get<std::string>("sName"));
421
0
    RedactionTargetType eType
422
0
        = static_cast<RedactionTargetType>(atoi(rValue.second.get<std::string>("eType").c_str()));
423
0
    OUString sContent = OUString::fromUtf8(rValue.second.get<std::string>("sContent"));
424
0
    bool bCaseSensitive
425
0
        = OUString::fromUtf8(rValue.second.get<std::string>("bCaseSensitive")).toBoolean();
426
0
    bool bWholeWords
427
0
        = OUString::fromUtf8(rValue.second.get<std::string>("bWholeWords")).toBoolean();
428
0
    sal_uInt32 nID = atoi(rValue.second.get<std::string>("nID").c_str());
429
430
0
    return std::unique_ptr<RedactionTarget>(
431
0
        new RedactionTarget{ sName, eType, sContent, bCaseSensitive, bWholeWords, nID });
432
0
}
433
}
434
435
IMPL_LINK_NOARG(SfxAutoRedactDialog, LoadHdl, sfx2::FileDialogHelper*, void)
436
0
{
437
0
    assert(m_pFileDlg);
438
439
0
    OUString sTargetsFile;
440
0
    if (ERRCODE_NONE == m_pFileDlg->GetError())
441
0
        sTargetsFile = m_pFileDlg->GetPath();
442
443
0
    if (sTargetsFile.isEmpty())
444
0
        return;
445
446
0
    OUString sSysPath;
447
0
    osl::File::getSystemPathFromFileURL(sTargetsFile, sSysPath);
448
0
    sTargetsFile = sSysPath;
449
450
0
    weld::WaitObject aWaitObject(getDialog());
451
452
0
    try
453
0
    {
454
        // Create path string, and read JSON from file
455
0
        std::string sPathStr(OUStringToOString(sTargetsFile, RTL_TEXTENCODING_UTF8));
456
457
0
        boost::property_tree::ptree aTargetsJSON;
458
459
0
        boost::property_tree::read_json(sPathStr, aTargetsJSON);
460
461
        // Clear the dialog
462
0
        clearTargets();
463
464
        // Recreate & add the targets to the dialog
465
0
        for (const boost::property_tree::ptree::value_type& rValue :
466
0
             aTargetsJSON.get_child("RedactionTargets"))
467
0
        {
468
0
            addTarget(JSONtoRedactionTarget(rValue));
469
0
        }
470
0
    }
471
0
    catch (css::uno::Exception& e)
472
0
    {
473
0
        SAL_WARN("sfx.doc",
474
0
                 "Exception caught while trying to load the targets JSON from file: " << e.Message);
475
0
        return;
476
        //TODO: Warn the user with a message box
477
0
    }
478
0
}
479
480
IMPL_LINK_NOARG(SfxAutoRedactDialog, SaveHdl, sfx2::FileDialogHelper*, void)
481
0
{
482
0
    assert(m_pFileDlg);
483
484
0
    OUString sTargetsFile;
485
0
    if (ERRCODE_NONE == m_pFileDlg->GetError())
486
0
        sTargetsFile = m_pFileDlg->GetPath();
487
488
0
    if (sTargetsFile.isEmpty())
489
0
        return;
490
491
0
    OUString sSysPath;
492
0
    osl::File::getSystemPathFromFileURL(sTargetsFile, sSysPath);
493
0
    sTargetsFile = sSysPath;
494
495
0
    weld::WaitObject aWaitObject(getDialog());
496
497
0
    try
498
0
    {
499
        // Put the targets into a JSON array
500
0
        boost::property_tree::ptree aTargetsArray;
501
0
        for (const auto& targetPair : m_aTableTargets)
502
0
        {
503
0
            aTargetsArray.push_back(
504
0
                std::make_pair("", redactionTargetToJSON(targetPair.first.get())));
505
0
        }
506
507
        // Build the JSON tree
508
0
        boost::property_tree::ptree aTargetsTree;
509
0
        aTargetsTree.add_child("RedactionTargets", aTargetsArray);
510
511
        // Create path string, and write JSON to file
512
0
        std::string sPathStr(OUStringToOString(sTargetsFile, RTL_TEXTENCODING_UTF8));
513
514
0
        boost::property_tree::write_json(sPathStr, aTargetsTree);
515
0
    }
516
0
    catch (css::uno::Exception& e)
517
0
    {
518
0
        SAL_WARN("sfx.doc",
519
0
                 "Exception caught while trying to save the targets JSON to file: " << e.Message);
520
0
        return;
521
        //TODO: Warn the user with a message box
522
0
    }
523
0
}
524
525
void SfxAutoRedactDialog::StartFileDialog(StartFileDialogType nType, const OUString& rTitle)
526
0
{
527
0
    OUString aFilterAllStr(SfxResId(STR_SFX_FILTERNAME_ALL));
528
0
    OUString aFilterJsonStr(SfxResId(STR_REDACTION_JSON_FILE_FILTER));
529
530
0
    bool bSave = nType == StartFileDialogType::SaveAs;
531
0
    short nDialogType = bSave ? css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION
532
0
                              : css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE;
533
0
    m_pFileDlg.reset(new sfx2::FileDialogHelper(nDialogType, FileDialogFlags::NONE, getDialog()));
534
535
0
    m_pFileDlg->SetTitle(rTitle);
536
0
    m_pFileDlg->AddFilter(aFilterAllStr, FILEDIALOG_FILTER_ALL);
537
0
    m_pFileDlg->AddFilter(aFilterJsonStr, FILEDIALOG_FILTER_JSON);
538
0
    m_pFileDlg->SetCurrentFilter(aFilterJsonStr);
539
540
0
    Link<sfx2::FileDialogHelper*, void> aDlgClosedLink
541
0
        = bSave ? LINK(this, SfxAutoRedactDialog, SaveHdl)
542
0
                : LINK(this, SfxAutoRedactDialog, LoadHdl);
543
0
    m_pFileDlg->SetContext(sfx2::FileDialogHelper::AutoRedact);
544
0
    m_pFileDlg->StartExecuteModal(aDlgClosedLink);
545
0
}
546
547
void SfxAutoRedactDialog::addTarget(std::unique_ptr<RedactionTarget> pTarget)
548
0
{
549
    // Only the visual/display part
550
0
    m_aTargetsBox.InsertTarget(pTarget.get());
551
552
    // Actually add to the targets vector
553
0
    auto name = pTarget->sName;
554
0
    if (m_aTargetsBox.GetTargetByName(name))
555
0
        m_aTableTargets.emplace_back(std::move(pTarget), name);
556
0
    else
557
0
    {
558
0
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
559
0
            getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
560
0
            SfxResId(STR_REDACTION_TARGET_ADD_ERROR)));
561
0
        xBox->run();
562
0
    }
563
0
}
564
565
void SfxAutoRedactDialog::clearTargets()
566
0
{
567
    // Clear the targets box
568
0
    m_aTargetsBox.clear();
569
570
    // Clear the targets vector
571
0
    m_aTableTargets.clear();
572
0
}
573
574
SfxAutoRedactDialog::SfxAutoRedactDialog(weld::Window* pParent)
575
0
    : SfxDialogController(pParent, u"sfx/ui/autoredactdialog.ui"_ustr, u"AutoRedactDialog"_ustr)
576
0
    , m_bIsValidState(true)
577
0
    , m_bTargetsCopied(false)
578
0
    , m_aTargetsBox(m_xBuilder->weld_tree_view(u"targets"_ustr))
579
0
    , m_xLoadBtn(m_xBuilder->weld_button(u"btnLoadTargets"_ustr))
580
0
    , m_xSaveBtn(m_xBuilder->weld_button(u"btnSaveTargets"_ustr))
581
0
    , m_xAddBtn(m_xBuilder->weld_button(u"add"_ustr))
582
0
    , m_xEditBtn(m_xBuilder->weld_button(u"edit"_ustr))
583
0
    , m_xDeleteBtn(m_xBuilder->weld_button(u"delete"_ustr))
584
0
{
585
    // Can be used to remember the last set of redaction targets?
586
0
    OUString sExtraData;
587
0
    SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id());
588
589
0
    if (aDlgOpt.Exists())
590
0
    {
591
0
        css::uno::Any aUserItem = aDlgOpt.GetUserItem(u"UserItem"_ustr);
592
0
        aUserItem >>= sExtraData;
593
0
    }
594
595
    // update the targets configuration if necessary
596
0
    if (!sExtraData.isEmpty())
597
0
    {
598
0
        weld::WaitObject aWaitCursor(m_xDialog.get());
599
600
0
        try
601
0
        {
602
            // Create path string, and read JSON from file
603
0
            boost::property_tree::ptree aTargetsJSON;
604
0
            std::stringstream aStream(std::string(sExtraData.toUtf8()));
605
606
0
            boost::property_tree::read_json(aStream, aTargetsJSON);
607
608
            // Recreate & add the targets to the dialog
609
0
            for (const boost::property_tree::ptree::value_type& rValue :
610
0
                 aTargetsJSON.get_child("RedactionTargets"))
611
0
            {
612
0
                addTarget(JSONtoRedactionTarget(rValue));
613
0
            }
614
0
        }
615
0
        catch (css::uno::Exception& e)
616
0
        {
617
0
            SAL_WARN("sfx.doc",
618
0
                     "Exception caught while trying to load the last dialog state: " << e.Message);
619
0
            return;
620
            //TODO: Warn the user with a message box
621
0
        }
622
0
    }
623
624
    // Handler connections
625
0
    m_xLoadBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, Load));
626
0
    m_xSaveBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, Save));
627
0
    m_xAddBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, AddHdl));
628
0
    m_xEditBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, EditHdl));
629
0
    m_xDeleteBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, DeleteHdl));
630
0
    m_aTargetsBox.connect_item_activated(LINK(this, SfxAutoRedactDialog, DoubleClickEditHdl));
631
0
}
632
633
void SfxAutoRedactDialog::ImplDestroy()
634
0
{
635
0
    if (m_aTableTargets.empty())
636
0
    {
637
        // Clear the dialog data
638
0
        SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id());
639
0
        aDlgOpt.Delete();
640
0
        return;
641
0
    }
642
643
0
    try
644
0
    {
645
        // Put the targets into a JSON array
646
0
        boost::property_tree::ptree aTargetsArray;
647
0
        for (const auto& targetPair : m_aTableTargets)
648
0
        {
649
0
            aTargetsArray.push_back(
650
0
                std::make_pair("", redactionTargetToJSON(targetPair.first.get())));
651
0
        }
652
653
        // Build the JSON tree
654
0
        boost::property_tree::ptree aTargetsTree;
655
0
        aTargetsTree.add_child("RedactionTargets", aTargetsArray);
656
0
        std::stringstream aStream;
657
658
0
        boost::property_tree::write_json(aStream, aTargetsTree, false);
659
660
0
        OUString sUserDataStr(OUString::fromUtf8(aStream.str()));
661
662
        // Store the dialog data
663
0
        SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id());
664
0
        aDlgOpt.SetUserItem(u"UserItem"_ustr, css::uno::Any(sUserDataStr));
665
666
0
        if (!m_bTargetsCopied)
667
0
            clearTargets();
668
0
    }
669
0
    catch (css::uno::Exception& e)
670
0
    {
671
0
        SAL_WARN("sfx.doc",
672
0
                 "Exception caught while trying to store the dialog state: " << e.Message);
673
0
        return;
674
        //TODO: Warn the user with a message box
675
0
    }
676
0
}
677
678
0
SfxAutoRedactDialog::~SfxAutoRedactDialog() { suppress_fun_call_w_exception(ImplDestroy()); }
679
680
bool SfxAutoRedactDialog::hasTargets() const
681
0
{
682
    //TODO: Add also some validity checks?
683
0
    if (m_aTableTargets.empty())
684
0
        return false;
685
686
0
    return true;
687
0
}
688
689
bool SfxAutoRedactDialog::getTargets(std::vector<std::pair<RedactionTarget, OUString>>& r_aTargets)
690
0
{
691
0
    if (m_aTableTargets.empty())
692
0
        return true;
693
694
0
    for (auto const& rPair : m_aTableTargets)
695
0
        r_aTargets.push_back({ *rPair.first, rPair.second });
696
0
    m_bTargetsCopied = true;
697
0
    return true;
698
0
}
699
700
IMPL_LINK_NOARG(SfxAddTargetDialog, SelectTypeHdl, weld::ComboBox&, void)
701
0
{
702
0
    if (m_xType->get_active_id() == "predefined")
703
0
    {
704
        // Hide the usual content widgets
705
        // We will just set the id as content
706
        // And handle with proper regex in the SfxRedactionHelper
707
0
        m_xLabelContent->set_sensitive(false);
708
0
        m_xLabelContent->set_visible(false);
709
0
        m_xContent->set_sensitive(false);
710
0
        m_xContent->set_visible(false);
711
0
        m_xWholeWords->set_sensitive(false);
712
0
        m_xWholeWords->set_visible(false);
713
0
        m_xCaseSensitive->set_sensitive(false);
714
0
        m_xCaseSensitive->set_visible(false);
715
716
        // And show the predefined targets
717
0
        m_xLabelPredefContent->set_sensitive(true);
718
0
        m_xLabelPredefContent->set_visible(true);
719
0
        m_xPredefContent->set_sensitive(true);
720
0
        m_xPredefContent->set_visible(true);
721
0
    }
722
0
    else if (m_xType->get_active_id() == "image")
723
0
    {
724
0
        m_xLabelContent->set_sensitive(false);
725
0
        m_xLabelContent->set_visible(false);
726
0
        m_xContent->set_sensitive(false);
727
0
        m_xContent->set_visible(false);
728
0
        m_xWholeWords->set_sensitive(false);
729
0
        m_xWholeWords->set_visible(false);
730
0
        m_xCaseSensitive->set_sensitive(false);
731
0
        m_xCaseSensitive->set_visible(false);
732
733
0
        m_xLabelPredefContent->set_sensitive(false);
734
0
        m_xLabelPredefContent->set_visible(false);
735
0
        m_xPredefContent->set_sensitive(false);
736
0
        m_xPredefContent->set_visible(false);
737
0
    }
738
0
    else
739
0
    {
740
0
        m_xLabelPredefContent->set_sensitive(false);
741
0
        m_xLabelPredefContent->set_visible(false);
742
0
        m_xPredefContent->set_sensitive(false);
743
0
        m_xPredefContent->set_visible(false);
744
745
0
        m_xLabelContent->set_sensitive(true);
746
0
        m_xLabelContent->set_visible(true);
747
0
        m_xContent->set_sensitive(true);
748
0
        m_xContent->set_visible(true);
749
0
        m_xWholeWords->set_sensitive(true);
750
0
        m_xWholeWords->set_visible(true);
751
0
        m_xCaseSensitive->set_sensitive(true);
752
0
        m_xCaseSensitive->set_visible(true);
753
0
    }
754
0
}
755
756
SfxAddTargetDialog::SfxAddTargetDialog(weld::Window* pParent, const OUString& rName)
757
0
    : GenericDialogController(pParent, u"sfx/ui/addtargetdialog.ui"_ustr, u"AddTargetDialog"_ustr)
758
0
    , m_xName(m_xBuilder->weld_entry(u"name"_ustr))
759
0
    , m_xType(m_xBuilder->weld_combo_box(u"type"_ustr))
760
0
    , m_xLabelContent(m_xBuilder->weld_label(u"label_content"_ustr))
761
0
    , m_xContent(m_xBuilder->weld_entry(u"content"_ustr))
762
0
    , m_xLabelPredefContent(m_xBuilder->weld_label(u"label_content_predef"_ustr))
763
0
    , m_xPredefContent(m_xBuilder->weld_combo_box(u"content_predef"_ustr))
764
0
    , m_xCaseSensitive(m_xBuilder->weld_check_button(u"checkboxCaseSensitive"_ustr))
765
0
    , m_xWholeWords(m_xBuilder->weld_check_button(u"checkboxWholeWords"_ustr))
766
0
{
767
0
    m_xName->set_text(rName);
768
0
    m_xName->select_region(0, rName.getLength());
769
770
0
    m_xType->connect_changed(LINK(this, SfxAddTargetDialog, SelectTypeHdl));
771
0
}
772
773
SfxAddTargetDialog::SfxAddTargetDialog(weld::Window* pParent, const OUString& sName,
774
                                       const RedactionTargetType& eTargetType,
775
                                       const OUString& sContent, bool bCaseSensitive,
776
                                       bool bWholeWords)
777
0
    : GenericDialogController(pParent, u"sfx/ui/addtargetdialog.ui"_ustr, u"AddTargetDialog"_ustr)
778
0
    , m_xName(m_xBuilder->weld_entry(u"name"_ustr))
779
0
    , m_xType(m_xBuilder->weld_combo_box(u"type"_ustr))
780
0
    , m_xLabelContent(m_xBuilder->weld_label(u"label_content"_ustr))
781
0
    , m_xContent(m_xBuilder->weld_entry(u"content"_ustr))
782
0
    , m_xLabelPredefContent(m_xBuilder->weld_label(u"label_content_predef"_ustr))
783
0
    , m_xPredefContent(m_xBuilder->weld_combo_box(u"content_predef"_ustr))
784
0
    , m_xCaseSensitive(m_xBuilder->weld_check_button(u"checkboxCaseSensitive"_ustr))
785
0
    , m_xWholeWords(m_xBuilder->weld_check_button(u"checkboxWholeWords"_ustr))
786
0
{
787
0
    m_xName->set_text(sName);
788
0
    m_xName->select_region(0, sName.getLength());
789
790
0
    m_xType->set_active_id(getTypeID(eTargetType));
791
0
    m_xType->connect_changed(LINK(this, SfxAddTargetDialog, SelectTypeHdl));
792
793
0
    if (eTargetType == RedactionTargetType::REDACTION_TARGET_PREDEFINED)
794
0
    {
795
0
        SelectTypeHdl(*m_xPredefContent);
796
0
        m_xPredefContent->set_active(o3tl::toInt32(o3tl::getToken(sContent, 0, ';')));
797
0
    }
798
0
    else if (eTargetType == RedactionTargetType::REDACTION_TARGET_IMAGE)
799
0
    {
800
0
        m_xContent->set_visible(false);
801
0
        m_xLabelContent->set_visible(false);
802
0
        m_xCaseSensitive->set_visible(false);
803
0
        m_xWholeWords->set_visible(false);
804
0
    }
805
0
    else
806
0
    {
807
0
        m_xContent->set_text(sContent);
808
0
    }
809
810
0
    m_xCaseSensitive->set_active(bCaseSensitive);
811
0
    m_xWholeWords->set_active(bWholeWords);
812
813
0
    set_title(SfxResId(STR_REDACTION_EDIT_TARGET));
814
0
}
815
816
RedactionTargetType SfxAddTargetDialog::getType() const
817
0
{
818
0
    OUString sTypeID = m_xType->get_active_id();
819
820
0
    if (sTypeID == "text")
821
0
        return RedactionTargetType::REDACTION_TARGET_TEXT;
822
0
    else if (sTypeID == "regex")
823
0
        return RedactionTargetType::REDACTION_TARGET_REGEX;
824
0
    else if (sTypeID == "predefined")
825
0
        return RedactionTargetType::REDACTION_TARGET_PREDEFINED;
826
0
    else if (sTypeID == "image")
827
0
        return RedactionTargetType::REDACTION_TARGET_IMAGE;
828
0
    else
829
0
        return RedactionTargetType::REDACTION_TARGET_UNKNOWN;
830
0
}
831
832
OUString SfxAddTargetDialog::getContent() const
833
0
{
834
0
    if (m_xType->get_active_id() == "predefined")
835
0
    {
836
0
        return OUString(OUString::number(m_xPredefContent->get_active()) + ";"
837
0
                        + m_xPredefContent->get_active_text());
838
0
    }
839
0
    else if (m_xType->get_active_id() == "image")
840
0
        return "All Images";
841
842
0
    return m_xContent->get_text();
843
0
}
844
845
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */