Page MenuHomePhabricator

Revise table of contents data format
Closed, ResolvedPublic3 Estimated Story Points

Description

Background

While working on the table of contents @bwang noticed some issues with the data format we landed on as part of T287767. Specifically the nesting level for lists is incorrect. In the example below, each li element of the same level (i.e. "sidebar-toc-level-1") should be siblings in the same list.

<nav>
  <ul id="table-of-contents">
    <li class="sidebar-toc-level-1">
      <a href="#Heading">Heading</a>
      <ul>
        <li class="sidebar-toc-level-2">
          <a href="#Subheading_1">Subheading 1</a>
            <ul>
              <li class="sidebar-toc-level-3">
                <a href="#Subheading_2">Subheading 2</a>
              </li>
            </ul>
          </li>
          <li class="sidebar-toc-level-1">
            <a href="#Heading_2">Heading</a>
          </li>
       </ul> 
    </li>
  </ul>
</nav>

Given MediaWiki 1.38 hasn't been released yet we are likely to make potentially breaking changes to the format prior to the release. We are not aware of anyone using this yet so that should be fine.

A better format would allow recursive access

Acceptance criteria

  • Data returned in the required format
  • Make sure unit tests cover the function

Developer notes

  • Modify the private Skin::getSectionsData to change the return value.

e.g.

Section = { Section[]: subsections, string: heading }

QA steps

NOTE: testing should probably be done by an engineer
  • Add some test headings to a local sample page - snippet below has a sampling of embedded sections:
== Plot summary ==
the Plot

== Heading ==
Macaroon tiramisu sesame snaps gummies cheesecake tart chocolate tootsie roll chocolate cake. Tart brownie oat cake chocolate bar muffin gummi bears fruitcake. Topping bonbon candy canes tootsie roll tart.

=== Sub Heading ===
Donut toffee danish bonbon bonbon shortbread cupcake croissant marzipan. Cheesecake gummi bears cotton candy liquorice sesame snaps liquorice marshmallow macaroon. 

=== Another Sub Heading ===
Jelly-o dessert gummi bears cotton candy candy caramels chocolate cake cotton candy. Donut cookie sweet roll cupcake cotton candy icing croissant chupa chups soufflé. 

==== Sub Sub Heading 1 ====
Jelly cheesecake chocolate jujubes dragée wafer candy jelly. Sesame snaps bear claw fruitcake bonbon carrot cake apple pie. 

===== Sub Sub Sub Heading 1 =====
Carrot cake icing icing chupa chups cake toffee topping shortbread. Cotton candy jelly-o donut pie sesame snaps dessert pie oat cake. 

===== Sub Sub Sub Heading 2 =====
Cake sugar plum candy canes lollipop donut tootsie roll chocolate bar pudding macaroon. Ice cream powder carrot cake bear claw chocolate caramels cheesecake. 

====== Sub Sub Sub Sub Heading 1 ======
Cake sugar plum candy canes lollipop donut tootsie roll chocolate bar pudding macaroon. Ice cream powder carrot cake bear claw chocolate caramels cheesecake. Candy canes lollipop brownie lemon drops croissant tart lollipop cake chocolate bar. 

===== Sub Sub Sub Heading 3 =====
Candy canes lollipop brownie lemon drops croissant tart lollipop cake chocolate bar. 

==== Sub Sub Heading 2 ====
Topping bonbon candy canes tootsie roll tart.Jelly cheesecake chocolate jujubes dragée wafer candy jelly. Sesame snaps bear claw fruitcake bonbon carrot cake apple pie. Donut toffee danish bonbon bonbon shortbread cupcake croissant marzipan.

== Another Heading ==
Bonbon jelly soufflé cake shortbread halvah. Chupa chups chocolate cookie cupcake cake. Marzipan caramels shortbread chocolate cake bear claw muffin. Sweet roll macaroon biscuit halvah sugar plum cotton candy. Oat cake pudding wafer oat cake chocolate cake marzipan dragée.

== Yet Another Heading ==
Gummi bears pudding tootsie roll tootsie roll sweet roll tart jelly-o topping sweet. Dragée soufflé tiramisu candy bonbon oat cake. Lemon drops pie sesame snaps cake sweet roll jelly-o croissant sweet. Sweet roll biscuit ice cream gummies muffin tiramisu dragée. Cake sweet roll soufflé croissant dessert ice cream jelly beans. Lollipop soufflé cupcake gummies apple pie sugar plum. Pie jelly croissant dragée dragée. 

