Add noopener to Link Tags VI

Final version of the code to correct link tags with target="_blank" in template variables


In the previous article, we looked at how to modify TV values in various locations. In this final article on the topic, we'll put the TV modification code all together and make it more efficient.

MODX logo

Putting it All Together

In the last article, we used a function to correct the links. We'll use it again, here, but we'll call it in a more efficient loop that combines processing the default values of the TV and the input and output properties. We'll also add some output that lets the user know what was done.

/** @var $modx modX */
if (!function_exists('fixContent')) {
    function fixContent(&$string) {
        $retVal = false;

        /* pattern to find link with _blank */
        $pattern = '/<a [^>]+_blank[^>]*>/i';

        /* Pattern for adding noopener */
        $replacePattern = '/target\s*=\s*[\'"]_blank[\'"]/i';

        /* Get all tags with _blank on the page */
        if (preg_match_all($pattern, $string, $matches)) {

            /* If we got any, loop through them and replace if necessary */
            foreach ($matches[0] as $key => $value) {
                /* Don't process tags that already have noopener and noreferrer */
                if (strpos($value, 'noopener noreferrer') !== false) {
                    continue;
                }

                /* Check for them in reverse order */
                if (strpos($value, 'noreferrer noopener') !== false) {
                    continue;
                }

                /* Save the old tag value */
                $oldValue = $value;

                /* Handle case with noopener, but not noreferrer */
                if ( (strpos($value, 'noopener') !== false)
                        && strpos($value, 'noreferrer') === false) {
                    $newValue = str_replace('noopener',
                        'noopener noreferrer', $value);

                /* handle cases with noreferrer, but not noopener */
                } elseif ( (strpos($value, 'noreferrer') !== false)
                        && strpos($value, 'noopener') === false) {
                    $newValue = str_replace('noreferrer',
                        'noopener noreferrer', $value);

                /* Handle cases with neither noopener, no noreferrer */
                } else {

                    /* Do the replacement within the current tag */
                    $newValue = preg_replace($replacePattern,
                        'target="_blank" rel="noopener noreferrer"',
                        $oldValue);
                }

                /* Do the replacement of the tag in the page */
                $string = str_replace($oldValue, $newValue, $string);

                /* Set return as true to tell calling function
                   we did at least one replacement for this page */
                $retVal = true;
            }
        } else {
            $retVal = false;
        }

        return $retVal;
    } // End of fixContent function
}

if (!function_exists('has_blank')) {
    function has_blank($string) {
        return stripos($string, '_blank') !== false;
    }
}

/* Main Script Starts Here */

/* These are just for the final output */
$output = '';
$summary = array(
    'default_text' => 0,
    'input_properties' => 0,
    'output_properties' => 0,
    'resource_values' => 0,
);

/* Handle changes to TV objects */

$fieldsToCheck = array(
    'default_text',
    'input_properties',
    'output_properties',
);

/* Start with the TV objects */
$tvObjects = $modx->getCollection('modTemplateVar');

foreach ($tvObjects as $obj) {
    $dirty = false;

    /* Get raw values of all fields into an array */
    $rawFields = $obj->toArray("", true);


    foreach($fieldsToCheck as $fieldToCheck) {
        /* Get raw field value */
        $content = $obj->get($fieldToCheck);
        if (has_blank($content)) {
            /* Send raw value to fixContent() */
            $fixed = fixContent($content);

            /* If $fixed is true, we changed something */
            if ($fixed) {
                $summary['$fieldToCheck']++'
                $fields[$fieldToCheck] = $content;
                $dirty = true;
            }
        }
    }
    if ($dirty) {
        $obj->fromArray($fields, "", false, true);
        $obj->save();
    }
}

/* Handle changes to TemplateVarTemplate Objects */
$tvrs = $modx->getCollection('modTemplateVarResource');

foreach($trvs as $tvr) {
    /* Get current value */
    $content = $tvr->get('value');

    if (is_string($content) && has_blank($content)) {
        /* Send current value to fixContent() */
        $fixed = fixContent($content);

        /* if $fixed is true, we changed something */
        if ($fixed) {
            $summary['Resource Values']++'
            $tvr->set('value', $content);
            $tvr->save();
        }
    }
}

/* Display Output */
$output .= '<br>
<h3>Results</h3>';
foreach($summary as $type => $count) {
   $output .= '<br>' . 'Modified ' . $count .
        ' ' $type . ' 'objects';
}

return $output;

Notice that for the first loop, where we get the TV objects, we don't need the is_string() because all three of the fields we're checking hold strings.


One Final Improvement

This code, along with the code in the earlier articles in this series, fixes all existing tags on the site. As long as all your Manager users remember to use rel="noopener noreferrer" in all future tags, it's fine. Another solution, though, would be to create a plugin that modifies the tags as the objects are saved. It could use much of the code we've already got. We'll look at that solution in the next article.



Coming Up

In the following article, we'll look at a plugin that corrects link tags as they are saved.




Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)



Comments (0)


Please login to comment.

  (Login)