cui/inc/strings.hrc                                        |    1 
 cui/source/options/optaccessibility.cxx                    |    9 +
 include/sfx2/AccessibilityIssue.hxx                        |    1 
 officecfg/registry/schema/org/openoffice/Office/Common.xcs |   10 +
 sw/inc/AccessibilityCheckStrings.hrc                       |    1 
 sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx   |   15 ++
 sw/qa/core/accessibilitycheck/data/NestedLinks.odt         |binary
 sw/source/core/access/AccessibilityCheck.cxx               |   89 +++++++++++--
 sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx          |    1 
 9 files changed, 115 insertions(+), 12 deletions(-)

New commits:
commit 2cc3a9f4c8c2359b525261ec917bb68fc5e8b6c4
Author:     Balazs Varga <balazs.varga.ext...@allotropia.de>
AuthorDate: Thu Mar 13 13:49:02 2025 +0100
Commit:     Balazs Varga <balazs.varga.ext...@allotropia.de>
CommitDate: Mon Mar 17 08:36:42 2025 +0100

    tdf#164797 - sw a11y: add new check of links and references in Header/Footer
    
    to warn users about PAC "Link annotation is not nested inside a Link
    structure element." errors, so they can avoid them.
    
    Change-Id: I8d1fc7042e2ee34a5777c3a448d2379038ea24e9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182868
    Tested-by: Jenkins
    Reviewed-by: Balazs Varga <balazs.varga.ext...@allotropia.de>

diff --git a/cui/inc/strings.hrc b/cui/inc/strings.hrc
index a6d08758a626..5a42bd184054 100644
--- a/cui/inc/strings.hrc
+++ b/cui/inc/strings.hrc
@@ -553,6 +553,7 @@
 #define STR_HYPERLINK_TEXT_IS_LINK                  
NC_("STR_HYPERLINK_TEXT_IS_LINK", "Check if the hyperlink text is the same as 
the link address.")
 #define STR_HYPERLINK_TEXT_IS_SHORT                 
NC_("STR_HYPERLINK_TEXT_IS_SHORT", "Check if the hyperlink text is too short.")
 #define STR_HYPERLINK_NO_NAME                       
NC_("STR_HYPERLINK_NO_NAME", "Check if the hyperlink has an alternative name 
set.")
+#define STR_LINK_TEXT_IS_NOT_NESTED                 
NC_("STR_LINK_TEXT_IS_NOT_NESTED", "Check if links and reference fields are 
used in the header or footer.")
 #define STR_TEXT_CONTRAST                           NC_("STR_TEXT_CONTRAST", 
"Check if text contrast is high enough.")
 #define STR_TEXT_BLINKING                           NC_("STR_TEXT_BLINKING", 
"Check if the document contains blinking text.")
 #define STR_AVOID_FOOTNOTES                         NC_("STR_AVOID_FOOTNOTES", 
"Check if the document contains footnotes.")
diff --git a/cui/source/options/optaccessibility.cxx 
b/cui/source/options/optaccessibility.cxx
index 27971df71360..79bbccde8e6c 100644
--- a/cui/source/options/optaccessibility.cxx
+++ b/cui/source/options/optaccessibility.cxx
@@ -47,6 +47,7 @@ namespace
         { u"HyperlinkText"_ustr, { 
sfx::AccessibilityIssueID::HYPERLINK_IS_TEXT, STR_HYPERLINK_TEXT_IS_LINK } },
         { u"HyperlinkShort"_ustr, { 
sfx::AccessibilityIssueID::HYPERLINK_SHORT, STR_HYPERLINK_TEXT_IS_SHORT } },
         { u"HyperlinkNoName"_ustr, { 
sfx::AccessibilityIssueID::HYPERLINK_NO_NAME, STR_HYPERLINK_NO_NAME } },
+        { u"LinkInHeaderOrFooter"_ustr, { 
sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER, STR_LINK_TEXT_IS_NOT_NESTED } 
},
         { u"FakeFootnotes"_ustr, { sfx::AccessibilityIssueID::FAKE_FOOTNOTE, 
STR_AVOID_FAKE_FOOTNOTES } },
         { u"FakeCaptions"_ustr, { sfx::AccessibilityIssueID::FAKE_CAPTION, 
STR_AVOID_FAKE_CAPTIONS } },
         { u"ManualNumbering"_ustr, { 
sfx::AccessibilityIssueID::MANUAL_NUMBERING, STR_FAKE_NUMBERING } },
@@ -241,6 +242,10 @@ bool SvxAccessibilityOptionsTabPage::FillItemSet( 
SfxItemSet* )
                     