=== Sub Heading 1 ===
Dragée sweet shortbread brownie pastry tart apple pie shortbread chocolate. Candy canes cheesecake ice cream chupa chups chocolate bear claw wafer. Ice cream gingerbread pudding tart sweet roll. Sweet marzipan pastry chupa chups carrot cake powder candy soufflé. Apple pie jujubes powder cheesecake chupa chups shortbread. Gummies topping cupcake cake oat cake tart chupa chups dragée cookie.

==== Sub Sub Heading 1 ====
Jelly cheesecake chocolate jujubes dragée wafer candy jelly. Sesame snaps bear claw fruitcake bonbon carrot cake apple pie. Donut toffee danish bonbon bonbon shortbread cupcake croissant marzipan. Cheesecake gummi bears cotton candy liquorice sesame snaps liquorice marshmallow macaroon. 

===== Sub Sub Sub Heading 1 =====
Carrot cake icing icing chupa chups cake toffee topping shortbread. Cotton candy jelly-o donut pie sesame snaps dessert pie oat cake. Jelly-o dessert gummi bears cotton candy candy caramels chocolate cake cotton candy. Donut cookie sweet roll cupcake cotton candy icing croissant chupa chups soufflé. 

===== Sub Sub Sub Heading 2 =====
Cake sugar plum candy canes lollipop donut tootsie roll chocolate bar pudding macaroon. Ice cream powder carrot cake bear claw chocolate caramels cheesecake. 

====== Sub Sub Sub Sub Heading 1 ======
Cake sugar plum candy canes lollipop donut tootsie roll chocolate bar pudding macaroon. 

=== Sub Heading 2 ===
Marzipan gummies sesame snaps marshmallow muffin. Croissant danish liquorice ice cream soufflé tiramisu pie cake cupcake. 

== Heading Again ==
Dragée soufflé tiramisu candy bonbon oat cake. Lemon drops pie sesame snaps cake sweet roll jelly-o croissant sweet. Sweet roll biscuit ice cream gummies muffin tiramisu dragée. Cake sweet roll soufflé croissant dessert ice cream jelly beans. Lollipop soufflé cupcake gummies apple pie sugar plum. 

== More Headings ==
Jelly cheesecake chocolate jujubes dragée wafer candy jelly. Sesame snaps bear claw fruitcake bonbon carrot cake apple pie. 


== heading 1 ==
 
=== heading 2 ===

==== heading 3 ====

===== heading 4 =====

====== heading 5 ======

====== heading 6 =====

=== heading 7 (2) ===



== test 1 ==
 
=== test 2 ===

==== test 3 ====

===== test 4 =====

====== test 5 ======

====== test 6 ======

====== test 7 ======

==== test 8 (3) ====

== test 9 (1) ==
  • The snippet above will produce a TOC like so:

Screen Shot 2022-01-25 at 11.59.58 PM.png (2×822 px, 269 KB)

  • Set a breakpoint (or var_dump the value) on the return of Skin::getSectionsData() to look at the updated, nested TOC data array.
  • Child sections should be nested under their parent sections inside the parent's array-sections key and sections without child sections should have an empty array-sections array:

Screen Shot 2022-02-01 at 11.27.11 AM.png (2×2 px, 1 MB)

Event Timeline

Jdlrobson updated the task description. (Show Details)

@bwang could you describe the data you'd like to see here?

Approach 1

A nested associative array structure would be easy to work with, as it directly models the markup and would make our mustache templates very simple.

For example, a heading structure like this

1. Heading 1
  1.1 Subheading 1
    1.2 Subheading 2
2. Heading 2

could be rendered with the following data and templates

