root/link-harvest/trunk/link-harvest.php

Revision 20341, 45.5 kB (checked in by alexkingorg, 9 months ago)

version 1.1 ready for release

Line 
1 <?php
2
3 /*
4 Plugin Name: Link Harvest
5 Plugin URI: http://alexking.org/projects/wordpress
6 Description: This will harvest links from your WordPress database, creating a links list sorted by popularity. Once you have activated the plugin, you can configure the <a href="options-general.php?page=link-harvest.php">Settings</a> and see your <a href="index.php?page=link-harvest.php">list of links</a>. Also see <a href="options-general.php?page=link-harvest.php#aklh_template_tags">how to show the list of links</a> in your blog. Questions on configuration, etc.? Make sure to read the README.
7 Version: 1.1
8 Author: Alex King
9 Author URI: http://alexking.org
10 */
11
12 // Copyright (c) 2006-2007 Alex King. All rights reserved.
13 // http://alexking.org/projects/wordpress
14 //
15 // Released under the GPL license
16 // http://www.opensource.org/licenses/gpl-license.php
17 //
18 // This is an add-on for WordPress
19 // http://wordpress.org/
20 //
21 // **********************************************************************
22 // This program is distributed in the hope that it will be useful, but
23 // WITHOUT ANY WARRANTY; without even the implied warranty of
24 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
25 // **********************************************************************
26
27 @define('AK_WPROOT', '../../');
28
29 @define('AKLH_DEBUG', false);
30
31 if (AKLH_DEBUG) {
32 //    ini_set('display_errors', '1');
33 //    ini_set('error_reporting', E_ALL);
34     $logfile = dirname(__FILE__).'/aklh_log.txt';
35     if (!is_file($logfile) || !is_writeable($logfile)) {
36         die('Debugging is enabled, but there is no wp-content/plugins/aklh_log.txt file or the file is not writeable.');
37     }
38 }
39
40 if (!isset($wpdb)) {
41     require(AK_WPROOT.'wp-blog-header.php');
42 }
43
44 load_plugin_textdomain('link-harvest');
45
46 if (!function_exists('is_admin_page')) {
47     function is_admin_page() {
48         if (function_exists('is_admin')) {
49             return is_admin();
50         }
51         if (function_exists('check_admin_referer')) {
52             return true;
53         }
54         else {
55             return false;
56         }
57     }
58 }
59
60 if (!function_exists('ak_prototype')) {
61     function ak_prototype() {
62         if (!function_exists('wp_enqueue_script')) {
63             global $ak_prototype;
64             if (!isset($ak_prototype) || !$ak_prototype) {
65                 print('
66         <script type="text/javascript" src="'.get_bloginfo('wpurl').'/wp-includes/js/prototype.js"></script>
67                 ');
68             }
69             $ak_prototype = true;
70         }
71     }
72 }
73
74
75 $wpdb->ak_domains = $wpdb->prefix.'ak_domains';
76 $wpdb->ak_linkharvest = $wpdb->prefix.'ak_linkharvest';
77
78 $aklh = new ak_link_harvest;
79
80 // CHECK FOR LINK HARVEST TABLES
81
82 if (isset($_GET['activate']) && $_GET['activate'] == 'true') {
83     $tables = $wpdb->get_col("
84         SHOW TABLES
85     ");
86     if (!in_array($wpdb->ak_linkharvest, $tables) && !in_array($wpdb->ak_domains, $tables)) {
87         $aklh->install();
88     }
89 }
90 $aklh->get_settings();
91
92 class ak_link_harvest {
93     var $exclude;
94     var $table_length;
95     var $harvest_enabled;
96     var $options;
97     var $default_limit;
98     var $timeout;
99     var $token;
100     
101     function ak_link_harvest() {
102         $this->exclude = array();
103         $this->table_length = 100;
104         $this->harvest_enabled = 1;
105         $this->default_limit = 5;
106         $this->timeout = 2;
107         $this->token = 1;
108         $this->options = array(
109             'exclude' => 'explode'
110             , 'table_length' => 'int'
111             , 'harvest_enabled' => 'int'
112             , 'token' => 'int'
113         );
114         $this->excluded_file_extensions = array(
115             '.mov'
116             , '.jpg'
117             , '.gif'
118             , '.png'
119             , '.pdf'
120             , '.mpg'
121             , '.mp3'
122             , '.mpeg'
123             , '.avi'
124             , '.swf'
125             , '.doc'
126             , '.xls'
127             , '.wmv'
128             , '.wmf'
129             , '.wma'
130             , '.txt'
131             , '.m4p'
132         );
133     }
134     
135     function install() {
136         global $wpdb;
137         $result = $wpdb->query("
138             CREATE TABLE `$wpdb->ak_domains` (
139             `id` int(11) NOT NULL auto_increment,
140             `domain` varchar(255) NOT NULL,
141             `title` varchar(255) NULL,
142             `count` int(11) NOT NULL default '0',
143             PRIMARY KEY  (`id`),
144             UNIQUE KEY `domain` (`domain`)
145             )
146         ");
147         $result = $wpdb->query("
148             CREATE TABLE `$wpdb->ak_linkharvest` (
149             `id` int(11) NOT NULL auto_increment,
150             `post_id` int(11) NOT NULL,
151             `url` text NOT NULL,
152             `title` varchar(255) NULL,
153             `domain_id` varchar(255) NOT NULL,
154             `modified` datetime NOT NULL,
155             PRIMARY KEY  (`id`),
156             KEY `post_id` (`post_id`),
157             KEY `domain_id` (`domain_id`)
158             )
159         ");
160         add_option('aklh_exclude', '', 'Ignore links to these domains.');
161         add_option('aklh_table_length', '100', 'Number of domains to show in the links table.');
162         add_option('aklh_harvest_enabled', '1', 'Can we run a link harvest?');
163         add_option('aklh_token', '1', 'Use ###linkharvest### to show the links list?');
164     }
165     
166     function clear_data() {
167         global $wpdb;
168         $wpdb->query("
169             TRUNCATE TABLE $wpdb->ak_domains
170         ");
171         $wpdb->query("
172             TRUNCATE TABLE $wpdb->ak_linkharvest
173         ");
174     }
175     
176     function get_settings() {
177         foreach ($this->options as $option => $type) {
178             $this->$option = get_option('aklh_'.$option);
179             switch ($type) {
180                 case 'explode':
181                     $this->$option = explode(' ', $this->$option);
182                     break;
183                 case 'int':
184                     $this->$option = intval($this->$option);
185                     break;
186             }
187         }
188     }
189     
190     function update_settings() {
191         foreach ($this->options as $option => $type) {
192             if (isset($_POST[$option])) {
193                 switch ($type) {
194                     case 'explode':
195                         $value = stripslashes($_POST[$option]);
196                         break;
197                     case 'int':
198                         $value = intval($_POST[$option]);
199                         break;
200                     default:
201                         $value = stripslashes($_POST[$option]);
202                 }
203                 update_option('aklh_'.$option, $value);
204             }
205         }
206
207         header('Location: '.get_bloginfo('wpurl').'/wp-admin/options-general.php?page=link-harvest.php&updated=true');
208         die();
209     }
210     
211     function excluded_file_type($link) {
212         foreach ($this->excluded_file_extensions as $ext) {
213             if (substr($link, strlen($ext) * -1) == $ext) {
214                 return true;
215             }
216         }
217         return false;
218     }
219     
220     function get_links($body) {
221 // From: http://www.onaje.com/php/article.php4/46
222         $href_regex ="<";            // 1 start of the tag
223         $href_regex .="\s*";         // 2 zero or more whitespace
224         $href_regex .="a";           // 3 the a of the <a> tag itself
225         $href_regex .="\s+";         // 4 one or more whitespace
226         $href_regex .="[^>]*";       // 5 zero or more of any character that is _not_ the end of the tag
227         $href_regex .="href";        // 6 the href bit of the tag
228         $href_regex .="\s*";         // 7 zero or more whitespace
229         $href_regex .="=";           // 8 the = of the tag
230         $href_regex .="\s*";         // 9 zero or more whitespace
231         $href_regex .="[\"']?";      // 10 none or one of " or '
232         $href_regex .="(";           // 11 opening parenthesis, start of the bit we want to capture
233         $href_regex .="[^\"' >]+";   // 12 one or more of any character _except_ our closing characters
234         $href_regex .=")";           // 13 closing parenthesis, end of the bit we want to capture
235         $href_regex .="[\"' >]";     // 14 closing chartacters of the bit we want to capture
236         
237         $regex  = "/";         // regex start delimiter
238         $regex .= $href_regex; //
239         $regex .= "/";         // regex end delimiter
240         $regex .= "i";         // Pattern Modifier - makes regex case insensative
241         $regex .= "s";         // Pattern Modifier - makes a dot metacharater in the pattern
242                     // match all characters, including newlines
243         $regex .= "U";         // Pattern Modifier - makes the regex ungready
244
245         $urls = array();
246
247         if (preg_match_all($regex, $body, $links)) {
248             foreach ($links[1] as $link) {
249                 if (in_array(substr($link, 0, 7), array('http://', 'https:/')) && !$this->excluded_file_type($link)) {
250                     $urls[] = trim($link);
251                 }
252             }
253         }
254         
255         return $urls;
256     }
257     
258     function get_domain_id($domain) {
259         global $wpdb;
260         $domain_id = $wpdb->get_var("
261             SELECT id
262             FROM $wpdb->ak_domains
263             WHERE domain = '$domain'
264         ");
265         if ($domain_id && !is_null($omain_id)) {
266             return $domain_id;
267         }
268         else {
269             $id = $this->add_domain($domain);
270             if ($id != false) {
271                 return $id;
272             }
273         }
274         return false;
275     }
276
277     function get_domain_ids($domains) {
278         global $wpdb;
279         if (!is_array($domains) || count($domains) == 0) {
280             return false;
281         }
282         $domain_ids = array();
283         $results = $wpdb->get_results("
284             SELECT id, domain
285             FROM $wpdb->ak_domains
286             WHERE domain IN ('".implode("', '", $domains)."')
287         ");
288         if ($results && count($results) > 0) {
289             foreach ($results as $data) {
290                 $domain_ids[$data->domain] = $data->id;
291             }
292         }
293         $missing = array();
294         foreach ($domains as $domain) {
295             if (!isset($domain_ids[$domain])) {
296                 $domain_ids[$domain] = $this->add_domain($domain);
297             }
298         }
299         return $domain_ids;
300     }
301     
302     function process_content($content, $post_id) {
303         $links = $this->get_links($content);
304         if ($links == false) {
305             return;
306         }
307         
308         aklh_log('Found '.count($links).' links in post id: '.$post_id);
309
310         $domains = array();
311         $domain_count = array();
312         $harvest = array();
313         
314         foreach ($links as $link) {
315 // FeedBurner hack, working around this:
316 // http://alexking.org/blog/2006/12/01/why-i-dont-use-feedburner
317             if (!empty($link)) {
318                 if (strstr($link, '/feeds.feedburner.com/') || strstr($link, '/~r/')) {
319                     require_once(ABSPATH.WPINC.'/class-snoopy.php');
320                     $snoop = new Snoopy;
321                     $snoop->maxlength = 2000;
322                     $snoop->read_timeout = $this->timeout;
323                     $snoop->fetch($link);
324                     if (!empty($snoop->lastredirectaddr)) {
325                         $link = $snoop->lastredirectaddr;
326                     }
327                 }
328                 $domain = $this->get_domain($link);
329                 if (!empty($domain)) {
330                     foreach ($this->exclude as $exclude) {
331                         if (strstr($domain, $exclude)) {
332                             return;
333                         }
334                     }
335                     if (isset($domain_count[$domain])) {
336                         $domain_count[$domain]++;
337                     }
338                     else {
339                         $domains[] = $domain;
340                         $domain_count[$domain] = 1;
341                     }
342                     $harvest[] = array($link, $domain);
343                 }
344
345                 aklh_log('Processed link: '.$link);
346
347             }
348             else {
349
350                 aklh_log('Skipped link: '.$link);
351
352             }
353         }
354         
355         if (count($domains) > 0) {
356             $domain_ids = $this->get_domain_ids($domains);
357
358             foreach ($harvest as $data) {
359                 $link = $data[0];
360                 $domain = $data[1];
361                 $this->add_link($link, $domain_ids[$domain], $post_id);
362             }
363             
364             foreach ($domain_ids as $domain => $domain_id) {
365                 $this->set_domain_counter(null, $domain_id, $domain_count[$domain], '+');
366             }
367         }
368
369     }
370     
371     function add_link($url, $domain_id, $post_id) {
372
373         aklh_log('About to add link for post id: '.$post_id.' - '.$url);
374
375         global $wpdb;
376         $title = stripslashes($this->get_page_title($url));
377         $result = $wpdb->query("
378             INSERT INTO $wpdb->ak_linkharvest
379             ( post_id
380             , url
381             , title
382             , domain_id
383             , modified
384             )
385             VALUES
386             ( '".mysql_real_escape_string($post_id)."'
387             , '".mysql_real_escape_string($url)."'
388             , '".mysql_real_escape_string($title)."'
389             , '".mysql_real_escape_string($domain_id)."'
390             , '".current_time('mysql',1)."'
391             )
392         ");
393         if (!$result) {
394
395             aklh_log('Failed to add link for post id: '.$post_id.' - '.$url);
396
397             return false;
398         }
399         else {
400
401             aklh_log('Added link for for post id: '.$post_id.' - '.$url);
402
403             return true;
404         }
405     }
406     
407     function add_domain($domain) {
408         global $wpdb;
409         $title = stripslashes($this->get_page_title('http://'.$domain));
410         $result = $wpdb->query("
411             INSERT INTO $wpdb->ak_domains
412             ( domain
413             , title
414             , count
415             )
416             VALUES
417             ( '".mysql_real_escape_string($domain)."'
418             , '".mysql_real_escape_string($title)."'
419             , '0'
420             )
421         ");
422         if (!$result) {
423
424             aklh_log('Failed to add domain: '.$domain);
425
426             return false;
427         }
428         else {
429
430             aklh_log('Added domain: '.$domain);
431
432             return mysql_insert_id();
433         }
434     }
435     
436     function set_domain_counter($domain = null, $domain_id = null, $count, $mod = '+') {
437         global $wpdb;
438         if (!is_null($domain)) {
439             $where = " WHERE domain = '".mysql_real_escape_string($domain)."' ";
440         }
441         else if (!is_null($domain_id)) {
442             $where = " WHERE id = '".intval($domain_id)."' ";
443         }
444         if (isset($where)) {
445             $result = $wpdb->query("
446                 UPDATE $wpdb->ak_domains
447                 SET count = count $mod ".intval($count)."
448                 $where
449             ");
450             if ($result != false) {
451                 return true;
452             }
453         }
454         return false;
455     }
456     
457     function harvest_posts($post_ids = array(), $start = 0, $limit = null, $delete_old = false) {
458         global $wpdb;
459         if (is_array($post_ids) && count($post_ids) > 0) {
460             if ($delete_old) {
461                 foreach ($post_ids as $post_id) {
462                     $links = $wpdb->get_results("
463                         SELECT *
464                         FROM $wpdb->ak_linkharvest
465                         WHERE post_id = $post_id
466                     ");
467                     if (count($links) > 0) {
468                         $domains = array();
469                         foreach ($links as $link) {
470                             if (!isset($domains[$link->domain_id])) {
471                                 $domains[$link->domain_id] = 1;
472                             }
473                             else {
474                                 $domains[$link->domain_id]++;
475                             }
476                         }
477                         if (count($domains) > 0) {
478                             foreach ($domains as $id => $count) {
479                                 $update = $wpdb->query("
480                                     UPDATE $wpdb->ak_domains
481                                     SET count = count - $count
482                                     WHERE id = $id
483                                 ");
484                             }
485                         }
486                         $delete = $wpdb->query("
487                             DELETE
488                             FROM $wpdb->ak_linkharvest
489                             WHERE post_id = $post_id
490                         ");
491                     }
492                 }
493             }
494             $posts = $wpdb->get_results("
495                 SELECT *
496                 FROM $wpdb->posts
497                 WHERE (
498                     post_status = 'publish'
499                     OR post_status = 'static'
500                 )
501                 AND ID IN (".implode(',', $post_ids).")
502             ");
503         }
504         else {
505             if ($start == 0) {
506                 $this->clear_data();
507             }
508             if (is_null($limit)) {
509                 $limit = $this->default_limit;
510             }
511             $posts = $wpdb->get_results("
512                 SELECT *
513                 FROM $wpdb->posts
514                 WHERE (
515                     post_status = 'publish'
516                     OR post_status = 'static'
517                 )
518                 AND post_content LIKE '%http://%'
519                 ORDER BY ID
520                 LIMIT $start, $limit
521             ");
522         }
523         foreach ($posts as $post) {
524
525             aklh_log('== Start processing post id: '.$post->ID);
526
527             $this->process_content($post->post_content, $post->ID);
528             $external = get_post_meta($post->ID, 'external_link', true);
529             if (!empty($external