officecfg::Office::Common::AccessibilityIssues::HyperlinkNoName::set(bChecked, 
batch);
                     break;
 
+                case sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER:
+                    
officecfg::Office::Common::AccessibilityIssues::LinkInHeaderOrFooter::set(bChecked,
 batch);
+                    break;
+
                 case sfx::AccessibilityIssueID::FAKE_FOOTNOTE:
                     
officecfg::Office::Common::AccessibilityIssues::FakeFootnotes::set(bChecked, 
batch);
                     break;
@@ -449,6 +454,10 @@ void SvxAccessibilityOptionsTabPage::Reset( const 
SfxItemSet* )
                 bChecked = 
officecfg::Office::Common::AccessibilityIssues::HyperlinkNoName::get();
                 break;
 
+            case sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER:
+                bChecked = 
officecfg::Office::Common::AccessibilityIssues::LinkInHeaderOrFooter::get();
+                break;
+
             case sfx::AccessibilityIssueID::FAKE_FOOTNOTE:
                 bChecked = 
officecfg::Office::Common::AccessibilityIssues::FakeFootnotes::get();
                 break;
diff --git a/include/sfx2/AccessibilityIssue.hxx 
b/include/sfx2/AccessibilityIssue.hxx
index 65174e0fdfdb..18bfadf288be 100644
--- a/include/sfx2/AccessibilityIssue.hxx
+++ b/include/sfx2/AccessibilityIssue.hxx
@@ -40,6 +40,7 @@ enum class AccessibilityIssueID
     HYPERLINK_IS_TEXT,
     HYPERLINK_SHORT,
     HYPERLINK_NO_NAME,
+    LINK_IN_HEADER_FOOTER,
     FAKE_FOOTNOTE,
     FAKE_CAPTION,
     MANUAL_NUMBERING,
diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs 
b/officecfg/registry/schema/org/openoffice/Office/Common.xcs
index 5358b7e3c20f..a7fedae390a2 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs
@@ -6411,6 +6411,16 @@
          </info>
          <value>true</value>
        </prop>
+      <prop oor:name="LinkInHeaderOrFooter" oor:type="xs:boolean" 
oor:nillable="false">
+        <info>
+          <desc>
+            Enables accessibility check of links and reference fields are used 
in the
+            header or footer.
+          </desc>
+          <label>Enable/Disable not nested link structure elements 
accessibility check.</label>
+        </info>
+        <value>true</value>
+      </prop>
        <prop oor:name="FakeFootnotes" oor:type="xs:boolean" 
oor:nillable="false">
         <info>
           <desc>
diff --git a/sw/inc/AccessibilityCheckStrings.hrc 
b/sw/inc/AccessibilityCheckStrings.hrc
index 6acfa0761157..6c8304f1773b 100644
--- a/sw/inc/AccessibilityCheckStrings.hrc
+++ b/sw/inc/AccessibilityCheckStrings.hrc
@@ -20,6 +20,7 @@
 #define STR_HYPERLINK_TEXT_IS_LINK      NC_("STR_HYPERLINK_TEXT_IS_LINK", 
"Hyperlink text is the same as the link address “%LINK%”.")
 #define STR_HYPERLINK_TEXT_IS_SHORT     NC_("STR_HYPERLINK_TEXT_IS_SHORT", 
"Hyperlink text is too short.")
 #define STR_HYPERLINK_NO_NAME           NC_("STR_HYPERLINK_NO_NAME", "Missing 
'Name' property of hyperlink.")
+#define STR_LINK_TEXT_IS_NOT_NESTED     NC_("STR_LINK_TEXT_IS_NOT_NESTED", 
"Avoid using links and reference fields in the header or footer.")
 #define STR_TEXT_CONTRAST               NC_("STR_TEXT_CONTRAST", "Text 
contrast is too low.")
 #define STR_TEXT_BLINKING               NC_("STR_TEXT_BLINKING", "Blinking 
