source: wordpress/plugins/owark/owark.php @ f907af8

Revision f907af8, 19.9 KB checked in by Eric van der Vlist <vdv@dyomedea.com>, 8 years ago (diff)

Fixing #9

  • Property mode set to 100644
Line 
1<?php
2/*  Copyright 2011 Eric van der Vlist (vdv@dyomedea.com)
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2, as
6    published by the Free Software Foundation.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16*/
17
18/*
19Plugin Name: owark
20Plugin URI: http://owark.org
21Description: Tired of broken links? Archive yours with owark, the Open Web Archive!
22Version: 0.1
23Author: Eric van der Vlist
24Author URI: http://eric.van-der-vlist.com
25License: GLP2
26*/
27
28
29if (!class_exists("Owark")) {
30        class Owark {
31
32        private $broken_links = array();
33        private $post_id = -1;
34        private $post_type = "";
35        private $version = '0.2';
36        private $notices = "";
37       
38        /**
39         * Class constructor
40         *
41         * @package owark
42         * @since 0.1
43         *
44         *
45         */
46                function Owark() {
47
48
49            if (is_admin()) {
50                add_action('admin_menu', array($this, 'owark_admin_menu'));
51                add_action('plugins_loaded', array($this, 'sanity_checks'));
52            }
53
54            // See http://stackoverflow.com/questions/2210826/need-help-with-wp-rewrite-in-a-wordpress-plugin
55            // Using a filter instead of an action to create the rewrite rules.
56            // Write rules -> Add query vars -> Recalculate rewrite rules
57            add_filter('rewrite_rules_array', array($this, 'create_rewrite_rules'));
58            add_filter('query_vars',array($this, 'add_query_vars'));
59
60            // Recalculates rewrite rules during admin init to save resources.
61            // Could probably run it once as long as it isn't going to change or check the
62            // $wp_rewrite rules to see if it's active.
63            add_filter('admin_init', array($this, 'flush_rewrite_rules'));
64            add_action( 'template_redirect', array($this, 'template_redirect_intercept') );
65
66            add_filter ( 'the_content', array($this, 'content_filter'));
67            add_filter ( 'comment_text', array($this, 'comment_filter'));
68            add_filter ( 'get_comment_author_link', array($this, 'comment_filter'));
69
70            add_action('owark_schedule_event', array(Owark, 'schedule'));
71            if ( !wp_next_scheduled( 'owark_schedule_event', array('occurrences' => 30) ) ) {
72                        wp_schedule_event(time(), 'hourly', 'owark_schedule_event', array('occurrences' => 30));
73                }
74
75
76                }
77
78        /**
79         * Check we have everything we need...
80         *
81         * @package owark
82         * @since 0.1
83         *
84         *
85         */
86        function sanity_checks(){
87
88            // Install or upgrade tables if needed
89
90            $installed_ver = get_option( "owark_db_version" );
91            if ($installed_ver != $this->version) {
92                global $wpdb;
93                $table = $wpdb->prefix."owark";
94                $sql = "CREATE TABLE $table (
95                    id int(10) unsigned NOT NULL AUTO_INCREMENT,
96                    url text NOT NULL,
97                    status varchar(20) NOT NULL DEFAULT 'to-archive',
98                    arc_date datetime,
99                    arc_location text,
100                    encoding varchar(10),
101                    PRIMARY KEY(`id`),
102                    KEY `url` (`url`(150)) )";
103                require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
104                dbDelta($sql);
105
106                update_option( "owark_db_version", $this->version );
107                $this->notices = "<div class=\"updated fade\"><p><strong>The owark table has been installed or upgraded to version {$this->version}</strong></p></div>";
108            }
109
110            // Check that the broken link checker is installed
111            if (!function_exists('get_plugins'))
112                                require_once (ABSPATH."wp-admin/includes/plugin.php");
113
114            $blc = 'not-found';
115            foreach(get_plugins() as $plugin_file => $plugin_data) {
116                if ($plugin_data['Title'] == 'Broken Link Checker') {
117                    if (is_plugin_active($plugin_file)) {
118                        $blc = 'active';
119                    } else {
120                        $blc = 'inactive';
121                    }
122                }
123                }
124
125            if ($blc == 'inactive') {
126                 $this->notices = $this->notices . "<div class=\"updated fade\"><p><strong>Please activate the Broken Link Checker so that the Open Web Archive can be fully functional.</strong></p></div>";
127            } else if ($blc == 'not-found') {
128                 $this->notices = $this->notices . "<div class=\"error fade\"><p><strong>The Open Web Archive relies on the <a href=\"http://w-shadow.com/blog/2007/08/05/broken-link-checker-for-wordpress/\">Broken Link Checker</a>. Please install this plugin!</strong></p></div>";
129            }
130
131            // Check if we have an archive subdirectory
132
133            if (!is_dir(dirname(__FILE__) . '/archives')) {
134                @mkdir(dirname(__FILE__) . '/archives');
135                if (!is_dir(dirname(__FILE__) . '/archives')) {
136                    $this->notices = $this->notices . "<div class=\"error fade\"><p><strong>The Open Web Archive has not been able to create the folder /archives in its installation directory. Please create it by hand and make it writable for the web server.</strong></p></div>";                       
137                }
138            }
139
140            // Check that we can execute commands
141
142            if ( ini_get('disable_functions') ) {
143                $not_allowed = ini_get('disable_functions');
144                if ( stristr($not_allowed, 'exec') ) {
145                    $this->notices = $this->notices . "<div class=\"error fade\"><p><strong>The Open Web Archives requires that exec() is allowed to run wget and retrieve the pages to archive.</strong></p></div>";
146               }
147            }
148
149            // Check that wget is installed
150
151            $output = array();
152            exec('/usr/bin/wget -V', &$output);
153
154
155            if ( empty($output) ) {
156                $this->notices = $this->notices . "<div class=\"error fade\"><p><strong>The Open Web Archives is not able to run wget and retrieve the pages to archive. Please check that wget is installed and on the default path.</strong></p></div>";
157            }
158
159            // We need as least version 1.11 or higher
160            $helper = preg_match('/GNU Wget ([0-9\.]+) /', $output[0], $wget_version);
161            if ( $wget_version[1] < '1.11' ) {
162                $this->notices = $this->notices . "<div class=\"error fade\"><p><strong>The Open Web Archives needs wget version 1.11 or higher.</strong><br />Version read: {$wget_version[0]}</p></div>";
163            }
164
165            if ($this->notices != '') {
166                add_action('admin_notices', array($this, 'admin_notices'));
167             }
168
169        }
170
171        /**
172         * Show admin notices
173         *
174         * @package owark
175         * @since 0.1
176         *
177         *
178         */
179        function admin_notices(){
180
181            echo $this->notices;
182
183        }
184
185        /**
186         * Admin menus
187         *
188         * @package owark
189         * @since 0.1
190         *
191         *
192         */
193        function owark_admin_menu() {
194            add_management_page(__('The Open Web Archive', 'owark'), __('Web Archive', 'owark'), 'edit_others_posts', 'owark', array($this, 'management_page'));
195        }
196
197        /**
198         * URL of an archive page
199         *
200         * @package owark
201         * @since 0.1
202         *
203         *
204         */
205        function get_archive_url($archive_id) {
206            return home_url().'/owark/'.$archive_id;
207        }
208
209        /**
210         * Display the admin/tools page.
211         *
212         * @package owark
213         * @since 0.1
214         *
215         *
216         */
217        function management_page() {
218            //must check that the user has the required capability
219            if (!current_user_can('edit_others_posts')) {
220                wp_die( __('You do not have sufficient permissions to access this page.') );
221            }
222
223            global $wpdb;
224
225            echo '<div class="wrap">';
226            screen_icon();
227            echo '<h2>Owark - The Open Web Archive</h2>';
228            echo '<p><em>Tired of broken links? Archive yours with the Open Web Archive!</em></p>';
229            echo "</div>";
230
231            echo '<p>List of broken links with archived pages:</p>';
232
233            $query = "SELECT owark.id, owark.url, owark.status, owark.arc_date, owark.arc_location, blc_links.status_text
234                        FROM {$wpdb->prefix}owark AS owark, {$wpdb->prefix}blc_links as blc_links
235                        WHERE owark.url = blc_links.final_url COLLATE latin1_swedish_ci and blc_links.broken = 1
236                        ORDER BY owark.url";
237            $results = $wpdb->get_results($query);
238
239            echo '<table class="widefat">';
240            echo '<thead>';
241            echo '<tr>';
242            echo '<th>URL</th>';
243            echo '<th>Archive</th>';
244            echo '</tr>';
245            echo '</thead>';
246            echo '<tbody>';
247
248            foreach ($results as $link) {
249                $archive_url = $this->get_archive_url($link->id);
250                echo "<tr>
251                        <td><a href=\"{$link->url}\" target='_blank'>{$link->url}</a></td>
252                        <td><a href=\"{$archive_url}\" target='_blank'>{$link->arc_date}</a></td>
253                    </tr>";
254            }
255
256            echo '</tbody>';
257            echo '</table>';
258
259
260        }
261
262        /**
263         * Add a rewrite rule to display archive pages
264         *
265         * @package owark
266         * @since 0.1
267         *
268         *
269         */
270        function create_rewrite_rules($rules) {
271            global $wp_rewrite;
272            $newRule = array('owark/(.+)' => 'index.php?owark='.$wp_rewrite->preg_index(1));
273            $newRules = $newRule + $rules;
274            return $newRules;
275        }
276
277        /**
278         * Add a query variable used to display archive pages
279         *
280         * @package owark
281         * @since 0.1
282         *
283         *
284         */
285        function add_query_vars($qvars) {
286            $qvars[] = 'owark';
287            return $qvars;
288        }
289
290        /**
291         * Title says it all ;) ...
292         *
293         * @package owark
294         * @since 0.1
295         *
296         *
297         */
298        function flush_rewrite_rules() {
299            global $wp_rewrite;
300            $wp_rewrite->flush_rules();
301        }
302
303        /**
304         * Intercepts archive pages.
305         *
306         * @package owark
307         * @since 0.1
308         *
309         *
310         */
311        function template_redirect_intercept() {
312            global $wp_query;
313            if ($wp_query->get('owark')) {
314                $this->display_archive($wp_query->get('owark'));
315                exit;
316            }
317        }
318
319        /**
320         * Filter to replace broken links in comments.
321         *
322         * @package owark
323         * @since 0.1
324         *
325         *
326         */
327        function content_filter($content) {
328            global $post;
329            return $this->link_filter($content, $post->ID, $post->post_type);
330        }
331
332        /**
333         * Filter to replace broken links in comments.
334         *
335         * @package owark
336         * @since 0.1
337         *
338         *
339         */
340        function comment_filter($content) {
341            return $this->link_filter($content, get_comment_ID(), 'comment');
342        }
343
344        /**
345         * Generic filter to replace broken links in content.
346         *
347         * @package owark
348         * @since 0.1
349         *
350         *
351         */
352        function link_filter($content, $post_id, $post_type) {
353
354            global $wpdb;
355
356            // See if we haven't already loaded the broken links for this post...
357            if ($this->post_id != $post_id || $this->post_type != $post_type) {
358
359                $this->post_id =  $post_id;
360                $this->post_type = $post_type;
361
362                //Retrieve info about all occurrences of broken links in the current post
363                //which happens for comments (they have links to check in 2 different filters)
364                $q = "
365                    SELECT instances.raw_url, owark.id
366                    FROM {$wpdb->prefix}blc_instances AS instances,
367                        {$wpdb->prefix}blc_links AS links,
368                        {$wpdb->prefix}owark AS owark
369                    WHERE
370                        instances.link_id = links.link_id
371                        AND owark.url = links.final_url COLLATE latin1_swedish_ci
372                        AND instances.container_id = %s
373                        AND instances.container_type = %s
374                        AND links.broken = 1
375                ";
376                $q = $wpdb->prepare($q, $this->post_id, $this->post_type);
377                $results = $wpdb->get_results($q);
378
379                $this->broken_links = array();
380
381                foreach ($results as $link) {
382                    $this->broken_links[$link->raw_url] = $link->id;
383                }
384
385            }
386
387
388            if (empty($this->broken_links)) {
389                return $content;
390            }
391
392            // Regexp : see http://stackoverflow.com/questions/2609095/hooking-into-comment-text-to-add-surrounding-tag
393            return preg_replace_callback('/(<a.*?href\s*=\s*["\'])([^"\'>]+)(["\'][^>]*>.*?<\/a>)/si', array( $this, 'replace_a_link'), $content);
394        }
395
396        /**
397         * Replace a link.
398         *
399         * @package owark
400         * @since 0.1
401         *
402         *
403         */
404        function replace_a_link($matches) {
405            if (array_key_exists($matches[2], $this->broken_links)) {
406                return $matches[1].$this->get_archive_url($this->broken_links[$matches[2]]).$matches[3];
407            } else {
408                return $matches[0];
409            }
410        }
411
412
413        /**
414         * Display an archive page
415         *
416         * @package owark
417         * @since 0.1
418         *
419         *
420         */
421        function display_archive($parameter) {
422
423            global $wpdb;
424
425            $id = intval($parameter);
426
427            $query = "SELECT *
428                        from {$wpdb->prefix}owark AS owark
429                        where id = {$id}";
430            $link = $wpdb->get_row($query);
431            $wpdb->flush();
432
433            // Find the file to read
434            $blog_title = get_bloginfo('name');
435            $home_url = home_url();
436
437            $loc = "";
438            if( ($pos = strpos($link->arc_location, '/archives')) !== FALSE )
439                $loc = '/wp-content/plugins/owark' . substr($link->arc_location, $pos);
440            $arc_loc = home_url() . $loc;
441
442            // The file name is either index.html or guessed from the URL
443            if ($home_url[strlen($home_url)] == '/') {
444                $file_location = '.'. $loc .'/index.html';
445            } else {
446                $parts = str_split($home_url, '/');
447                $file_location = '.'. $loc . $parts[count($parts)] . '.html';
448            }
449
450            if (!file_exists($file_location)) {
451                // If index.html doesn't exist, find another html file!
452                $dir = opendir('.'.$loc);
453                if ($dir) {
454                    while (false !== ($file = readdir($dir))) {
455                        if ('.html' === substr($file, strlen($file) - 5)) {
456                            $file_location = '.'.$loc.'/' . $file;
457                            break;
458                        }
459                    }
460                    closedir($dir);
461                }
462            }
463
464            // Read the file
465
466            if (file_exists($file_location)) {
467                $f = fopen($file_location, "r");
468                $content = fread($f, filesize($file_location));
469                fclose($f);
470            } else {
471                $content = 'Archive not found';
472            }
473
474            // Which encoding?
475            $encoding = $link->encoding;
476
477            if ($encoding == NULL) {
478                // We need to guess the encoding!
479
480                $matches = NULL;
481                // <meta http-equiv="Content-Type" content="text/xml; charset=iso-8859-1"/>
482                if (preg_match('/<meta\s*http-equiv\s*=\s*["\']Content-Type["\']\s+content\s*=\s*["\'][^"\'>]*charset\s*=\s*([^"\'>]+)\s*["\']/si',
483                        $content, &$matches) > 0) {
484                    $encoding = $matches[1];
485                } else {
486                    $encoding = mb_detect_encoding($content);
487                }
488
489                if ($encoding) {
490                    $wpdb->update(
491                        "{$wpdb->prefix}owark",
492                        array('encoding' => $encoding),
493                        array('id' => $id));
494                }
495            }
496
497            header("Content-Type: text/html; charset=$encoding");
498
499            echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
500        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
501<meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'">';
502
503            echo "<base href=\"{$arc_loc}/\">";
504            echo '<div style="background:#fff;border:1px solid #999;margin:-1px -1px 0;padding:0;">';
505            echo '<div style="background:#ddd;border:1px solid #999;color:#000;font:13px arial,sans-serif;font-weight:normal;margin:12px;padding:8px;text-align:left">';
506            echo "This is an <a href='http://owark.org'>Open Web Archive</a> archive of <a href=\"{$link->url}\">{$link->url}</a>.";
507            echo "<br />This snapshot has been taken on {$link->arc_date} for the website <a href=\"{$home_url}\">{$blog_title}</a> which contains a link to this page and has saved a copy to be displayed in the page ever disappears.";
508            echo '</div></div><div style="position:relative">';
509
510
511             $f = fopen($file_location, "r");
512             echo $content;
513             echo '</div>';
514
515          }
516
517        /**
518         * Check if we've got something to archive
519         *
520         * @package owark
521         * @since 0.1
522         *
523         *
524         */
525        public static function schedule($occurrences) {
526
527            $archiving  = get_option( 'owark_archiving', false);
528            if (! $archiving) {
529                update_option('owark_archiving', true);
530            } else {
531                return;
532            }
533            global $wpdb;
534
535            $query = "SELECT DISTINCT final_url from {$wpdb->prefix}blc_links
536                        WHERE final_url NOT IN (SELECT url COLLATE latin1_swedish_ci FROM {$wpdb->prefix}owark)
537                        AND broken=0
538                        AND final_url!=''";
539            $url = $wpdb->get_row($query);
540            $wpdb->flush();
541
542            if ($url != NULL) {
543                $date = date('c');
544                $relpath = '/archives/'. str_replace('%2F', '/', urlencode(preg_replace('/https?:\/\//', '', $url->final_url))) . '/' . $date;
545                $path = dirname(__FILE__).$relpath;
546                //mkdir($path, $recursive=true);                                           
547
548                $output = array();
549                $status = 0;
550                exec("wget -t3 -E -H -k -K -p -nd -nv --timeout=60 --user-agent=\"Mozilla/5.0 (compatible; owark/0.1; http://owark.org/)\" -P $path {$url->final_url}",
551                    &$output, &$status);
552
553                $q = $wpdb->insert("{$wpdb->prefix}owark", array(
554                    'url' => $url->final_url,
555                    'status' => $status,
556                    'arc_date' => $date,
557                    'arc_location' => $relpath));
558
559                if ($occurrences > 0) {
560                    wp_schedule_single_event(time() + 90, 'owark_schedule_event', array('occurrences' => $occurrences - 1));   
561                }
562
563            }
564            delete_option('owark_archiving');
565        }
566
567
568
569
570        }
571
572
573}
574
575
576if (class_exists("Owark")) {
577        $owark = new Owark();
578}
579
580
581
582?>
Note: See TracBrowser for help on using the repository browser.