Download & Extend

Support link target which contains CamelCase

Project:freelinking
Version:4.7.x-1.0
Component:Code
Category:bug report
Priority:normal
Assigned:Unassigned
Status:closed (fixed)

Issue Summary

Related to and also resolves: http://drupal.org/node/128816

The issue I was having was related to links of this form:
[[Some CamelCase Here|Link Name]]

I mentioned this http://drupal.org/node/48326 and was told it was nightmarish. Which I agree, it can be. But I thought I'd give the solution a shot anyway. It involves splitting the filter into two passes. The first handles bracketed links, and the second the CamelCase links. Here is the modified _freelinking_do_filtering function:

function _freelinking_do_filtering($text, $store = FALSE) {
    // First pass. Only bracketed links
    $freelinkingregexp = '/\[\[.+]]/U'; // this finds [[ links like this]], un-greedily
    preg_match_all($freelinkingregexp, $text, $flmatches);
    $wikiwords = $flmatches[0];

    $tokens = $phrases = $replacements = array();
    foreach (array_unique($wikiwords) as $wikiword) {
        if (substr($wikiword, 0, 2) == '[[') { // if it's a freelink, the expressions are different
            $phrase = substr($wikiword, 2, -2);
            $freelink = $phrase;
            $barpos = strpos($phrase, '|');
            $pattern = '/\[\[' . preg_quote($phrase, '/') . ']]/';
            if ($barpos) {
                $freelink = substr($freelink, 0, $barpos);
                $phrase = substr($phrase, $barpos + 1);
            }
            if (preg_match('/^(http|mailto|https|ftp):/', $freelink)) {
                $replacement = '<a class="freelinking external" href="' . $freelink . '">' . $phrase . '</a>';
            }
            else {
                $replacement = l($phrase, 'freelinking/' . rawurlencode($freelink), array('class' => 'freelinking'));
            }
        }
        $index = count($tokens);
        $token = '::TOKEN'.$index.'::';
        $tokens[$index] = '/'.$token.'/';
        $phrases[$index] = $phrase;
        $replacements[$index] = $replacement;

        $text = preg_replace($pattern, $token, $text);
    } // foreach wikiword

    // Second pass. CamelCase links (if required).
    $allowcamelcase = variable_get("freelinking_camelcase", TRUE);

    $cctokens = $ccphrases = $ccreplacements = array();
    if ($allowcamelcase) {
        $camelcaseregexp = '/\b([[:upper:]][[:lower:][:digit:]]+){2,}\b/'; // this gets us close, but is not perfect. Example: ThisIsACamelCaseWord won't match
        preg_match_all($camelcaseregexp, $text, $ccmatches);
        $wikiwords = $ccmatches[0];

        foreach (array_unique($wikiwords) as $wikiword) {
            $pattern = '/\b' . $wikiword . '\b(?![^<]*>)/';
            $phrase = $wikiword; // consistency for the db
            $freelink = $wikiword; // also for the db
            $replacement = l($wikiword, 'freelinking/' . urlencode($wikiword));

            $index = count($cctokens);
            $token = '::CCTOKEN'.$index.'::';
            $cctokens[$index] = '/'.$token.'/';
            $ccphrases[$index] = $phrase;
            $ccreplacements[$index] = $replacement;

            $text = preg_replace($pattern, $token, $text);
        } // foreach wikiword
    }

    // Replace the tokens
    ksort($tokens);
    ksort($replacements);
    $text = preg_replace($tokens, $replacements, $text);
    ksort($cctokens);
    ksort($ccreplacements);
    $text = preg_replace($cctokens, $ccreplacements, $text, variable_get("freelinking_onceonly", 0) ? 1 : -1);

    if ($store) {
        ksort($phrases);
        foreach ($phrases as $key => $phrase) {
            _freelinking_store($phrases[$key], $replacements[$key]);
        }

        if ($allowcamelcase) {
            ksort($ccphrases);
            foreach ($ccphrases as $key => $phrase) {
                _freelinking_store($ccphrases[$key], $ccreplacements[$key]);
            }
        }
    }

    return $text;
} // endfunction _freelinking_do_filtering

The filter runs in three passes: the first for bracketed links; the second for CamelCase links. These two replaces the links with a token. In the final pass, the tokens will be replaced with the actual links.

Should work for:

[[Some CamelCase Here|Link Text]]
[[CamelCase|Link Text]]
CamelCase

I would appreciate any feedback for this patch. It seems to do what is expected, but there may be cases I have not considered.

Comments

#1

Status:needs review» needs work

Interesting idea. Care to make this a real patch? http://drupal.org/patch/create

#2

Attached. :)

Let me know if there're any problems with it.

AttachmentSize
freelinking-173474.patch 5.08 KB

#3

Tweaked. It now correctly supports "Only link first occurance" for CamelCase.

AttachmentSize
freelinking-173474v2.patch 5.08 KB

#4

Subscribing. (Turned off CamelCase to get around this.)

#5

Status:needs work» fixed

Fixed in freelinking-6.x-1.7. Please verify and close.

#6

Status:fixed» closed (fixed)

Automatically closed -- issue fixed for two weeks with no activity.

nobody click here