text.")
 #define STR_AVOID_FOOTNOTES             NC_("STR_AVOID_FOOTNOTES", "Avoid 
footnotes.")
diff --git a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx 
b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx
index 981cf03cd26b..ff381355fe73 100644
--- a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx
+++ b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx
@@ -155,6 +155,21 @@ CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, 
testHyperlinks)
     CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::DIRECT_FORMATTING, 
aIssues[4]->m_eIssueID);
 }
 
+CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testNestedlinks)
+{
+    createSwDoc("NestedLinks.odt");
+    SwDoc* pDoc = getSwDoc();
+    sw::AccessibilityCheck aCheck(pDoc);
+    aCheck.check();
+    auto& aIssues = aCheck.getIssueCollection().getIssues();
+    CPPUNIT_ASSERT_EQUAL(size_t(3), aIssues.size());
+    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER, 
aIssues[0]->m_eIssueID);
+    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueLevel::WARNLEV, 
aIssues[0]->m_eIssueLvl);
+    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER, 
aIssues[1]->m_eIssueID);
+    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueLevel::WARNLEV, 
aIssues[1]->m_eIssueLvl);
+    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER, 
aIssues[2]->m_eIssueID);
+}
+
 CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testCheckHighlightedText)
 {
     createSwDoc("HighlightTest.odt");
diff --git a/sw/qa/core/accessibilitycheck/data/NestedLinks.odt 
b/sw/qa/core/accessibilitycheck/data/NestedLinks.odt
new file mode 100644
index 000000000000..45e251eeb9da
Binary files /dev/null and b/sw/qa/core/accessibilitycheck/data/NestedLinks.odt 
differ
diff --git a/sw/source/core/access/AccessibilityCheck.cxx 
b/sw/source/core/access/AccessibilityCheck.cxx
index 3985683f65ff..a5a940865d1d 100644
--- a/sw/source/core/access/AccessibilityCheck.cxx
+++ b/sw/source/core/access/AccessibilityCheck.cxx
@@ -37,6 +37,7 @@
 #include <svx/xfillit0.hxx>
 #include <svx/xflclit.hxx>
 #include <ftnidx.hxx>
+#include <authfld.hxx>
 #include <txtftn.hxx>
 #include <txtfrm.hxx>
 #include <svl/itemiter.hxx>
@@ -190,6 +191,12 @@ void 
lcl_SetHiddenIssues(std::shared_ptr<sw::AccessibilityIssue>& pIssue)
                 pIssue->setHidden(true);
         }
         break;
