dokuwiki-indexmenu-plugin – Blame information for rev 1

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