$data = array(
  array(
    'toclevel' => 1,
    'level' => "2",
    'line' => "Heading",
    'number' => "1",
    'anchor' => "Heading",
    'data-subsections' => array(
      'toclevel' => 2,
      'level' => "3",
      'line' => "Subheading 1",
      'number' => "1.1",
      'anchor' => "Subheading_1",
      'data-subsections' => array(
        'toclevel' => 3,
        'level' => "4",
        'line' => "Subheading 2",
        'number' => "1.2",
        'anchor' => "Subheading_2",
        'data-subsections' => []
      )
    )
  ),
  array(
    'toclevel' => 1,
    'level' => "2",
    'line' => "Heading 2",
    'number' => "2",
    'anchor' => "Heading_2",
    'data-subsections' => []
  ),
);
TableOfContents.mustache
<nav>
  <ul id="table-of-contents">
    {{#array-sections}}
      {{>TableOfContents__link}}
    {{/array-sections}}
  </ul>
</nav>

TableOfContents__link.mustache
<li class="sidebar-toc-level-{{toclevel}}">
  <a href="#{{anchor}}">
    <div class="sidebar-toc-text">
    <span class="sidebar-toc-numb">{{number}}</span> {{line}}</div>
  </a>
  {{#data-subsections}}
    <ul>
      {{>TableOfContents__link}}
    </ul>
  {{/data-subsections}}
</li>

This approach would require us to enable recursive partials (i.e.TemplateParser::enableRecursivePartials) but I'm unsure if there are other implications from that. The other downside is that this data structure is a departure from the flat array provided by the parser, and we'd need to add some complexity to getSectionsData in order to convert the data.

Approach 2

Alternatively we could keep the existing flat array and replace is-last-item with data that indicates how many subsections are to be closed for a given section.

With the same example as above, the data and templates would look like:

$data = array(
  array(
    'toclevel' => 1,
    'level' => "2",
    'line' => "Heading",
    'number' => "1",
    'anchor' => "Heading",
    'has-subsection' => true,
    'subsection-close' => 0,
  ), 
  array(
    'toclevel' => 2,
    'level' => "3",
    'line' => "Subheading 1",
    'number' => "1.1",
    'anchor' => "Subheading_1",
    'has-subsection' => true,
    'subsection-close' => 0,
  ),
  array(
    'toclevel' => 3,
    'level' => "4",
    'line' => "Subheading 2",
    'number' => "1.2",
    'anchor' => "Subheading_2",
    'has-subsection' => false,
    'subsection-close' => 2,
  ),
  array(
    'toclevel' => 1,
    'level' => "2",
    'line' => "Heading 2",
    'number' => "2",
    'anchor' => "Heading_2",
    'has-subsection' => false,
    'subsection-close' => 0,
  ),
);

This would let us keep the flat array from the parser data, but getSectionsData would still need to be updated to provide this extra data. Still unsure of how to best implement this data.

For future reference, the task where data use is taking place and the issue encountered, is T297611.

To clarify, this task is scoped to changing the data format of getSectionsData. Eabling recursive partials will be handled in another task.

Change 756590 had a related patch set uploaded (by Clare Ming; author: Clare Ming):

[mediawiki/core@master] Update Skin::getSectionData method

https://gerrit.wikimedia.org/r/756590

cjming moved this task from Doing to Code Review on the Web-Team-Backlog (Kanbanana-FY-2021-22) board.
cjming subscribed.

Test wiki created on Patch demo by CMing (WMF) using patch(es) linked to this task:

https://patchdemo.wmflabs.org/wikis/84a2370371/w/

Test wiki on Patch demo by CMing (WMF) using patch(es) linked to this task was deleted:

https://patchdemo.wmflabs.org/wikis/84a2370371/w/

Change 756590 merged by jenkins-bot:

[mediawiki/core@master] Update Skin::getSectionData method

https://gerrit.wikimedia.org/r/756590

Change 757842 had a related patch set uploaded (by Clare Ming; author: Clare Ming):

[mediawiki/core@master] Update section name in toc data array

https://gerrit.wikimedia.org/r/757842

Change 757842 merged by jenkins-bot:

[mediawiki/core@master] Update section name in toc data array

https://gerrit.wikimedia.org/r/757842

Change 757767 had a related patch set uploaded (by Jdlrobson; author: Jdlrobson):

[mediawiki/skins/Vector@master] Add recursive template for toc

https://gerrit.wikimedia.org/r/757767

Jdlrobson moved this task from QA to Code Review on the Web-Team-Backlog (Kanbanana-FY-2021-22) board.

Note the QA and code review are the same here. Move to sign off when done.

Change 757767 merged by jenkins-bot:

[mediawiki/skins/Vector@master] Add recursive template for toc

https://gerrit.wikimedia.org/r/757767