dokuwiki-indexmenu-plugin – Blame information for rev 3

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 <?php
2 /**
3 * Indexmenu Admin Plugin: Indexmenu Component.
4 *
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author Samuele Tognini <samuele@samuele.netsons.org>
7 */
8  
9 if(!defined('DOKU_INC')) die();
10  
11 require_once(DOKU_PLUGIN."indexmenu/inc/pclzip.lib.php");
12  
13 if(!defined('INDEXMENU_IMG_ABSDIR')) define('INDEXMENU_IMG_ABSDIR', DOKU_PLUGIN."indexmenu/images");
14 define('INDEXMENU_ICOS', 'base,folder,folderopen,folderh,folderhopen,page,plus,minus,nolines_plus,nolines_minus,minusbottom,plusbottom,join,joinbottom,line,empty');
15  
16 class admin_plugin_indexmenu extends DokuWiki_Admin_Plugin {
17 var $req = 'fetch';
18 var $repos = array(
19 "url" => array(DOKU_URL),
20 "status" => array(""),
21 );
22  
23 var $selected = -1;
24  
25 /**
26 * return sort order for position in admin menu
27 */
28 function getMenuSort() {
29 return 999;
30 }
31  
32 /**
33 * handle user request
34 */
35 function handle() {
36 $url = "http://samuele.netsons.org/dokuwiki";
37 if(empty($url)) {
38 $this->repos['url'][] = $this->getLang('no_repos');
39 $this->repos['status'][] = "disabled";
40 $this->repos['install'][] = -1;
41 } else {
42 $this->repos['url'] = array_merge($this->repos['url'], explode(',', $url));
43 }
44  
45 if(!isset($_REQUEST['req'])) return; // first time - nothing to do
46 $this->req = $_REQUEST['req'];
47  
48 if(is_numeric($_REQUEST['repo'])) $this->selected = $_REQUEST['repo'];
49 }
50  
51 /**
52 * output appropriate html
53 */
54 function html() {
55 global $conf;
56 ptln('<div id="config__manager">');
57 ptln(' <h1>'.$this->getLang('menu').'</h1>');
58 ptln($this->_donate());
59 ptln(' <fieldset>');
60 ptln(' <legend>'.$this->getLang('checkupdates').'</legend>');
61 $this->_form_open("checkupdates");
62 $this->_form_close('check');
63 if($this->req == 'checkupdates') {
64 $this->_checkupdates();
65 }
66 ptln(' </fieldset>');
67 ptln(' <fieldset>');
68 ptln(' <legend>Themes</legend>');
69 ptln(' <table class="inline">');
70 ptln(' <tr class="default"><td class="label" colspan="2">');
71 ptln(' <span class="outkey">'.$this->getLang('infos').'</span>');
72 ptln(' </td></tr>');
73 $n = 0;
74 //cycles thru repositories urls
75 foreach($this->repos['url'] as $url) {
76 ptln(' <tr class="search_hit"><td>');
77 $legend = ($n == 0) ? $conf['title'] : $this->repos['url'][$n];
78 ptln(' <span><label><strong>'.$legend.'</strong></label></span>');
79 ptln(' </td>');
80 ptln(' <td class="value">');
81 $this->_form_open("fetch", $n);
82 $this->_form_close("fetch");
83 ptln(' </td></tr>');
84 //list requested theme
85 if($n == $this->selected) {
86 ptln(' <tr class="default"><td colspan="2">');
87 if($this->req == 'install') $this->install($this->selected, $_REQUEST['name']);
88 if($this->req == 'upload' && $_REQUEST['name']) {
89 $info = "";
90 if(isset($_REQUEST['author_info'])) {
91 $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
92 $info .= "author=".strtr($_REQUEST['author_info'], $obfuscate)."\n";
93 }
94 if(isset($_REQUEST['url_info'])) $info .= "url=".$_REQUEST['url_info']."\n";
95 if(isset($_REQUEST['author_info'])) $info .= "description=".$_REQUEST['author_info'];
96 if(!$this->upload($_REQUEST['name'], $info)) msg($this->getLang('install_no'), -1);
97 }
98 if($this->req == 'delete' && $_REQUEST['name']) $this->_delete($_REQUEST['name']);
99 ptln(' </td></tr><tr><td colspan="2">');
100 $this->dolist($n);
101 ptln(' </td></tr>');
102 }
103 $n++;
104 }
105 ptln(' </table>');
106 ptln(' </fieldset>');
107 ptln('</div>');
108 }
109  
110 /**
111 * Connect to theme repository and list themes
112 *
113 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
114 * @author Samuele Tognini <samuele@samuele.netsons.org>
115 */
116 function dolist($n) {
117 global $INFO;
118 if($n === false) return;
119 //info.txt keys to parse
120 $keys = array('author', 'url', 'description');
121 $icos = explode(',', INDEXMENU_ICOS);
122 $turl = "";
123 $info = "";
124 //get list
125 $data = $this->_remotequery($this->repos['url'][$n]."/lib/plugins/indexmenu/ajax.php?req=local");
126 $data = explode(",", $data);
127 //print themes
128 for($i = 3; $i < count($data); $i++) {
129 $theme = $data[$i];
130 $turl = $data[1].$data[2]."/".$theme;
131 ptln(' <em>'.$theme.'</em>');
132 ptln(' <div class="indexmenu_list_themes">');
133 ptln(' <div>');
134 //print images
135 foreach(array_slice($icos, 0, 8) as $ico) {
136 $ext = explode(".", $theme);
137 $ext = array_pop($ext);
138 $ext = ($ext == $theme) ? '.gif' : ".$ext";
139 ptln(' <img src="'.$turl."/".$ico.$ext.'" title="'.$ico.'" alt="'.$ico.'" />');
140 }
141 ptln(' </div>');
142 //get theme info.txt
143 if($info = $this->_remotequery($turl."/info.txt", false)) {
144 foreach($keys as $key) {
145 if(!preg_match('/'.$key.'=(.*)/', $info, $out)) continue;
146 ptln(" <div>");
147 ptln(" <strong>".hsc($key).": </strong>".hsc($out[1]));
148 ptln(" </div>");
149 }
150 }
151 if($n == 0) {
152 $act = "upload";
153 if($theme != "default") {
154 $this->_form_open("delete", $n);
155 ptln(' <input type="hidden" name="name" value="'.$theme.'" />');
156 $this->_form_close("delete");
157 }
158 } else {
159 $act = "install";
160 ptln(' <a href="'.$this->repos['url'][$n]."/lib/plugins/indexmenu/ajax.php?req=send&amp;t=".$theme.'">Download</a>');
161 }
162 $this->_form_open($act, $n);
163 if($n == 0 && !is_file(INDEXMENU_IMG_ABSDIR."/".$theme."/info.txt")) {
164 ptln(' <div><strong>author:</strong><input type="text" name="author_info" value="'.$INFO["userinfo"]["name"].hsc(" <".$INFO["userinfo"]["mail"].">").'" size="50" maxlength="100" /><br />');
165 ptln(' <strong>url:</strong><input type="text" name="url_info" value="'.$this->repos['url'][$n].'" size="50" maxlength="200" /><br />');
166 ptln(' <strong>description:</strong><input type="text" name="description_info" value="" size="50" maxlength="200" /></div>');
167 }
168 ptln(' <input type="hidden" name="name" value="'.$theme.'" />');
169 $this->_form_close($act);
170 ptln(' <br /><br /></div>');
171 }
172 }
173  
174 /**
175 * Download and install themes
176 *
177 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
178 * @author Samuele Tognini <samuele@samuele.netsons.org>
179 */
180 function install($n, $name) {
181 $repo = $this->repos['url'][$n];
182 if(!isset($name)) return false;
183 $return = true;
184 if(!$absdir = $this->checktmpsubdir()) return false;
185 $tmp = $absdir."/tmp";
186  
187 //send theme list request
188 if(!$zipfile = io_download($repo."/lib/plugins/indexmenu/ajax.php?req=send&t=".$name, "$tmp/", true)) {
189 msg($this->getLang('down_err').": $name", -1);
190 $return = false;
191 } else {
192 //create zip
193 $zip = new PclZip("$tmp/$zipfile");
194 $regexp = "/^".$name."\/(info.txt)|(style.css)|(".str_replace(",", "|", INDEXMENU_ICOS).")\.(gif|png|jpg)$/i";
195 $status = $zip->extract(PCLZIP_OPT_PATH, $absdir."/", PCLZIP_OPT_BY_PREG, $regexp);
196 //error
197 if($status == 0) {
198 msg($this->getLang('zip_err')." $tmp/$zipfile: ".$zip->errorName(true), -1);
199 $return = false;
200 } else {
201 msg("<strong>$name</strong> ".$this->getLang('install_ok'), 1);
202 }
203 }
204 //clean tmp
205 $this->_rm_dir($tmp);
206 return $return;
207 }
208  
209 /**
210 * Remove a directory
211 *
212 */
213 function _rm_dir($path) {
214 if(!is_string($path) || $path == "") return false;
215  
216 if(is_dir($path)) {
217 if(!$dh = @opendir($path)) return false;
218  
219 while($f = readdir($dh)) {
220 if($f == '..' || $f == '.') continue;
221 $this->_rm_dir("$path/$f");
222 }
223  
224 closedir($dh);
225 return @rmdir($path);
226 } else {
227 return @unlink($path);
228 }
229 }
230  
231 /**
232 * Retrive and create themes tmp directory
233 *
234 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
235 * @author Samuele Tognini <samuele@samuele.netsons.org>
236 */
237 function checktmpsubdir() {
238 $tmp = INDEXMENU_IMG_ABSDIR."/tmp";
239 if(!io_mkdir_p($tmp)) {
240 msg($this->getLang('dir_err').": $tmp", -1);
241 return false;
242 }
243 return INDEXMENU_IMG_ABSDIR;
244 }
245  
246 /**
247 * Upload a theme into my site
248 *
249 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
250 * @author Samuele Tognini <samuele@samuele.netsons.org>
251 */
252 function upload($theme, $info) {
253 $return = true;
254 $host = 'samuele.netsons.org';
255 $path = '/dokuwiki/lib/plugins/indexmenu/upload/index.php';
256 //TODO: merge zip creation with that in ajax.php (create a class?)
257 if(!$absdir = $this->checktmpsubdir()) return false;
258 $tmp = $absdir."/tmp";
259 $zipfile = "$theme.zip";
260 $filelist = "$absdir/$theme";
261 //create info
262 if(!empty($info)) {
263 io_savefile("$tmp/$theme/info.txt", $info);
264 $filelist .= ",$tmp/$theme";
265 }
266 //create zip
267 $zip = new PclZip("$tmp/$zipfile");
268 $status = $zip->create($filelist, PCLZIP_OPT_REMOVE_ALL_PATH);
269 if($status == 0) {
270 //error
271 msg($this->getLang('zip_err').": ".$zip->errorName(true), -1);
272 $return = false;
273 } else {
274 //prepare POST headers.
275 $boundary = "---------------------------".uniqid("");
276 $data = join("", file("$tmp/$zipfile"));
277 $header = "POST $path HTTP/1.0\r\n";
278 $header .= "Host: $host\r\n";
279 $header .= "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)\r\n";
280 $header .= "Content-type: multipart/form-data, boundary=$boundary\r\n";
281 $body = "--".$boundary."\r\n";
282 $body .= "Content-Disposition: form-data; name=\"userfile\"; filename=\"$zipfile\"\r\n";
283 $body .= "Content-Type: application/x-zip-compressed\r\n\r\n";
284 $body .= $data."\r\n";
285 $body .= "--".$boundary."\r\n";
286 $body .= "Content-Disposition: form-data; name=\"upload\"\r\n\r\n";
287 $body .= "Upload\r\n";
288 $body .= "--".$boundary."--\r\n";
289 $header .= "Content-Length: ".strlen($body)."\r\n\r\n";
290  
291 //connect and send zip
292 if($fp = fsockopen($host, 80)) {
293 fwrite($fp, $header.$body);
294 //reply
295 $buf = "";
296 while(!feof($fp)) {
297 $buf .= fgets($fp, 3200);
298 }
299 fclose($fp);
300 //parse resply
301 if(preg_match("/<!--indexmenu-->(.*)<!--\/indexmenu-->/s", $buf, $match)) {
302 $str = substr($match[1], 4, 7);
303 switch($str) {
304 case "ERROR ":
305 $mesg_type = -1;
306 break;
307 case "SUCCESS":
308 $mesg_type = 1;
309 break;
310 default:
311 $mesg_type = 2;
312 }
313 msg($match[1], $mesg_type);
314 } else {
315 $return = false;
316 }
317 } else {
318 $return = false;
319 }
320 }
321  
322 $this->_rm_dir($tmp);
323 return $return;
324 }
325  
326 /**
327 * Check for new messages from upstream
328 *
329 * @author Samuele Tognini <samuele@samuele.netsons.org>
330 */
331 function _checkupdates() {
332 require_once (DOKU_INC.'inc/HTTPClient.php');
333 global $conf;
334 global $INFO;
335 $w = -1;
336 $date = $this->getInfo('date');
337 $date = $date['date'];
338 $data = $this->_remotequery("http://samuele.netsons.org/dokuwiki/lib/plugins/indexmenu/remote.php?check=$date");
339 if($data === "") {
340 msg($this->getLang('noupdates'), 1);
341 $data .= @preg_replace('/\n\n.*$/s', '', @io_readFile(DOKU_PLUGIN.'indexmenu/changelog'))."\n%\n";
342 $w = 1;
343 } else {
344 $data = preg_replace('/\<br(\s*)?\/?\>/i', "", $data);
345 $data = preg_replace('/\t/', " ", $data);
346 }
347 $data = preg_replace('/\[\[(?!(http|https))(.:)(.*?)\]\]/s', "[[plugin:$3]]", $data);
348 $data = preg_replace('/\[\[(?!(http|https))(.*?)\]\]/s', "[[http://www.dokuwiki.org/$2]]", $data);
349 $msgs = explode("\n%\n", $data);
350 foreach($msgs as $msg) {
351 if($msg) {
352 $msg = p_render('xhtml', p_get_instructions($msg), $info);
353 msg($msg, $w);
354 }
355 }
356 }
357  
358 /**
359 * Get url response and check it
360 *
361 * @author Samuele Tognini <samuele@samuele.netsons.org>
362 */
363 function _remotequery($url, $tag = true) {
364 require_once (DOKU_INC.'inc/HTTPClient.php');
365 $http = new DokuHTTPClient();
366 $http->timeout = 8;
367 $data = $http->get($url);
368 if($tag) {
369 if($data === false) {
370 msg($this->getLang('conn_err'), -1);
371 } else {
372 (substr($data, 0, 9) === "indexmenu") ? $data = substr($data, 9) : $data = "";
373 }
374 }
375 return $data;
376 }
377  
378 /**
379 * Open an html form
380 *
381 * @author Samuele Tognini <samuele@samuele.netsons.org>
382 */
383 function _form_open($act, $n = -1) {
384 global $ID;
385 ptln(' <form action="'.wl($ID).'" method="post">');
386 ptln(' <input type="hidden" name="do" value="admin" />');
387 ptln(' <input type="hidden" name="page" value="'.$this->getPluginName().'" />');
388 ptln(' <input type="hidden" name="req" value="'.$act.'" />');
389 ptln(' <input type="hidden" name="repo" value="'.$n.'" />');
390 }
391  
392 /**
393 * Close the html form
394 *
395 * @author Samuele Tognini <samuele@samuele.netsons.org>
396 */
397 function _form_close($act) {
398 ptln(' <input type="submit" name="btn" '.$this->repos['status'][$n].' value="'.$this->getLang($act).'" />');
399 ptln(' </form>');
400 }
401  
402 /**
403 * Remove an installed theme
404 *
405 * @author Samuele Tognini <samuele@samuele.netsons.org>
406 */
407 function _delete($theme) {
408 if($theme == "default") return;
409 if($this->_rm_dir(INDEXMENU_IMG_ABSDIR."/".utf8_encodeFN(basename($theme)))) {
410 msg($this->getLang('delete_ok').": $theme.", 1);
411 } else {
412 msg($this->getLang('delete_no').": $theme.", -1);
413 }
414 }
415  
416 /**
417 * Print the donate button.
418 *
419 * @author Samuele Tognini <samuele@samuele.netsons.org>
420 */
421 function _donate() {
422 $out = "<fieldset>\n";
423 $out .= '<p>'.$this->getLang('donation_text').'</p>';
424 $out .= '<form action="https://www.paypal.com/cgi-bin/webscr" method="post">'."\n";
425 $out .= '<input type="hidden" name="cmd" value="_s-xclick" />'."\n";
426 $out .= '<input type="hidden" name="hosted_button_id" value="102873" />'."\n";
427 $out .= '<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" name="submit" alt="" />'."\n";
428 $out .= '<img alt="" src="https://www.paypal.com/it_IT/i/scr/pixel.gif" width="1" height="1" />'."\n";
429 $out .= "</form></fieldset>\n";
430 return $out;
431 }
432  
433 }