1<?php
2/**
3 @file       noiewarning/action.php
4 @brief      Warn users of Internet Explorer browser.
5 @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 @author     Luis Machuca Bezzaza <luis [dot] machuca [at] gulix [dot] cl>
7 @version    2018-08-21
8**/
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11if(!defined('DW_LF')) define('DW_LF',"\n");
12
13if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
14define('THIS_PLUGIN', DOKU_PLUGIN.'noiewarning/');
15require_once(DOKU_PLUGIN.'action.php');
16require_once(DOKU_INC.'inc/confutils.php');
17require_once(DOKU_INC.'inc/infoutils.php'); // for notify facilities
18
19/**
20 * All DokuWiki plugins to extend the admin function
21 * need to inherit from this class
22 */
23class action_plugin_noiewarning extends DokuWiki_Action_Plugin {
24
25    // the plugin's data object
26    var $cnf= array();
27    // the browser recommendation
28    var $betterbrowser= null;
29
30    function action_plugin_noiewarning () {
31        global $ID;
32        global $ACT;
33        // load options from configuration
34        $this->cnf= array (
35            'id'         => $ID,
36            'is_IE'      => $this->_is_IE(),
37            'browser'    => $_SERVER['HTTP_USER_AGENT'],
38            'message'    => null,
39            'file'       => null,
40            'showhack'   => false
41        );
42
43        $this->betterbrowser= $this->get_better_browser();
44        if ($this->cnf['showhack'] !== false) {
45            $hackwarn = 'ERROR: the loaded hash "'. $this->cnf['file'];
46            $hackwarn.= '" seems to recommend Internet Explorer.<br/>';
47            $hackwarn.= '<strong>This wiki installation may have been hacked!</strong>';
48            msg($hackwarn, -1);
49            return; // nothing more to do
50        }
51        // if not IE, nothing more to do
52        if (!$this->cnf['is_IE'] ) return; // nothing more to be done
53
54        $this->cnf['method'] = $this->getConf('method');
55        $this->cnf['datasrc']= $this->getConf('source');
56        $this->cnf['banner'] = $this->getLang('wedetect');
57        // set up information
58
59    }
60
61    function getInfo () {
62        $parts = explode('_',get_class($this));
63        $file  = DOKU_PLUGIN.'/'.$parts[2].'/plugin.info.txt';
64        $info  = array();
65        if(!@file_exists($file)) {
66            trigger_error('getInfo() not implemented in '.get_class($this).' and '.$info.' not found', E_USER_WARNING);
67        }
68        else {
69            $info= confToHash($file);
70            $lang= explode(',', $info['lang']);
71            if (in_array('desc', $lang) && ''!==$this->getLang('plugininfo_desc')) $info['desc']= $this->getLang('plugininfo_desc');
72        }
73        return $info;
74    }
75
76    /***
77     * Register its handlers with the DokuWiki's event controller
78     */
79    function register(Doku_Event_Handler $controller) {
80        global $ACT;
81        if ($ACT != 'show' || $this->cnf['showhack']===true) return;
82        $controller->register_hook(
83        'TPL_CONTENT_DISPLAY','BEFORE', $this, 'warning',array());
84
85        // debug handler (should be disabled by default)
86        //$controller->register_hook(
87        //'TPL_CONTENT_DISPLAY','BEFORE', $this, '_debug');
88    }
89
90
91/*
92  rendering options (from getConf)
93  subsection   -> write the message as first content of wikipage
94  notify       -> use the DokuWiki notification message area
95  load_note    -> use the note plugin if available
96  splash       -> use a JS+CSS splash screen (not available in this release)
97*/
98    function warning (Doku_Event $event, $param) {
99        global $ID;
100        global $INFO;
101        global $ACT;
102        static $done= false;
103
104        // we do work only when IE is detected
105        if (!$this->_is_IE() // if user is not using IE
106        || $ACT!='show' // or we are not actually displaying a page
107        ) {
108            $done= true;
109            return;
110        }
111        else {
112            $emessage= $this->cnf['banner'];
113
114            switch ($this->cnf['method']) {
115            case 'notify': {
116                $this->render_notify_($event, $param);
117                break;
118            }
119            case 'note': {
120                $this->render_note_($event, $param);
121                break;
122            }
123            /* case not yet implemented
124            case 'splash': {
125                $this->render_splash_($event, $param);
126                break;
127            }
128            */
129            default: {
130                // prepends the warning to the page output data
131                //$event->data= $emessage. DW_LF. $event->data;
132                $this->render_text_($event, $param);
133                break;
134            }
135
136            } // end switch
137            $done= true;
138        } // end else (working against IE)
139
140    // end function
141    }
142
143
144/**
145 *******************************************************************
146 * Helper Functions
147 */
148
149    /**
150     * @fn      _is_IE
151     * @brief   Attempts to detect Internet Explorer
152     * @return  <tt>true</tt> if IE is detected, <tt>false</tt> otherwise
153     */
154    private function _is_IE () {
155        $useragent= $_SERVER['HTTP_USER_AGENT'];
156        if (strpos($useragent, 'MSIE') !== false)
157            return true;
158        else if (strpos($useragent, 'Trident/') !== false)
159            return true;
160        else if (strpos($useragent, 'Edge/') !== false)
161            return true;
162        else
163            return false;    // end function
164    }
165
166    function render_text_ (Doku_Event $event, $param) {
167        global $conf;
168        $warn= p_render('xhtml', p_get_instructions($this->getLang('wedetect')), $info);
169        $warn= '<div id="noiewarning"> '. $warn;
170
171        if ($this->betterbrowser !== null && !empty($this->betterbrowser)) { // can't do a thing
172
173            $url= $this->betterbrowser['url'];
174            $favicon= htmlspecialchars( $this->betterbrowser['icon'] ? 'background-image: url('.$this->betterbrowser['icon'].') ;' : '' );
175            $color= $this->betterbrowser['color'] ? 'border-bottom: 2px solid '. str_replace('%','#', $this->betterbrowser['color']). ';' : '';
176            $werecommend= htmlspecialchars($this->getLang('werecommend'));
177            $name= htmlspecialchars($this->betterbrowser['browser']);
178            $warn= <<<EOF
179$warn
180<p>$werecommend <a href="$url" class="urlextern" style="$favicon $color">$name</a>.</p>
181EOF;
182
183        }
184        $warn= $warn. DW_LF. '</div>'. DW_LF;
185        $event->data = $warn.$event->data;
186    }
187
188
189    function render_notify_ (Doku_Event $event, $param) {
190        global $conf;
191        $warn= p_render('xhtml', p_get_instructions($this->getLang('wedetect')), $info);
192        $warn= substr($warn, 4, -5);
193        msg($warn, -1);
194
195        if ($this->betterbrowser !== null && !empty($this->betterbrowser)) {
196
197            $url= $this->betterbrowser['url'];
198            $favicon= $this->betterbrowser['icon'] ? 'background-image: url('.$this->betterbrowser['icon'].') ;' : '';
199            $name= htmlspecialchars($this->betterbrowser['browser']);
200            $title= htmlspecialchars("Switch to ". $name);
201            $werecommend= htmlspecialchars($this->getLang('werecommend'));
202            $more= 'line-height: 1.6em; text-decoration: none; text-shadow: -1px -1px '. $this->betterbrowser['color']. ';'. $favicon;
203            $reclink= <<<EOF
204$werecommend <a href="$url" class="urlextern" rel="follow" title="$title" style="$more" >$name</a>
205EOF;
206
207            msg($reclink, 0);
208        } // end if
209    }
210
211    function render_note_ (Doku_Event $event, $param) {
212        $notehelper= &plugin_load('syntax', 'note');
213        if (!$notehelper) {
214            msg("noiewarning: the plugin (note) was selected as an assist, but not found.", -1);
215        }
216        global $conf;
217        $warn = '<note warning>'.DW_LF. DW_LF;
218        $warn.= $this->getLang('wedetect'). DW_LF;
219        if ($this->betterbrowser !== null && !empty($this->betterbrowser)) { // can't do a thing
220            $wrecc.= DW_LF. $this->getLang('werecommend');
221            $url= $this->betterbrowser['url'];
222            $name= $this->betterbrowser['browser'];
223
224            $wrecc.= "[[${url}|${name}]] .". DW_LF;
225            $warn.= $wrecc;
226        }
227        $warn.= '</note>'. DW_LF;
228
229        $outp= p_render('xhtml', p_get_instructions($warn), $info);
230        $event->data = $outp.$event->data;
231    }
232
233    function render_splash_ (Doku_Event $event, $param) {
234    }
235
236
237    function _debug (Doku_Event $event, $param) {
238        global $ACT;
239        $d .= '<pre> ACT: '. $ACT. DW_LF;
240        if (!$this->betterbrowser) $d .= ' --- BADARRAY ---';
241        else $d .= print_r ($this->betterbrowser, true);
242        $d .= '</pre><br/>';
243        $event->data = $d. $event->data;
244    }
245
246
247  /**
248   * @fn    _getraw
249   * @brief Loads the wikitext from a file, given its wikipage
250   *
251   * Technically just a wrapper on io_readfile, but as it is intended
252   * to grow and do specific parsing, will be separated here.
253   */
254    private function _getraw ($wikipage) {
255    if (page_exists($page) ) {
256        // start a renderer and pass it the warning wikipage
257        $file  = wikiFN($page);
258        $text  = io_readfile($file);
259        }
260    return $text;
261    // end function
262    }
263
264
265
266    /**
267     This function looks for a hash file called
268     DOKU_CONF/better-browser.txt, which contains info about a
269     recommended browser.
270    **/
271    private function get_better_browser ($id = '') {
272        $hash= null;
273        if ($id === '') $id= $this->getConf('better');
274        // search recommend at $DOKU_CONF or browserlist path
275        if ($id === 'custom') {
276            $betterfile= DOKU_CONF. '/better-browser.txt';
277            if (file_exists($betterfile)) {
278                $hash= confToHash($betterfile);
279                $this->cnf['file']= $id;
280            }
281            else $id= 'firefox';
282        }
283        if ($hash === null) {
284            $betterfile= THIS_PLUGIN.'recommend/'.$id.'.txt';
285            if (file_exists($betterfile)) {
286                $hash= confToHash($betterfile);
287                $this->cnf['file']= $id;
288            }
289        }
290        //sanitize the hash, checking that there are no mentions of IE
291        if ($hash === null) return $hash; // nothing more can be done
292        if (in_array('IE', $hash)!==false
293        || in_array('Internet Explorer', $hash)!==false
294        || in_array('Microsoft', $hash)!==false
295        || in_array('Edge/', $hash)!==false
296        ) {
297            $this->cnf['file']= $id. '(?)';
298            // should NOT be recommending!
299            $hash= null;
300            //$hackwarn= htmlspecialchars($hackwarn);
301            if ($this->cnf['showhack'] === false) {
302                $this->cnf['showhack']= true;
303            }
304        }
305        return $hash;
306    }
307
308}
309