root/afridex/plugins/kb-linker/kb_linker.php @ 21

Revision 21, 12.3 kB (checked in by admin, 18 years ago)
RevLine 
[21]1<?php
2/*
3Plugin Name: KB Linker
4Plugin URI: http://adambrown.info/b/widgets/kb-linker/
5Description: Looks for user-defined phrases in posts and automatically links them. Example: Link every occurrence of "Wordpress" to wordpress.org.
6Author: Adam R. Brown
7Version: 1.102
8Author URI: http://adambrown.info/
9*/
10
11//      OPTIONAL SETTINGS
12        /*special characters for foreign languages. Add any you want to the array below. Char goes on left, HTML entity on right. The German codes are here as examples.
13                See http://www.w3schools.com/tags/ref_entities.asp for HTML entities.   */
14        $kblinker_special_chars = array(
15                                'ä' => '&#228;',
16                                'Ä' => '&#196;',
17                                'ö' => '&#246;',
18                                'Ö' => '&#214;',
19                                'ü' => '&#252;',
20                                'Ü' => '&#220;',
21                                'ß' => '&#223;'
22        );
23        /*      would you like to add title="" tags to the links with the keyword in them? true or false. */
24        define( 'KBLINKER_USE_TITLES' , true );
25        /*      if the preceding setting is TRUE, you can customize the text before or after the keyword below. For example, if you want titles to say "More about KEYWORD.",
26                then make before = 'More about ' (note the space) and after = '.'       */
27        $kblinker_title_text = array(
28                'before' => 'More about ', // default: 'More about '
29                'after' => ' &raquo;',  // default: '$raquo;' (an arrow pointing to the right)
30        );
31//      END OF SETTINGS. NO MORE EDITING REQUIRED.
32
33
34
35
36
37/*      DEVELOPMENT NOTES
38
39        CHANGE LOG
40        1.01    initial release
41        1.02    quick fix to check is_array before extracting on line 105
42        1.03    - added support for German (or other) special characters
43                - added support for opening links in different targets
44        1.04    add 'i' tag to the tag-detection regexes (it was already in the main replacement one, causing some errors)
45        1.05    bugfix
46        1.06    bugfix
47        1.10    - puts keyword in link's "title" tags
48                - won't link the same URL more than once per post, even if multiple keywords are associated with it
49                - won't link a page to itself (imperfect)
50        1.101   bugfix
51        1.102   bugfix
52
53        IMPORTANT NOTE TO ANYBODY CONSIDERING ADDING THIS PLUGIN TO A WP-MU INSTALLATION:
54        If you aren't sure whether you are using a WP-MU blog, then you aren't. Trust me. If this warning applies to you, then you will know it.
55        For WP-MU administrators: You should not use this plugin. Your users could use it to place (potentially malicious) javascript into their blogs.
56        This plugin is PERFECTLY SAFE for non-WP-MU blogs, so ignore this message if you're using regular wordpress (you probably are).
57
58        KNOWN BUGS
59        - Pre 1.04: If a tag or attribute contained the keyword, but with different capitalization than specified in options, it was replaced. That causes problems.
60        - 1.04: Fixed that bug, but in return, you might find capitalization changing (within tags and attributes) to match that given in the linker's options. Sorry.
61        - 1.05 Fixed the capitalization bug
62
63        GENERAL NOTE FOR DEVELOPERS/HACKERS/ETC:
64                You will notice that I've included extensive commenting in the code below. I do not have time to support this plugin. The commenting is there to make this plugin
65                easy to modify. Please try making modifications on your own before posting a support question at the plugin's URI.
66                That being said, you are welcome to post well-informed support questions on my site.
67
68        DATABASE STRUCTURE
69                the options->KB Linker page will create a set of matching terms and URLs that gets stored as a list.
70                structure of option "kb_linker":
71                        pairs => array( see below)
72                        text=>  same content as pairs, but in unprocessed form (for displaying in the option's form)
73                        plurals => 1, 0         if 1, we should look for variants of the keywords ending in s or es
74*/
75
76function kb_linker($content){
77        global $kblinker_special_chars,$kblinker_title_text;
78
79        $option = get_option('kb_linker');
80        if (is_array($option)){
81                extract($option);
82        }
83
84        // uncomment for testing (to override options):
85        #$pairs = array( 'contributor'=>'http://google.com', 'a'=>'http://yahoo.com/', 'scripting'=>'scripting', 'don'=>'don', 'first post'=>'firstpost.org', 'first'=>'first.org', 'wp'=>'WP.ORG');
86
87        // die if option isn't set yet
88        if ( !is_array($pairs) )
89                return $content;
90
91        // let's make use of that special chars setting.
92        if (is_array($kblinker_special_chars)){
93                foreach ($kblinker_special_chars as $char => $code){
94                        $content = str_replace($code,$char,$content);
95                }
96        }
97
98        // needed below...
99        $usedUrls = array();
100        $currentUrl = 'http://' . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]; // may not work on all hosting setups.
101
102        // most of the action is in here.
103        foreach ($pairs as $keyword => $url){
104                if (in_array( $url, $usedUrls )) // don't link to the same URL more than once
105                        continue;
106                if (strpos( $content, $url )){ // we've already used this URL, or it was manually inserted by author into post
107                        $usedUrls[] = $url;
108                        continue;
109                }
110                if ($url == $currentUrl){ // don't link a page to itself
111                        $usedUrls[] = $url;
112                        continue;
113                }
114
115                // first, let's check whether we've got a "target" attribute specified.
116                if (false!==strpos( $url, ' ' ) ){      // Let's not waste CPU resources unless we see a ' ' in the URL:
117                        $target = trim(   substr( $url, strpos($url,' ') )   );
118                        $target = ' target="'.$target.'"';
119                        $url = substr( $url, 0, strpos($url,' ') );
120                }else{
121                        $target='';
122                }
123               
124                // let's escape any '&' in the URL.
125                $url = str_replace( '&amp;', '&', $url ); // this might seem unnecessary, but it prevents the next line from double-escaping the &
126                $url = str_replace( '&', '&amp;', $url );
127               
128                // we don't want to link the keyword if it is already linked.
129                // so let's find all instances where the keyword is in a link and precede it with &&&, which will be sufficient to avoid linking it. We use &&&, since WP would pass that
130                // to us as &amp;&amp;&amp; (if it occured in a post), so it would never be in the $content on its own.
131                // this has two steps. First, look for the keyword as linked text:
132                $content = preg_replace( '|(<a[^>]+>)(.*)('.$keyword.')(.*)(</a[^>]*>)|Ui', '$1$2&&&$3$4$5', $content);
133
134                // Next, look for the keyword inside tags. E.g. if they're linking every occurrence of "Google" manually, we don't want to find
135                // <a href="http://google.com"> and change it to <a href="http://<a href="http://www.google.com">.com">
136                // More broadly, we don't want them linking anything that might be in a tag. (e.g. linking "strong" would screw up <strong>).
137                // if you get problems with KB linker creating links where it shouldn't, this is the regex you should tinker with, most likely. Here goes:
138                $content = preg_replace( '|(<[^>]*)('.$keyword.')(.*>)|Ui', '$1&&&$2$3', $content);
139
140                // I'm sure a true master of regular expressions wouldn't need the previous two steps, and would simply write the replacement expression (below) better. But this works for me.
141       
142                // set the title attribute:
143                if (KBLINKER_USE_TITLES)
144                        $title = ' title="'.$kblinker_title_text['before'].$keyword.$kblinker_title_text['after'].'"';
145               
146                // now that we've taken the keyword out of any links it appears in, let's look for the keyword elsewhere.
147                if ( 1 != $plurals ){    // we do basically the same thing whether we're looking for plurals or not. Let's do non-plurals option first:
148                        $content = preg_replace( '|(?<=[\s>;"\'/])('.$keyword.')(?=[\s<&.,!\'";:\-/])|i', '<a href="'.$url.'" class="kblinker"'.$target.$title.'>$1</a>', $content, 1); // that "1" at the end limits it to replacing the keyword only once per post.
149                        /* some notes about that regular expression to make modifying it easier for you if you're new to these things:
150                        (?<=[\s>;"\'])
151                                (?<=    marks it as a lookbehind assertion
152                                to ensure that we are linking only complete words, we want keyword preceded by one of space, tag (>), entity (;) or certain kinds of punctuation (escaped with \ when necessary)
153                                Note that '&' is NOT one of the allowed lookbehinds (or our '&&&' trick wouldn't work)
154                        (?=[\s<&.,\'";:\-])
155                                (?=     marks this as a lookahead assertion
156                                again, we link only complete words. Must be followed by space, tag (<), entity (&), or certain kinds of punctuation.
157                                Note that some of the punctuations are escaped with \
158                        */
159                }else{  // if they want us to look for plurals too:
160                        // this regex is almost identical to the non-plurals one, we just add an s? where necessary:
161                        $content = preg_replace( '|(?<=[\s>;"\'/])('.$keyword.'s?)(?=[\s<&.,!\'";:\-/])|i', '<a href="'.$url.'" class="kblinker"'.$target.$title.'>$1</a>', $content, 1);       // that "1" at the end limits it to replacing once per post.
162                }
163        }
164        // get rid of our '&&&' things.
165        $content = str_replace( '&&&', '', $content);
166        return $content;
167}
168
169
170function kb_linker_options_page(){
171        $sample = 'wordpress->http://wordpress.org/
172google->http://www.google.com/ _blank
173kb linker->http://adambrown.info/b/widgets/kb-linker/
174knuckleheads->http://www.house.gov/';
175
176        if ( $_POST['kb_linker'] ){
177                $pairs = str_replace("\r", '', $_POST['kb_linker']);
178                $pairs = explode("\n", $pairs);
179                foreach( $pairs as $pair ){
180                        $pair = trim( $pair ); // no leading or trailing spaces. Can mess with the "target" thing in function kb_linker()
181                        $pair = explode( "->", $pair );
182                        if ( ( '' != $pair[0] ) && ( '' != $pair[1] ) )
183                                $new[ $pair[0] ] = $pair[1];
184                }
185                $pairs = $new;  // contains the pairs as an array for use by the filter
186                $text = $_POST['kb_linker'];    // contains the pairs as entered in the form for display below
187               
188                $plurals = ( 1 == $_POST['kb_plurals'] ) ? 1 : 0;
189                $option = array( 'pairs'=>$pairs, 'text'=>$text, 'plurals'=>$plurals ); // store both versions of the option, pairs and text
190                update_option( 'kb_linker', $option );
191                print '<div id="message" class="updated fade"><p><strong>KB Linker options updated.</strong> <a href="'.get_bloginfo('url').'">View site &raquo;</a></p></div>';
192        }else{
193                $option = get_option('kb_linker');
194                if (is_array($option)){
195                        extract($option);
196                }else{
197                        $text = $sample;
198                        $plurals = 0;
199                }
200        }
201
202        $checked = ( 1 == $plurals ) ? 'checked="checked"' : '' ;
203
204        print '
205        <div class="wrap">
206        <h2>KB Linker</h2>
207        <p>KB Linker will link phrases you specify to sites you specify. For example, you could make it so that whenever "Wordpress" occurs in a post it is automatically linked to wordpress.org.</p>
208        <p>Enter your keyword-URL pairs in the box below. Each pair should appear on its own line. Separate each keyword from its respective link with "->". Look at the bottom of this page for important details. Below are a few examples to get you going. Note that the link to Google will open in a new window, since it is followed with "&nbsp;_blank" (note the space).</p>
209        <blockquote><pre>'.$sample.'</pre></blockquote>
210        <p>Alright, knock yourself out:</p>
211       
212        <form method="post" action="http://'.$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'].'">
213        <textarea id="kb_linker" name="kb_linker" rows="10" cols="45" class="widefat">'.$text.'</textarea>
214        <p><input type="checkbox" '.$checked.' name="kb_plurals" id="kb_plurals" value="1" /> Also link the keyword if it ends in <i>s</i> (i.e. plurals in certain languages)</p>
215        <p class="submit" style="width:420px;"><input type="submit" value="Submit &raquo;" /></p>
216        </form>
217       
218        <p>Considerations:</p>
219        <ul>
220                <li>URLs should be valid (i.e. begin with http://)</li>
221                <li>The same URL can appear on more than one line (i.e. with more than one keyword).</li>
222                <li>Because a word can only link to one site, a keyword should not appear on more than one line. If it does, only the last instance of the keyword will be matched to its URL.</li>
223                <li>If one of your keywords is a substring of the other--e.g. "download wordpress" and "wordpress"--then you should list the shorter one later than the first one.</li>
224                <li>Keywords are case-insensitive (e.g. "wordpress" is the same as "WoRdPrEsS").</li>
225                <li>Spaces count, so "wordpress" is not the same as "wordpress ".</li>
226                <li>Keywords will be linked only if they occur in your post as a word (or phrase), not as a partial word. So if one of your keywords is "a" (for some strange reason), it will be linked only when it occurs as the word "a"--when the letter "a" occurs within a word, it will not be linked.</li>
227                <li>You can use any valid target attribute, not just "_blank"--see <a href="http://www.w3schools.com/tags/tag_a.asp">W3C</a> for a list of valid targets.</li>
228        </ul>
229        </div>
230        ';
231}
232
233function kb_linker_admin_page(){
234        add_submenu_page('options-general.php', 'KB Linker', 'KB Linker', 5, 'kb_linker.php', 'kb_linker_options_page');
235}
236
237add_filter('the_content', 'kb_linker', 9); // run before Dagon Design Sitemap Generator (runs at priority 10)
238add_action('admin_menu', 'kb_linker_admin_page');
239?>
Note: See TracBrowser for help on using the browser.