PageRenderTime 41ms CodeModel.GetById 15ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/web/public_html/wp-content/plugins/wumii-related-posts/wumii-related-posts.php

https://github.com/allengaller/zenx-news
PHP | 703 lines | 499 code | 116 blank | 88 comment | 89 complexity | 29174aba6275fe011c1db46d0ca334cd MD5 | raw file
  1<?php
  2/*
  3Plugin Name: 无觅相关文章插件
  4Plugin URI: http://wordpress.org/extend/plugins/wumii-related-posts/
  5Author: Wumii Team
  6Version: 1.0.5.7
  7Author URI: http://www.wumii.com
  8Description: 利用数据挖掘的技术,智能匹配相关文章,并以图片形式展示。
  9 
 10Copyright 2010 wumii.com (email: team[at]wumii.com)
 11*/
 12
 13if (!class_exists('WumiiRelatedPosts')) {
 14    class WumiiRelatedPosts {
 15        /*
 16         * !!! You must do the two things before you try to do local test with this plugin:
 17         * 1. turn on WUMII_DEBUG mode
 18         * 2. change WUMII_SERVER to "http://{computer-name}:8080"
 19         *    {computer-name} is the name of computer which running a local test wumii web server.
 20         */
 21        const WUMII_DEBUG = false;
 22        const WUMII_SERVER = 'http://widget.wumii.cn';
 23
 24        const VERSION = '1.0.5.7';
 25        const ADMIN_OPTION_NAME = 'wumii_admin_options';
 26        const PLUGIN_PATH = '/wp-content/plugins/wumii-related-posts';
 27        
 28        // admin option names
 29        const NUM_POSTS = 'num_posts';
 30        const SINGLE_PAGE_ONLY = 'single_page_only';
 31        const DISPLAY_IN_FEED = 'display_in_feed';
 32        const ENABLE_CUSTOM_POS = 'enable_custom_pos';
 33        const SCRIPT_IN_FOOTER = 'script_in_footer';
 34        const WUMII_COMMENT_PLUGIN_ENABLED = 'wumii_comment_plugin_enabled';
 35        
 36        // this containter is a stack and its size is 1.
 37        const NOTIFY_MESSAGE_CONTAINER = 'wumii_notify_message_container';
 38        
 39        const VERIFICATION_CODE = 'wumii_verification_code';
 40        
 41        private $sitePrefix;
 42        
 43        private $adminOptions;
 44        private $excludePostIds = array();
 45        private $lastRssRequestFailedTime;
 46        private $canFilledRssContentPostIds = array();
 47        
 48        // PHP 5 style constructor
 49        function __construct() {
 50            $this->WumiiRelatedPosts();
 51        }
 52        
 53        // treated as regular method since PHP 5.3.3
 54        // PHP 4 style constructor
 55        function WumiiRelatedPosts() {
 56            $this->resetAdminOptions();
 57            
 58            $persistentOptions = get_option(self::ADMIN_OPTION_NAME);
 59            if (is_array($persistentOptions)) {
 60                foreach ($this->adminOptions as $key => $value) {
 61                    if (array_key_exists($key, $persistentOptions)) {
 62                        $this->adminOptions[$key] = $persistentOptions[$key];
 63                    }
 64                }
 65            }
 66            
 67            // get_bloginfo('url') deprecated since WordPress 3.0.0.
 68            $this->sitePrefix = function_exists('home_url') ? home_url() : get_bloginfo('url');
 69            $this->replaceCommentBox();
 70        }
 71        
 72        function getOrigTitle() {
 73            global $post;
 74            return isset($post->post_title) ? $post->post_title : '';
 75        }
 76        
 77        function showNotifyMessage() {
 78            $notifyMessage = $this->popNotifyMessage();
 79            if ($notifyMessage) {
 80                $this->printHtmlNotifyMessage(array($notifyMessage));
 81            }
 82        }
 83        
 84        function registerOptionsPage () {
 85            add_options_page('无觅相关文章插件', '无觅', 'activate_plugins', basename(__FILE__), array($this, 'printAdminPage'));
 86        }
 87        
 88        function addPluginActionLinks($links, $file) {
 89            if ($file == plugin_basename(__FILE__)) {
 90                $links[] = '<a href="options-general.php?page=wumii-related-posts.php">' . __('Settings') . '</a>';
 91            }
 92            return $links;
 93        }
 94        
 95        function addToRssContent($content) {
 96            $newContent = $content;
 97            if ($this->getAdminOption(self::DISPLAY_IN_FEED) && is_feed()) {
 98                global $post;
 99                
100                // 1. Both "rss excerpt mode" and "rss full text mode" will call the_excerpt_rss()
101                //    function first to generate feed description.
102                // 2. "the_excerpt_rss" apply filters process:
103                //         (1. Only run when empty(post->excerpt) && post_password_required(post))
104                //            the_content (Get the whole content and then extract excerpt.
105                //                      We shouldn't add related items here since the content will
106                //                      be processed by wp_trim_excerpt() later) ->
107                //         (2)
108                //            the_excerpt_rss (Here will add our related items content).
109                // 3. "rss full text mode" applies the filters on "the_content" hook again after
110                //    the_excerpt_rss() to generate the feed content. However this time we should
111                //    add related items content.
112                // 4. Because wordpress 2.5.x don't have function post_password_required and we have
113                //    to do a lot to check if the current user is admin, we don't show rss related items
114                //    if post excerpt is empty and it's a password required post.
115                if (!has_excerpt() && !in_array($post->ID, $this->canFilledRssContentPostIds)) {
116                    $this->canFilledRssContentPostIds[] = $post->ID;
117                } else {
118                    $relatedItemsHtml = $this->getRelatedItemsHtml();
119                    if ($relatedItemsHtml) {
120                        $newContent .= $relatedItemsHtml;
121                    }
122                }
123            }
124            
125            return $newContent;
126        }
127        
128        private function getRelatedItemsHtml() {
129            // Don't do anything for 30 seconds if last request failed.
130            if (isset($this->lastRssRequestFailedTime) &&
131                time() - $this->lastRssRequestFailedTime < 30) {
132                return;
133            }
134            
135            $encodedUrl = urlencode(get_permalink());
136            $encodedTitle = urlencode($this->getOrigTitle());
137            $num = $this->getAdminOption(self::NUM_POSTS);
138            $encodedSitePrefix = urlencode($this->sitePrefix);
139            
140            global $wp_version;
141            $path = "/ext/relatedItemsRss?type=1&url=$encodedUrl&title=$encodedTitle&num=$num&sitePrefix=$encodedSitePrefix&mode=3&v=" . self::VERSION . "&pf=WordPress$wp_version";
142            $relatedItemsHtml = $this->httpGet(self::WUMII_SERVER, $path);
143            
144            if ($relatedItemsHtml) {
145                return $relatedItemsHtml;
146            } else {
147                $this->lastRssRequestFailedTime = time();
148            }
149        }
150        
151        private function httpGet($server, $path) {
152            global $wp_version;
153            $response = '';
154            if (function_exists('wp_remote_get')) {    // wp_remote_get function was added in WordPress 2.7
155                $addr = $server . $path;
156                
157                // Default: method: GET, timeout: 5s, redirection: 5, httpversion: 1.0, blocking: true, body: null, cookies: array()
158                $args = array(
159                    'user-agent' => apply_filters('http_headers_useragent', 'WordPress/' . $wp_version . '; ' . $_SERVER['HTTP_USER_AGENT']),
160                    // TODO: use batch fetch related items instead of lower the timeout.
161                    'timeout' => 1
162                );
163                $raw_response = wp_remote_get($addr, $args);
164                
165                if (!is_wp_error($raw_response) && 200 == $raw_response['response']['code']) {
166                    $response = trim($raw_response['body']);
167                }
168            }
169            
170            // For:
171            // 1. WordPress version < 2.7
172            // 2. For some reason the client php is misconfigurated, the wp_remote_get function can not work properly.
173            //    Currently know wp_remote_get($url, $args) will crash if wordpress do request with WP_Http_ExtHTTP(in /wp-includes/http.php) and the PECL extension is not bundled with PHP.
174            // (empty($response) === true) if match one of the above reasons.
175            if (empty($response) && function_exists('fsockopen')) {
176                $urlComponents = parse_url($server);
177                $host = $urlComponents['host'] . ($urlComponents['port'] ? ':' . $urlComponents['port'] : '');
178                
179                $http_request  = "GET $path HTTP/1.0\r\n";
180                $http_request .= "Host: $host\r\n";
181                $http_request .= "Content-Type: application/x-www-form-urlencoded; charset=" . get_option('blog_charset') . "\r\n";
182                $http_request .= 'User-Agent: WordPress/' . $wp_version . '; ' . $_SERVER['HTTP_USER_AGENT'] . "\r\n\r\n";
183                
184                $answer = '';
185                // The connection timeout is 1 second.
186                // TODO: recover this to 5s and remove stream_set_timeout() setting when we support betch fetch related items.
187                if( false != ($fs = @fsockopen($host, 80, $errno, $errstr, 1)) && is_resource($fs)) {
188                    // The timeout for reading data over the socket is 1 second.
189                    stream_set_timeout($fs, 1);
190                    
191                    fwrite($fs, $http_request);
192            
193                    while (!feof($fs)) {
194                        $answer .= fgets($fs, 1160); // One TCP-IP packet
195                    }
196                    fclose($fs);
197                    
198                    $answer = explode("\r\n\r\n", $answer, 2);
199                    
200                    // Response Header: $answer[0]
201                    // Response Content: $answer[1]
202                    if (preg_match('#HTTP/.*? 200#', $answer[0])) {
203                        $response = trim($answer[1]);
204                    }
205                }
206            }
207            
208            return $response;
209        }
210        
211        function addWumiiContent($content) {
212            $newContent = $content;
213            if ($this->canDisplayWumiiContent()) {
214                $escapedUrl = $this->htmlEscape(get_permalink());
215                $escapedTitle = $this->htmlEscape($this->getOrigTitle());
216                $escapedPic = $this->htmlEscape($this->getPostThumbnailSrc());
217                
218                // The first line in 'WUMII_HOOK' must be an empty line. Because some blogs use 'Embeds'(http://codex.wordpress.org/Embeds)
219                // in the post content and the embed must be on its own line.
220                // For example, if the 'embed' happen to add before our wumii code,
221                // then we have to make sure wumii code doesn't follow that within the same line.
222                $newContent .= <<<WUMII_HOOK
223
224<div class="wumii-hook">
225    <input type="hidden" name="wurl" value="$escapedUrl" />
226    <input type="hidden" name="wtitle" value="$escapedTitle" />
227    <input type="hidden" name="wpic" value="$escapedPic" />
228</div>
229WUMII_HOOK;
230            }
231
232            global $wp_query;
233            if ($wp_query->current_post + 1 == $wp_query->post_count) {
234                $newContent = $this->addScriptInPage($newContent);
235            }
236
237            return $newContent;
238        }
239        
240        function echoWumiiScript() {
241            echo $this->createWumiiScript();
242        }
243        
244        function echoVerificationMeta() {
245            $code = $this->getVerificationCode();
246            if ($code) {
247                echo "<meta name='wumiiVerification' content='$code' />\n";
248            }
249        }
250        
251        private function addScriptInPage($content) {
252            if ($this->getAdminOption(self::SCRIPT_IN_FOOTER)) {
253                add_action('wp_footer', array($this, 'echoWumiiScript'));
254                return $content;
255            } else {
256                return $content . $this->createWumiiScript();
257            }
258        }
259        
260        private function createWumiiScript() {
261            $enableCustomPos = $this->getAdminOption(self::ENABLE_CUSTOM_POS) ? 'true' : 'false';
262            $numPosts = $this->getAdminOption(self::NUM_POSTS);
263            
264            $server = self::WUMII_SERVER;
265            
266            $script = '';
267            if (self::WUMII_DEBUG) {
268                $script = "<script type='text/javascript'>var wumiiDebugServer = '$server';</script>";
269            }
270            
271            global $wp_version;
272            $params = array(
273                'num' => $numPosts,
274                'mode' => 3,    // Use the DisplayMode.AUTO as default
275                'displayInFeed' => $this->getAdminOption(self::DISPLAY_IN_FEED),    // 1=true, 0=false
276                'version' => self::VERSION,
277                'pf' => 'WordPress' . $wp_version
278            );
279            $queryParams = '';
280            foreach ($params as $name => $value) {
281                $value = urlencode($value);
282                $queryParams .= "&$name=$value";
283            }
284            
285            $jsonCategories = $this->getJsonCategories();
286            
287            // Do not break the following html code into lines between each html tag.
288            // One default filter in wordpress will replace "\n" with "<br />" between html tags.
289            $script .= <<<WUMII_SCRIPT
290
291<p style="margin:0;padding:0;height:1px;overflow:hidden;">
292    <script type="text/javascript"><!--
293        var wumiiSitePrefix = "$this->sitePrefix";
294        var wumiiEnableCustomPos = $enableCustomPos;
295        var wumiiParams = "$queryParams";
296        var wumiiCategories = $jsonCategories;
297    //--></script><script type="text/javascript" src="$server/ext/relatedItemsWidget"></script><a href="http://www.wumii.com/widget/relatedItems" style="border:0;"><img src="http://static.wumii.cn/images/pixel.png" alt="无觅相关文章插件,快速提升流量" style="border:0;padding:0;margin:0;" /></a>
298</p>
299WUMII_SCRIPT;
300            
301            return $script;
302        }
303        
304        private function getJsonCategories() {
305            $categories = get_the_category();
306            $categoryArr = array();
307            if ($categories) {
308                foreach($categories as $category) {
309                    $categoryArr[] = html_entity_decode($category->cat_name);
310                }
311            }
312            return json_encode($categoryArr);
313        }
314        
315        private function htmlEscape($str) {
316            return htmlspecialchars(html_entity_decode($str, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8');
317        }
318        
319        private function getPostThumbnailSrc() {
320            // function 'get_post_thumbnail_id' need blog support
321            if (!function_exists('get_post_thumbnail_id')) {
322                return;
323            }
324            
325            $image_info = wp_get_attachment_image_src(get_post_thumbnail_id(), 'full');
326            if ($image_info) {
327                return $image_info[0];
328            }
329        }
330        
331        private function canDisplayWumiiContent() {
332            // We can identify the live-blogging post by checking if Live Blogging plugin is activated and the shortcode in the content.
333            // The related items will show in such live blogging post only one time generally,
334            // but if the theme call the filter on 'the_content' hook or run another 'The Loop' to fetch the post content using in some other cases before to display,
335            // the related items will not show.
336            // In brief, the related items show one time at most in each post.
337            global $post;
338            if (array_key_exists($post->ID, $this->excludePostIds)) {
339                return false;
340            }
341            
342            global $shortcode_tags; // Container for storing shortcode tags and their hook to call for the shortcode
343            if (!empty($shortcode_tags) && is_array($shortcode_tags)
344                    && array_key_exists('liveblog', $shortcode_tags) && strpos(get_the_content(), '[liveblog]') !== false) {
345                $this->excludePostIds[$post->ID] = 1;
346            }
347            
348            return get_post_status($post->ID) == 'publish' &&
349                   get_post_type() == 'post' && // In some pages e.g. "attachment page" should not display related items
350                   empty($post->post_password) &&
351                   !is_preview() && // When create a new post and press the preview button before publish it,
352                                    // the post's permalink is not the correct form as the setting in "Permalink".
353                                    // We have to prevent the related items displaying in these pages.
354                   !is_feed() &&
355                   !is_page() && // In some pages e.g. "about me" also should not display.
356                   (is_single() || !$this->getAdminOption(self::SINGLE_PAGE_ONLY));
357        }
358        
359        private function resetAdminOptions () {
360            $this->adminOptions = array(
361                self::NUM_POSTS => 8,
362                self::SINGLE_PAGE_ONLY => true,
363                self::DISPLAY_IN_FEED => true,
364                self::ENABLE_CUSTOM_POS => false,
365                self::SCRIPT_IN_FOOTER => true,
366                self::WUMII_COMMENT_PLUGIN_ENABLED => false);
367        }
368        
369        private function popNotifyMessage() {
370            $notifyMessage = get_option(self::NOTIFY_MESSAGE_CONTAINER);
371            if ($notifyMessage) {
372                update_option(self::NOTIFY_MESSAGE_CONTAINER, '');
373            }
374            return $notifyMessage;
375        }
376        
377        // This function is always executed by one user(admin).So no synchronization problem may occur.
378        private function pushNotifyMessage($message, $type = 'updated') {
379            if ($legacyMessage = get_option(self::NOTIFY_MESSAGE_CONTAINER)) {
380                echo 'NOTIFY_MESSAGE_CONTAINER is not empty, legacy message: ' . $legacyMessage->getMessage();
381                return false;
382            }
383            
384            update_option(self::NOTIFY_MESSAGE_CONTAINER, new Wumii_Notify_Message($message, $type));
385        }
386        
387        private function setVerificationCode($code) {
388            update_option(self::VERIFICATION_CODE, $code);
389        }
390        
391        private function getVerificationCode() {
392            return get_option(self::VERIFICATION_CODE);
393        }
394        
395        private function saveAdminOptions() {
396            update_option(self::ADMIN_OPTION_NAME, $this->adminOptions);
397        }
398        
399        private function getAdminOption($key) {
400            return $this->adminOptions[$key];
401        }
402        
403        // echo true will be 1, echo false will be 0
404        // But echo !true will be null. So it have to strictly convert false to '0'.
405        private function getBooleanInStr($boolean) {
406            return $boolean ? '1' : '0';
407        }
408        
409        private function addReplacementsForCheckboxState($replacementsArr, $checkboxOptionNames) {
410            if (!is_array($checkboxOptionNames)) {
411                return false;
412            }
413            
414            foreach ($checkboxOptionNames as $optionName) {
415                $checkState = $this->getAdminOption($optionName);
416                $replacementsArr[$optionName . '_checked_' . $this->getBooleanInStr($checkState)] = 'checked="checked"';
417                $replacementsArr[$optionName . '_checked_' . $this->getBooleanInStr(!$checkState)] = '';
418            }
419            
420            return $replacementsArr;
421        }
422        
423        private function printHtmlAdminOptionsPage() {
424            $adminOptionTemplate = new Wumii_Template('adminOptionsPanel.html');
425            
426            $replacements = array('request_uri' => $_SERVER["REQUEST_URI"]);
427            $replacements = $this->addReplacementsForCheckboxState($replacements,
428                                                                    array(self::SINGLE_PAGE_ONLY,
429                                                                          self::DISPLAY_IN_FEED,
430                                                                          self::ENABLE_CUSTOM_POS,
431                                                                          self::SCRIPT_IN_FOOTER,
432                                                                          self::WUMII_COMMENT_PLUGIN_ENABLED));
433            
434            $numPost = $this->getAdminOption(self::NUM_POSTS);
435            // We allow the num of related items from 1 to 12.
436            for ($i = 1; $i <= 12; $i++) {
437                if ($i == $numPost) {
438                    $replaceStr = 'selected="selected"';
439                } else {
440                    $replaceStr = '';
441                }
442                $replacements['num_posts_selected_' . $i] = $replaceStr;
443            }
444            $replacements['verification_code'] = $this->getVerificationCode();
445            
446            $adminOptionTemplate->addReplacements($replacements);
447            
448            echo $adminOptionTemplate->render();
449        }
450        
451        function printAdminPage() {
452            $OptionsUpdatedMessage = array();
453            if (array_key_exists('add_verification_meta', $_POST)) {
454                
455                // WARNING: This check is not reliable if the theme doesn't call get_header() to fetch the contents of header.php.
456                // REASON: We don't know in which file of the theme call the function get_header(), although in almost every case themes will call it.
457                if ($this->isHookFuncInTemplate('header.php', 'wp_head')) {
458                    $code = $_POST[self::VERIFICATION_CODE];
459                    $this->setVerificationCode($code);
460                    if ($code) {
461                        $OptionsUpdatedMessage[] = new Wumii_Notify_Message('已成功添加认证代码,现在请回到<a href="http://www.wumii.com/site/index" target="_blank">无觅网站管理中心</a>,点击“验证”按钮即可完成博客认证。');
462                    }
463                } else {
464                    $OptionsUpdatedMessage[] = new Wumii_Notify_Message('此主题可能不支持将代码嵌入到header区域,请使用其他方法进行博客认证。', 'error');
465                }
466            } else {
467                if (array_key_exists('update_wumii_settings', $_POST)) {
468                    foreach ($this->adminOptions as $key => $value) {
469                        if (array_key_exists($key, $_POST)) {
470                            $this->adminOptions[$key] = $_POST[$key];
471                            if ($key == self::WUMII_COMMENT_PLUGIN_ENABLED) {
472                                if ($_POST[$key]) {
473                                    $this->replaceCommentBox();
474                                } else {
475                                       $this->removeCommentBox();
476                                }
477                            }
478                        }
479                    }
480                    $OptionsUpdatedMessage[] = new Wumii_Notify_Message('设置已更新.');
481                } else if (array_key_exists('reset_wumii_settings', $_POST)) {
482                    $this->resetAdminOptions();
483                    $OptionsUpdatedMessage[] = new Wumii_Notify_Message('设置已重置.');
484                }
485                
486                if ($this->adminOptions[self::SCRIPT_IN_FOOTER]
487                        // The wp_footer action is theme-dependent.
488                        // If the theme-defined file footer.php doesn't call wp_footer(), the action will not fire.
489                        && !$this->isScriptInFooterSupported()) {
490                    $OptionsUpdatedMessage[] = new Wumii_Notify_Message('正在使用的主题可能不支持将加载脚本置于文档末尾, 建议不要开启此功能。', 'error');
491                }
492                
493                $this->saveAdminOptions();
494            }
495            $this->printHtmlNotifyMessage($OptionsUpdatedMessage);
496            $this->printHtmlAdminOptionsPage();
497        }
498        
499        private function removeCommentBox() {
500            if (has_filter('comments_template', 'wumii_comment')) {
501                remove_filter('comments_template', 'wumii_comment', $this->commentFilterPriority);
502            }
503        }
504        
505        private function replaceCommentBox() {
506            if ($this->isCommentPluginEnabled() && !is_plugin_active('wumii-comment/wumii-comment.php')) {
507                // warn: should stay the same with wumii-comment.php
508                if (!function_exists('wumii_comment')) {
509                    add_filter('comments_template', 'wumii_comment', is_plugin_active('duoshuo/duoshuo.php') ? 99999 : 10);
510                    function wumii_comment($file) {
511                        global $post;
512                        if (strcmp(get_option('default_comment_status'), 'open') != 0) {
513                            update_option('default_comment_status', 'open');
514                        }
515                        return dirname(__FILE__) . '/comment.php';
516                    }
517                }
518            }
519        }
520        
521        private function printHtmlNotifyMessage($notifyMessages) {
522            if (empty($notifyMessages) || !is_array($notifyMessages)) {
523                return false;
524            }
525            
526            foreach($notifyMessages as $msg) {
527                $type = $msg->getType();
528                $message = $msg->getMessage();
529                echo "<div class='$type'><p>$message</p></div>";
530            }
531        }
532        
533        private function isHookFuncInTemplate($template, $func) {
534            global $wp_version;
535            
536            $location = '';
537            if (strcmp($wp_version, '2.7.0') >= 0) {
538                // Only WordPress >= 2.7.0 support footer.php located in style sheet path
539                // and it's the first path being checked in wordpress's source code.
540                $probableLocation = get_stylesheet_directory() . '/' . $template;
541                if (file_exists($probableLocation)) {
542                    $location = $probableLocation;
543                }
544            }
545            
546            if (!$location) {
547                $probableLocation = get_template_directory() . '/' . $template;
548                if (file_exists($probableLocation)) {
549                    $location = $probableLocation;
550                }
551            }
552            
553            // this template doesn't include in current theme and wordpress will use the default template instead.
554            // Hook function must exist in the default template
555            if (!$location) {
556                return true;
557            }
558            $contents = file_get_contents($location);
559            return $contents !== false ? (strpos($contents, $func . '()') !== false) : false;
560        }
561        
562        // WARNING: This function is not reliable if theme doesn't call get_footer() to fetch the contents of footer.php.
563        // REASON: We don't know in which file of the theme call the function get_footer(), although in almost every case themes will call it.
564        private function isScriptInFooterSupported() {
565            return $this->isHookFuncInTemplate('footer.php', 'wp_footer');
566        }
567        
568        function checkFooterScriptSupportedByTheme($theme) {
569            if ($this->getAdminOption(self::SCRIPT_IN_FOOTER) && !$this->isScriptInFooterSupported()) {
570                $this->pushNotifyMessage('此主题不支持将<strong>无觅相关文章脚本</strong>置于文档末尾, 可能会导致相关文章无法显示, 建议关闭此功能.', 'error');
571            }
572        }
573        
574        function doActivation() {
575            // now script in footer is the default setting and we need to check if it supported when activation.
576            if (!$this->isScriptInFooterSupported()) {
577                $this->adminOptions[self::SCRIPT_IN_FOOTER] = false;
578                $this->saveAdminOptions();
579            }
580            $this->pushNotifyMessage('<strong>无觅相关文章插件: </strong>如果您的博客装有缓存(cache)插件, 请在首次启用插件时清空缓存. 另外, 启用插件后无觅爬虫会立刻去获取贵站的文章信息, 这时切记不要停掉插件, 以免影响文章的相关性.装完可来<strong><a href="http://www.wumii.com/site/index" target="_blank">无觅网站管理中心</a></strong>查看收录状况.');
581        }
582        
583        function finalize() {
584            delete_option(self::ADMIN_OPTION_NAME);
585            delete_option(self::NOTIFY_MESSAGE_CONTAINER);
586            delete_option(self::VERIFICATION_CODE);
587        }
588        
589            
590        function isCommentPluginEnabled() {
591            return $this->adminOptions[self::WUMII_COMMENT_PLUGIN_ENABLED];
592        }
593    }
594    
595    class Wumii_Notify_Message {
596        private $message;
597        private $type;
598        
599        function __construct($message, $type = 'updated') {
600            $this->Wumii_Notify_Message($message, $type);
601        }
602        
603        function Wumii_Notify_Message($message, $type = 'updated') {
604            if ($type != 'error') {
605                $type = 'updated';
606            }
607            
608            $this->message = $message;
609            $this->type = $type;
610        }
611        
612        function getMessage() {
613            return $this->message;
614        }
615        
616        function getType() {
617            return $this->type;
618        }
619    }
620    
621    class Wumii_Template {
622        private $htmlStrTemplate;
623        private $keyValuePair = array();
624        
625        // PHP 5 Constructors
626        function __construct($templateName) {
627            $this->Wumii_Template($templateName);
628        }
629        
630        // treated as regular method since PHP 5.3.3
631        function Wumii_Template($templateName) {
632            $this->htmlStrTemplate = file_get_contents($this->wumii_get_config_file_path() . WumiiRelatedPosts::PLUGIN_PATH . '/templates/' . $templateName);
633        }
634        
635        function addReplacements($keyValuePair) {
636            foreach ($keyValuePair as $key => $value) {
637                $this->keyValuePair[$this->getReplacementStr($key)] = $value;
638            }
639        }
640        
641        function render() {
642            return str_replace(array_keys($this->keyValuePair), array_values($this->keyValuePair), $this->htmlStrTemplate);
643        }
644        
645        private function getReplacementStr($key) {
646            return '{{' . $key . '}}';
647        }
648        
649        private function wumii_get_config_file_path() {
650            $pathname = $_SERVER['SCRIPT_FILENAME'];
651            preg_match("/^(.+)wp-admin/Ui", $pathname, $matches);
652            // Here if $matches[0] == 'C:/workspace/www/wordpress/wordpress/wp-admin', than matches[1] == 'C:/workspace/www/wordpress/wordpress/'.
653            return $matches[1];
654        }
655    }
656    
657    $wumii_incompatible_plugins_in_rss = array('ozh-better-feed/wp_ozh_betterfeed.php',
658                                               'rejected-wp-keyword-link-rejected/wp_keywordlink.php');
659    
660    $wumii_incompatible_plugins_in_content = array('markdown-for-wordpress-and-bbpress/markdown.php');
661    
662    function wumii_has_incompatible_plugins($incompatiblePlugins = array()) {
663        foreach($incompatiblePlugins as $plugin) {
664            if (is_plugin_active($plugin)) {
665                return true;
666            }
667        }
668        return false;
669    }
670    
671    require_once(ABSPATH . 'wp-admin/includes/plugin.php');
672    $wumii_related_posts = new WumiiRelatedPosts();
673    
674    add_action('wp_head', array($wumii_related_posts, 'echoVerificationMeta'));
675    
676    add_action('admin_notices', array($wumii_related_posts, 'showNotifyMessage'));
677    add_action('admin_menu', array($wumii_related_posts, 'registerOptionsPage'));
678    //  We need to place our content as near to the front as possible, so set the priority 1.
679    add_action('the_content', array($wumii_related_posts, 'addWumiiContent'),
680                   wumii_has_incompatible_plugins($wumii_incompatible_plugins_in_content) ? 99999 : 1);
681    add_action('switch_theme', array($wumii_related_posts, 'checkFooterScriptSupportedByTheme'));
682    
683    // function add_filter($tag, $function_to_add, $priority, $num_accepted_args)
684    add_filter('plugin_action_links', array($wumii_related_posts, 'addPluginActionLinks'), 10, 2);
685    
686    add_filter('the_excerpt_rss', array($wumii_related_posts, 'addToRssContent'));
687    
688    // In order to avoid some plugins removing or modifing our related items in rss, we try to delay to add our content as late as possible.
689    // Default priority is 10.
690    add_filter('the_content', array($wumii_related_posts, 'addToRssContent'),
691                   wumii_has_incompatible_plugins($wumii_incompatible_plugins_in_rss) ? 99999 : 10);
692    
693    register_activation_hook(__FILE__, array($wumii_related_posts, 'doActivation'));
694    register_deactivation_hook(__FILE__, array($wumii_related_posts, 'finalize'));
695    
696
697} else {
698    function classConflictException() {
699        echo '<div class="error"><p><strong>插件冲突。</strong>您的博客正在运行一个与“无觅相关文章插件”定义了相同类名的插件,只有在关闭冲突插件以后“无觅相关文章插件”才能正常启用。</p></div>';
700    }
701    add_action('admin_notices', 'classConflictException');
702}
703?>