+        case sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER:
+        {
+            if 
(!officecfg::Office::Common::AccessibilityIssues::LinkInHeaderOrFooter::get())
+                pIssue->setHidden(true);
+        }
+        break;
         case sfx::AccessibilityIssueID::FAKE_FOOTNOTE:
         {
             if 
(!officecfg::Office::Common::AccessibilityIssues::FakeFootnotes::get())
@@ -603,6 +610,7 @@ private:
         for (size_t i = 0; i < rHints.Count(); ++i)
         {
             const SwTextAttr* pTextAttr = rHints.Get(i);
+            SwDoc& rDocument = pTextNode->GetDoc();
             if (pTextAttr->Which() == RES_TXTATR_INETFMT)
             {
                 OUString sHyperlink = pTextAttr->GetINetFormat().GetValue();
@@ -634,7 +642,6 @@ private:
                     {
                         pIssue->setIssueObject(IssueObject::TEXT);
                         pIssue->setNode(pTextNode);
-                        SwDoc& rDocument = pTextNode->GetDoc();
                         pIssue->setDoc(rDocument);
                         pIssue->setStart(nStart);
                         pIssue->setEnd(nStart + sRunText.getLength());
@@ -645,22 +652,80 @@ private:
                         OUString sHyperlinkName = 
pTextAttr->GetINetFormat().GetName();
                         if (sHyperlinkName.isEmpty())
                         {
-                            std::shared_ptr<sw::AccessibilityIssue> pNameIssue
-                                = lclAddIssue(m_rIssueCollection, 
SwResId(STR_HYPERLINK_NO_NAME),
-                                              
sfx::AccessibilityIssueID::HYPERLINK_NO_NAME,
-                                              
sfx::AccessibilityIssueLevel::WARNLEV);
+                            pIssue = lclAddIssue(m_rIssueCollection, 
SwResId(STR_HYPERLINK_NO_NAME),
+                                                 
sfx::AccessibilityIssueID::HYPERLINK_NO_NAME,
+                                                 
sfx::AccessibilityIssueLevel::WARNLEV);
 
-                            if (pNameIssue)
+                            if (pIssue)
                             {
-                                
pNameIssue->setIssueObject(IssueObject::HYPERLINKTEXT);
-                                pNameIssue->setNode(pTextNode);
-                                SwDoc& rDocument = pTextNode->GetDoc();
-                                pNameIssue->setDoc(rDocument);
-                                pNameIssue->setStart(nStart);
-                                pNameIssue->setEnd(nStart + 
sRunText.getLength());
+                                
pIssue->setIssueObject(IssueObject::HYPERLINKTEXT);
+                                pIssue->setNode(pTextNode);
+                                pIssue->setDoc(rDocument);
+                                pIssue->setStart(nStart);
+                                pIssue->setEnd(nStart + sRunText.getLength());
                             }
                         }
                     }
+
+                    // check Hyperlinks in Header/Footer --> annotation is not 
nested
+                    if (rDocument.IsInHeaderFooter(*pTextNode))
+                    {
+                        pIssue
+                            = lclAddIssue(m_rIssueCollection, 
SwResId(STR_LINK_TEXT_IS_NOT_NESTED),
+                                          
sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER,
+                                          
sfx::AccessibilityIssueLevel::WARNLEV);
+
+                        if (pIssue)
+                        {
+                            pIssue->setIssueObject(IssueObject::TEXT);
+                            pIssue->setNode(pTextNode);
+                            pIssue->setDoc(rDocument);
+                            pIssue->setStart(nStart);
+                            pIssue->setEnd(nStart + sRunText.getLength());
+                        }
+                    }
+                }
+            }
+
+            // check other Link's in Header/Footer --> annotation is not nested
+            if (pTextAttr->Which() == RES_TXTATR_FIELD && 
rDocument.IsInHeaderFooter(*pTextNode))
+            {
+                bool bWarning = false;
+                const SwField* pField = pTextAttr->GetFormatField().GetField();
+                if (SwFieldIds::GetRef == pField->Which())
+                {
+                    bWarning = true;
+                }
+                else if (SwFieldIds::TableOfAuthorities == pField->Which())
+                {
+                    const auto& rAuthorityField = *static_cast<const 
SwAuthorityField*>(pField);
+                    if (auto targetType = rAuthorityField.GetTargetType();
+                        targetType == SwAuthorityField::TargetType::None)
+                    {
+                        bWarning = false;
+                    }
+                    else
+                    {
+                        bWarning = true;
+                    }
+                }
+
+                if (bWarning)
+                {
+                    auto pIssue
+                        = lclAddIssue(m_rIssueCollection, 
SwResId(STR_LINK_TEXT_IS_NOT_NESTED),
+                                      
sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER,
+                                      sfx::AccessibilityIssueLevel::WARNLEV);
+
+                    if (pIssue)
+                    {
+                        sal_Int32 nStart = pTextAttr->GetStart();
+                        pIssue->setIssueObject(IssueObject::TEXT);
+                        pIssue->setNode(pTextNode);
+                        pIssue->setDoc(rDocument);
+                        pIssue->setStart(nStart);
+                        pIssue->setEnd(nStart + 1);
+                    }
                 }
             }
         }
diff --git a/sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx 
b/sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx
index a898f9c5384d..6f68a99949c0 100644
--- a/sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx
+++ b/sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx
@@ -416,6 +416,7 @@ void A11yCheckIssuesPanel::populateIssues()
             case sfx::AccessibilityIssueID::AVOID_FOOTNOTES:
             case sfx::AccessibilityIssueID::AVOID_ENDNOTES:
             case sfx::AccessibilityIssueID::FONTWORKS:
+            case sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER:
             {
                 if (!pIssue->getHidden())
                     addEntryForGroup(AccessibilityCheckGroups::Other, 
nIndices, pIssue);

Reply via email to