We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 813 ☆ A M B ☆
    • 326 Posts
    Hi everyone,

    First off, great conference earlier this week! It inspired me to work on my first plugin. This will be of limited use to the community, but I’ll throw the code out there anyway in case anyone can benefit from it.

    Basically, I have several hundred pages that contain course codes, like the muted-blue links on this page:

    http://www2.ku.edu/~distinction/cgi-bin/degree-requirements64

    Originally, these were just text and not links, but it would’ve been extremely tedious for our content editors to manually link every course on the site. My plugin fires at OnWebPagePrerender, and using some regular expression awesomeness converts the text into a link to a corresponding page on our registration system. The plugin looks like this (there’s some other stuff going on there to prevent this plugin from affecting every single page, but the RegEx line is my real issue):

    $e = &$modx->event;
    
    if ($e->name == 'OnWebPagePrerender') {
        $docid = $modx->documentIdentifier;
        $pagetitle = $modx->documentObject['pagetitle'];
        $tvnames = array('coursereqs_check');
        $tvoutput = $modx->getTemplateVarOutput($tvnames, $docid, 1);
        $coursereqs_check = $tvoutput['coursereqs_check'];
    
        if (($coursereqs_check == 1) || ($pagetitle == "Degree Requirements") || ($pagetitle == "Minor Requirements") || ($pagetitle == "Requirements")) {
    	$o = &$modx->documentOutput; // get a reference of the output
    	$o = preg_replace("([A-Z]{2,4}\s[0-9]{3})", "<a href=\"https://classes.ku.edu/Classes/CourseSearchAPI.action?classesSearchText=$0\">$0</a>",$o);
        }
    }
    
    $e->output($o);
    return '';


    As you can tell, the RegEx looks for an all-caps, 2-4 letter string followed by a space and a three-digit number. This works great, except for one thing: some of the course subjects contain html-encoded ampersands (&amp;), like on this page:

    http://www2.ku.edu/~distinction/cgi-bin/degree-requirements34

    This screws up my RegEx logic entirely. I’m wondering if I need to unencode them first before running them through preg_replace and then re-encode at the end, but that doesn’t seem to be working. Can anyone help?

    Thanks!



      • 21257 MODX Staff
      • 730 Posts
      Hey Aaron,
      I started recommending a solution, then started testing it out.. it’s a little trickier than I first thought. Not a huge deal, but I chose to try it right before I have to get off the computer ;-) If no one else posts a solution in the meantime, I’ll get back here tonight and take the 5 minutes to finish it off.
        Mike Schell
        Lead Developer, MODX Cloud
        Email: [email protected]
        GitHub: https://github.com/netProphET/
        Twitter: @mkschell
        • 813 ☆ A M B ☆
        • 326 Posts
        Thanks, man. No other takers so far smiley I haven’t messed around with it yet today, but I tried some stuff last night and still couldn’t get it. It’s just a bit more complicated than it seems on the surface.
          • 21257 MODX Staff
          • 730 Posts
          Aaron, I think this should work, and should be compatible back to PHP 4.0.5:

          <?php
          $e = &$modx->event;
          
          if(!function_exists('course_link')) {
              function course_link($ma) {
                  return "<a href=\"https://classes.ku.edu/Classes/CourseSearchAPI.action?classesSearchText=" . urlencode($ma[0]) . "\">" . $ma[0] . "</a>";
              }
          }
          
          if ($e->name == 'OnWebPagePrerender') {
              $docid = $modx->documentIdentifier;
              $pagetitle = $modx->documentObject['pagetitle'];
              $tvnames = array('coursereqs_check');
              $tvoutput = $modx->getTemplateVarOutput($tvnames, $docid, 1);
              $coursereqs_check = $tvoutput['coursereqs_check'];
          
              if (($coursereqs_check == 1) || ($pagetitle == "Degree Requirements") || ($pagetitle == "Minor Requirements") || ($pagetitle == "Requirements")) {
                  $o = &$modx->documentOutput; // get a reference of the output
                  $o = str_replace('&', '&', $o);
                  $o = preg_replace_callback("/([A-Z\&]{2,4}\s[0-9]{3})/", 'course_link', $o);
              }
          }
          
          $e->output($o);
          return '';
          
            Mike Schell
            Lead Developer, MODX Cloud
            Email: [email protected]
            GitHub: https://github.com/netProphET/
            Twitter: @mkschell
            • 813 ☆ A M B ☆
            • 326 Posts
            Woohoo! It works (I did have to remove the opening "<?php", but once I realized that, it was easy). I can’t thank you enough. I was unfamiliar with preg_replace_callback, but it makes a lot of sense now that I see it.

            This is purely for my understanding of what’s going on here, so if you’re busy, no worries, but...how are my "&" signs still ending up properly encoded on the final page? Since we used str_replace to unencode them before running them through the RegEx and creating the output, I was expecting them to come out unencoded. But magically, they’re re-encoded when I view the page source. Glad it’s happening, but I’m clueless as to how this works! smiley

              • 21257 MODX Staff
              • 730 Posts
              Look for the urlencode function wink
              (BTW, I just put the <?php in there so the forum’s code formatter would sex it up.)
                Mike Schell
                Lead Developer, MODX Cloud
                Email: [email protected]
                GitHub: https://github.com/netProphET/
                Twitter: @mkschell
                • 813 ☆ A M B ☆
                • 326 Posts
                Oh...! Duh. Now I get it. I should read the whole function before asking silly questions. This is why I’m still a grasshopper and not a zen master.

                Thanks again!
                  • 813 ☆ A M B ☆
                  • 326 Posts
                  The project changed slightly, and I now need to pull in a TV value from the page and add it to the URL that’s generated in the function. However, I’m having trouble understanding how to pass it into the function (preg_replace_callback is confusing me). Any suggestions?
                    • 21257 MODX Staff
                    • 730 Posts
                    The callback function receives an array of matches from the regular expression, which in this case is the whole matched expression. ($ma[0] is the whole expression, while parenthesized parts of the expression would be returned starting at $ma[1], $ma[2] etc. In retrospect, the parentheses around the whole expression weren’t required.)
                    There are a couple of ways to handle this change, but IMO, why bother trying to pass the TV value into the callback? Just stick a placeholder (anything, e.g. ||TV|| or [+TVHere+] or <<whatever>>) in the URL string that the callback generates, then replace it, after the preg_replace_callback statement, with your TV value.
                      Mike Schell
                      Lead Developer, MODX Cloud
                      Email: [email protected]
                      GitHub: https://github.com/netProphET/
                      Twitter: @mkschell
                      • 813 ☆ A M B ☆
                      • 326 Posts
                      Brilliant! As usual, I was looking for the most complicated method, when a simple placeholder and str_replace after the function did the trick. Thank you!!!!