• [snippet] ChildDocumentMapper#

  • Olaf Reply #1, 6 years, 5 months ago

    Reply
    I love using the hierarchy of the MODx document tree to create certain functionality. While I was working on a FAQ snippet, I found out that most of these snippets follow the same procedure.

    The ChildDocumentMapper snippet allows you to map a chunk to every childpage. The usage is as follows:

      [list]
    • create a folder that is your container for the items
    • fill the contents of this folder with whatever text you want, and include the ChildDocumentMapper snippet
    • create a chunk that contains the layout for the items
    [/list]

    The snippet should be given a parameter called docChunk. Like this:
    [[ChildDocumentMapper?docChunk=NewsMapChunk]]


    What happens is that for every child document of the container the document variables are inserted into placeholders. These placeholders can be used in the docChunk together with some HTML layout.
    For a list of all document variables run this command on your MySQL database:
    describe site_content;
    


    An example chunk could be:
    <h3>[+pagetitle+]</h3>
    created by [+createdby+] on [+createdon+]<br />
    [+introtext+]
    


    This snippet allows you to create a single layout for every item. I see the usage in FAQ's, news listings, etc. Keep in mind that the items do not use their template, so it can be set to blank. Or integrate links to the child documents in the chunk (the pageid is also available).

    For the next version I'm thinking of using subfolders. Actually Theo went on with this snippet. You can find the latest version at the end of this thread. http://modxcms.com/forums/index.php/topic,843.msg26488.html#msg26488
    The snippet code:
    /*
     * ChildDocumentMapper. This snippet takes a start document ID and queries
     * this page for it's children. For every document a chunk is inserted. 
     *
     * Version 1.0
     *
     * Parameters:
     *
     * docChunk:   The chunk that is inserted for every document. (required)
     * emptyChunk: The chunk displayed when no child documents found.
     * publishedOnly: Use only published documents. (default = true)
     */
    
    // Check parameters and set defaults
    if (!isset($docChunk))
      return "No docChunk parameter set!";
    if (!isset($emptyChunk))
      $emptyChunk = "No documents found";
    if (!isset($publishedOnly))
      $publishedOnly = true;
    
    
    $root = $modx->documentIdentifier;
    $childPages = $modx->getDocumentChildren($root, $publishedOnly, 0);
    $nrChildPages = count($childPages);
    
    if ($nrChildPages != 0)
    {
      for ($index = 0; $index < $nrChildPages; $index++)
      {
        // Set placeholders that can be used in the Chunk
        foreach ($childPages[$index] as $docVar => $docVarValue)
          $modx->setPlaceholder($docVar, $docVarValue);
    
        // Expand the chunk code, and replace Placeholders
        $output .= $modx->mergePlaceholderContent(
                    $modx->mergeChunkContent("{{".$docChunk."}}"));
      }
    } else
      $output = $emptyChunk;
    
    return $output;
    



  • aNoble Reply #2, 6 years, 5 months ago

    Reply
    That's a great idea.I like the flexibility.

    I use a few snippets that could easily be replaced by this one. Thanks for the great code.


  • Zaigham (aka zi) Reply #3, 6 years, 5 months ago

    Reply
    Hey Olaf !

    Thanks for nice snippet !
    Best wishes and warm regards,

    zi


  • neverbeclever Reply #4, 6 years, 5 months ago

    Reply
    A truely brilliant snippet!

    I had an idea to make it even more powerfull. What about adding the TV's? It won't parse them now I believe.

    Wonderful!


  • davidm Reply #5, 6 years, 2 months ago

    Reply
    Not tested yet but I love the idea

    Thx !


  • neverbeclever Reply #6, 6 years, 2 months ago

    Reply
    Including TV's is quite simple if you have the right API function. I use this function in a slightly modified snippet. Using placeholders like [+tvname+] in your template is then possible.

    Here's the snippet. I've added some features to my needs that might not be usefull to everybody. Anyway, here it is.
    <?php
    /*
     * ChildDocumentMapper. This snippet takes a start document ID and queries
     * this page for it's children. For every document a chunk is inserted. 
     *
     * Version 1.0
     * - Originial version by Olaf
     * 
     * Version 1.1
     * - Added TV support (requires newer API that supports TVs)
     * - Added sorting parameter
     * - Added max parameter (limits the listing)
     * 
     * Version 1.2
     * - Added exclude feature by TV; exclude when TV-name=excluded, value=yes
     *
     * Parameters:
     *
     * id:         The root id that contains the children
     * doChunk:    The chunk that is inserted for every document. (required)
     * emptyChunk: The chunk displayed when no child documents found.
     *             Defaults to: template_nodocuments
     * moreChunk:  The chunk displayed when the max number of docs is reached.
     *             Defaults to: template_moredocuments
     * moreId:     The page id that contains more documents.
     *             This variable can be used in the moreChunk as [+moreId+]
     * sort:       Max 2 fields to sort on. Defaults to: pagetitle
     * sortorder:  Defaults to 'ASC'
     * max:        Maximum number of items before 'moreChunk' is shown
     *             Defaults to 0, no maximum
     * locate:     Sets the language for the date format. Defaults to uk_UK
     * no_tv:      Turns off the configured TV's. Defaults to false
     * excluded:   Turn on exclusion feature (excluded=1)
     *
     */
    
    // Configuration:
    // Comma separated list with TVs to include
       $tvs = "localfile, itemdate";
    
    // Date format for date TV's
       $dateformat = "%B %e, %Y";
       if ($locale=="nl_NL") $dateformat = "%e %B %Y";
    
    
    // Check parameters and set defaults
    if (!isset($doChunk))
      return "No template parameter set!";
    if (!isset($emptyChunk))
      $emptyChunk = "template_nodocuments";
    if (!isset($moreChunk))
      $moreChunk = "template_moredocuments";
    if (!isset($sort))
      $sort = "pagetitle";
    if (!isset($sortorder))
      $sortorder = 'ASC';
    if (!isset($locale))
      $locale = 'uk_UK';
    if (!isset($id))
      $id = $modx->documentIdentifier;
    if (!isset($max))
      $max = 0;
    $excluded_feature = isset($excluded) ? 1 : 0;
    
    setlocale(LC_TIME, $locale);
    
    // Covert $sort string to array
    foreach(explode(',',$sort) as $s) $sortkey[]=trim($s);
    // Create TV-array for passing to API-call if not disabled by parameter
    if ($no_tv) $tvars=array();
      else foreach(explode(',',$tvs) as $tv) $tvars[trim($tv)]="*";
    // Get the documents
    $childPages = $modx->getDocumentChildrenByTVar($id, $tvars, 1, $fields="*");
    // Convert formatted times to unix time-stamps (adapt function if needed)
    correctTime($childPages);
    // Do the sorting
    customSort($childPages, $sortkey[0], $sortkey[1], $sortorder);
    // Check for maximum to display
    $nrChildPages = count($childPages);
    $maxindex = $nrChildPages>$max ? $max : $nrChildPages;
    if (!$maxindex) $maxindex=$nrChildPages;
    
    // Count pages that are excluded from the list
    $excluded_pages=0;
    
    // Set the placeholders and produce the chunks
    if ($nrChildPages != 0) {
      for ($index = 0; $index < $maxindex; $index++) {
    
        // Check if page has an excluded TV set to yes
        if ($excluded_feature) {
          $excluded = $modx->getTemplateVar("excluded","",$childPages[$index]["id"]);
          $excluded = $excluded['value'];
        }
    
        if ($excluded=="yes") {
          // increase excluded pages and increase maxindex
          $excluded_pages++;
          if ($nrChildPages-1>$index && $nrChildPages>$maxindex) $maxindex++;
        } else {
          foreach ($childPages[$index] as $docVar => $docVarValue) {
            // Format date if needed
            if ($childPages[$index]["$docVar.type"]=='date') $docVarValue=trim(strftime($dateformat,$docVarValue));
          // Set placeholders that can be used in the Chunk
            $modx->setPlaceholder($docVar, $docVarValue);
          }
          // Expand the chunk code, and replace Placeholders
          $output .= $modx->mergePlaceholderContent(
                      $modx->mergeChunkContent("{{".$doChunk."}}"));
        }
      }
      if ($nrChildPages>$max && $max) {
        $output .= "{{".$moreChunk."}}";
        $modx->setPlaceholder("moreId", $moreId);
        $modx->setPlaceholder("moreCount", $nrChildPages-$max+$excluded_pages);
      }
    
    } else
      $output = "{{".$emptyChunk."}}";
    
    return $output;
    
    
    
    function customSort(&$data, $field1, $field2, $order) {
      $code = "\$retval = strnatcmp(\$a['$field1'], \$b['$field1']); if(!\$retval) return strnatcmp(\$a['$field2'], \$b['$field2']); return \$retval;";
      $params = ($order=='ASC') ? '$a,$b' : '$b,$a';
      usort($data, create_function($params, $code)); 
    }
    
    // This function corrects locally formatted date-TVs
    // like 'dd-mm-yyyy' to the universal unix time stamp
    function correctTime(&$data) {
      for ($index=0; $index<count($data); $index++) {
        foreach ($data[$index] as $name => $value) {
          if ($data[$index]["$name.type"]=='date') {
            list($date,$time) = split('[ ]', $data[$index][$name]);
            list($mday,$mon,$year) = split('[-]', $date);
            $data[$index][$name] = strtotime("$mon/$mday/$year");
          }
        }
      }
    }
    ?>
    


  • wendy Reply #7, 6 years, 2 months ago

    Reply
    Could you explain it further on how to install this on a MODx? For example, include the necessary code that need to be added to the MODx API, and where it's suppose to be added.

    I also wonder about v1.2, what does it mean by excluding tv?

    Thanks...


  • neverbeclever Reply #8, 6 years, 2 months ago

    Reply
    That's an interesting question about custom API's. I'll do when I've got some more time. Hopefully monday.

    About version 1.2 - I use the snippet for eg newsitems. I had to hide certain newsitems from the 5 entries shown on the homepage. On the news page there had to be a complete list of all the news however. So I use&excluded=1 on the homepage and&excluded=0 on the news page. The newsitems have a TV named 'excluded'. When the value of this TV is set to yes, the news shows on the news page but not on the home page. Making room for more important news.


  • davidm Reply #9, 6 years, 2 months ago

    Reply
    Thanks a lot for this Theo, just what I needed


  • neverbeclever Reply #10, 6 years, 2 months ago

    Reply
    MOVE TO A NEW THREAD?

    See also my comments on the API function
    Could someone of the coreteam give their view on custom API's?

    For now I simply edit /manager/includes/document.parser.class.inc.php and include the function to the DocumentParser class.

    It would be better to have somthing like this in the DocumentParser class

    $ret = @include_once "/assets/api/DocumentParser/customapi.inc.php"
    And put the extra functions in there.

    You can have this configurable in the manager as well.

    Maybe even better is to have a base class that you can extend with custom functions.

    Since plugins are ment to extend parser functionality it might be an idea to have a special event to include custom API's.

    As I said, I've hacked my function into the core class. Though I'm convinced that we should think of a better way to include custom functions. Plugins are probably the answer to this. Though I've not yet thought about this until now.

    I'd really like to know what others think about what's the best approach.