1<?php 2/** 3 * DokuWiki Actions 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9if(!defined('DOKU_INC')) die('meh.'); 10 11 12function act_dispatch(){ 13 $router = \dokuwiki\ActionRouter::getInstance(); // is this needed here or could we delegate it to tpl_content() later? 14 15 16 17 //call template FIXME: all needed vars available? 18 $headers[] = 'Content-Type: text/html; charset=utf-8'; 19 trigger_event('ACTION_HEADERS_SEND',$headers,'act_sendheaders'); 20 21 // clear internal variables 22 unset($router); 23 unset($headers); 24 // make all globals available to the template 25 extract($GLOBALS); 26 27 include(template('main.php')); 28 // output for the commands is now handled in inc/templates.php 29 // in function tpl_content() 30} 31 32/** 33 * Call the needed action handlers 34 * 35 * @author Andreas Gohr <andi@splitbrain.org> 36 * @triggers ACTION_ACT_PREPROCESS 37 * @triggers ACTION_HEADERS_SEND 38 */ 39function XXX_act_dispatch(){ 40 global $ACT; 41 global $ID; 42 global $INFO; 43 global $QUERY; 44 /* @var Input $INPUT */ 45 global $INPUT; 46 global $lang; 47 global $conf; 48 49 $preact = $ACT; 50 51 // give plugins an opportunity to process the action 52 $evt = new Doku_Event('ACTION_ACT_PREPROCESS',$ACT); 53 54 $headers = array(); 55 if ($evt->advise_before()) { 56 57 //sanitize $ACT 58 $ACT = act_validate($ACT); 59 60 //check if searchword was given - else just show 61 $s = cleanID($QUERY); 62 if($ACT == 'search' && empty($s)){ 63 $ACT = 'show'; 64 } 65 66 //login stuff 67 if(in_array($ACT,array('login','logout'))){ 68 $ACT = act_auth($ACT); 69 } 70 71 //check if user is asking to (un)subscribe a page 72 if($ACT == 'subscribe') { 73 try { 74 $ACT = act_subscription($ACT); 75 } catch (Exception $e) { 76 msg($e->getMessage(), -1); 77 } 78 } 79 80 //display some info 81 if($ACT == 'check'){ 82 check(); 83 $ACT = 'show'; 84 } 85 86 //check permissions 87 $ACT = act_permcheck($ACT); 88 89 //sitemap 90 if ($ACT == 'sitemap'){ 91 act_sitemap($ACT); 92 } 93 94 //recent changes 95 if ($ACT == 'recent'){ 96 $show_changes = $INPUT->str('show_changes'); 97 if (!empty($show_changes)) { 98 set_doku_pref('show_changes', $show_changes); 99 } 100 } 101 102 //diff 103 if ($ACT == 'diff'){ 104 $difftype = $INPUT->str('difftype'); 105 if (!empty($difftype)) { 106 set_doku_pref('difftype', $difftype); 107 } 108 } 109 110 //register 111 if($ACT == 'register' && $INPUT->post->bool('save') && register()){ 112 $ACT = 'login'; 113 } 114 115 if ($ACT == 'resendpwd' && act_resendpwd()) { 116 $ACT = 'login'; 117 } 118 119 // user profile changes 120 if (in_array($ACT, array('profile','profile_delete'))) { 121 if(!$INPUT->server->str('REMOTE_USER')) { 122 $ACT = 'login'; 123 } else { 124 switch ($ACT) { 125 case 'profile' : 126 if(updateprofile()) { 127 msg($lang['profchanged'],1); 128 $ACT = 'show'; 129 } 130 break; 131 case 'profile_delete' : 132 if(auth_deleteprofile()){ 133 msg($lang['profdeleted'],1); 134 $ACT = 'show'; 135 } else { 136 $ACT = 'profile'; 137 } 138 break; 139 } 140 } 141 } 142 143 //revert 144 if($ACT == 'revert'){ 145 if(checkSecurityToken()){ 146 $ACT = act_revert($ACT); 147 }else{ 148 $ACT = 'show'; 149 } 150 } 151 152 //save 153 if($ACT == 'save'){ 154 if(checkSecurityToken()){ 155 $ACT = act_save($ACT); 156 }else{ 157 $ACT = 'preview'; 158 } 159 } 160 161 //cancel conflicting edit 162 if($ACT == 'cancel') 163 $ACT = 'show'; 164 165 //draft deletion 166 if($ACT == 'draftdel') 167 $ACT = act_draftdel($ACT); 168 169 //draft saving on preview 170 if($ACT == 'preview') { 171 $headers[] = "X-XSS-Protection: 0"; 172 $ACT = act_draftsave($ACT); 173 } 174 175 //edit 176 if(in_array($ACT, array('edit', 'preview', 'recover'))) { 177 $ACT = act_edit($ACT); 178 }else{ 179 unlock($ID); //try to unlock 180 } 181 182 //handle export 183 if(substr($ACT,0,7) == 'export_') 184 $ACT = act_export($ACT); 185 186 //handle admin tasks 187 if($ACT == 'admin'){ 188 // retrieve admin plugin name from $_REQUEST['page'] 189 if (($page = $INPUT->str('page', '', true)) != '') { 190 /** @var $plugin DokuWiki_Admin_Plugin */ 191 if ($plugin = plugin_getRequestAdminPlugin()){ 192 $plugin->handle(); 193 } 194 } 195 } 196 197 // check permissions again - the action may have changed 198 $ACT = act_permcheck($ACT); 199 } // end event ACTION_ACT_PREPROCESS default action 200 $evt->advise_after(); 201 // Make sure plugs can handle 'denied' 202 if($conf['send404'] && $ACT == 'denied') { 203 http_status(403); 204 } 205 unset($evt); 206 207 // when action 'show', the intial not 'show' and POST, do a redirect 208 if($ACT == 'show' && $preact != 'show' && strtolower($INPUT->server->str('REQUEST_METHOD')) == 'post'){ 209 act_redirect($ID,$preact); 210 } 211 212 global $INFO; 213 global $conf; 214 global $license; 215 216 //call template FIXME: all needed vars available? 217 $headers[] = 'Content-Type: text/html; charset=utf-8'; 218 trigger_event('ACTION_HEADERS_SEND',$headers,'act_sendheaders'); 219 220 include(template('main.php')); 221 // output for the commands is now handled in inc/templates.php 222 // in function tpl_content() 223} 224 225/** 226 * Send the given headers using header() 227 * 228 * @param array $headers The headers that shall be sent 229 */ 230function act_sendheaders($headers) { 231 foreach ($headers as $hdr) header($hdr); 232} 233 234/** 235 * Sanitize the action command 236 * 237 * @author Andreas Gohr <andi@splitbrain.org> 238 * 239 * @param array|string $act 240 * @return string 241 */ 242function act_clean($act){ 243 // check if the action was given as array key 244 if(is_array($act)){ 245 list($act) = array_keys($act); 246 } 247 248 //remove all bad chars 249 $act = strtolower($act); 250 $act = preg_replace('/[^1-9a-z_]+/','',$act); 251 252 if($act == 'export_html') $act = 'export_xhtml'; 253 if($act == 'export_htmlbody') $act = 'export_xhtmlbody'; 254 255 if($act === '') $act = 'show'; 256 return $act; 257} 258 259/** 260 * Sanitize and validate action commands. 261 * 262 * Add all allowed commands here. 263 * 264 * @author Andreas Gohr <andi@splitbrain.org> 265 * 266 * @param array|string $act 267 * @return string 268 */ 269function act_validate($act) { 270 global $conf; 271 global $INFO; 272 273 $act = act_clean($act); 274 275 // check if action is disabled 276 if(!actionOK($act)){ 277 msg('Command disabled: '.htmlspecialchars($act),-1); 278 return 'show'; 279 } 280 281 //disable all acl related commands if ACL is disabled 282 if(!$conf['useacl'] && in_array($act,array('login','logout','register','admin', 283 'subscribe','unsubscribe','profile','revert', 284 'resendpwd','profile_delete'))){ 285 msg('Command unavailable: '.htmlspecialchars($act),-1); 286 return 'show'; 287 } 288 289 //is there really a draft? 290 if($act == 'draft' && !file_exists($INFO['draft'])) return 'edit'; 291 292 if(!in_array($act,array('login','logout','register','save','cancel','edit','draft', 293 'preview','search','show','check','index','revisions', 294 'diff','recent','backlink','admin','subscribe','revert', 295 'unsubscribe','profile','profile_delete','resendpwd','recover', 296 'draftdel','sitemap','media')) && substr($act,0,7) != 'export_' ) { 297 msg('Command unknown: '.htmlspecialchars($act),-1); 298 return 'show'; 299 } 300 return $act; 301} 302 303/** 304 * Run permissionchecks 305 * 306 * @author Andreas Gohr <andi@splitbrain.org> 307 * 308 * @param string $act action command 309 * @return string action command 310 */ 311function act_permcheck($act){ 312 global $INFO; 313 314 if(in_array($act,array('save','preview','edit','recover'))){ 315 if($INFO['exists']){ 316 if($act == 'edit'){ 317 //the edit function will check again and do a source show 318 //when no AUTH_EDIT available 319 $permneed = AUTH_READ; 320 }else{ 321 $permneed = AUTH_EDIT; 322 } 323 }else{ 324 $permneed = AUTH_CREATE; 325 } 326 }elseif(in_array($act,array('login','search','recent','profile','profile_delete','index', 'sitemap'))){ 327 $permneed = AUTH_NONE; 328 }elseif($act == 'revert'){ 329 $permneed = AUTH_ADMIN; 330 if($INFO['ismanager']) $permneed = AUTH_EDIT; 331 }elseif($act == 'register'){ 332 $permneed = AUTH_NONE; 333 }elseif($act == 'resendpwd'){ 334 $permneed = AUTH_NONE; 335 }elseif($act == 'admin'){ 336 if($INFO['ismanager']){ 337 // if the manager has the needed permissions for a certain admin 338 // action is checked later 339 $permneed = AUTH_READ; 340 }else{ 341 $permneed = AUTH_ADMIN; 342 } 343 }else{ 344 $permneed = AUTH_READ; 345 } 346 if($INFO['perm'] >= $permneed) return $act; 347 348 return 'denied'; 349} 350 351/** 352 * Handle 'draftdel' 353 * 354 * Deletes the draft for the current page and user 355 * 356 * @param string $act action command 357 * @return string action command 358 */ 359function act_draftdel($act){ 360 global $INFO; 361 @unlink($INFO['draft']); 362 $INFO['draft'] = null; 363 return 'show'; 364} 365 366/** 367 * Saves a draft on preview 368 * 369 * @todo this currently duplicates code from ajax.php :-/ 370 * 371 * @param string $act action command 372 * @return string action command 373 */ 374function act_draftsave($act){ 375 global $INFO; 376 global $ID; 377 global $INPUT; 378 global $conf; 379 if($conf['usedraft'] && $INPUT->post->has('wikitext')) { 380 $draft = array('id' => $ID, 381 'prefix' => substr($INPUT->post->str('prefix'), 0, -1), 382 'text' => $INPUT->post->str('wikitext'), 383 'suffix' => $INPUT->post->str('suffix'), 384 'date' => $INPUT->post->int('date'), 385 'client' => $INFO['client'], 386 ); 387 $cname = getCacheName($draft['client'].$ID,'.draft'); 388 if(io_saveFile($cname,serialize($draft))){ 389 $INFO['draft'] = $cname; 390 } 391 } 392 return $act; 393} 394 395/** 396 * Handle 'save' 397 * 398 * Checks for spam and conflicts and saves the page. 399 * Does a redirect to show the page afterwards or 400 * returns a new action. 401 * 402 * @author Andreas Gohr <andi@splitbrain.org> 403 * 404 * @param string $act action command 405 * @return string action command 406 */ 407function act_save($act){ 408 global $ID; 409 global $DATE; 410 global $PRE; 411 global $TEXT; 412 global $SUF; 413 global $SUM; 414 global $lang; 415 global $INFO; 416 global $INPUT; 417 418 //spam check 419 if(checkwordblock()) { 420 msg($lang['wordblock'], -1); 421 return 'edit'; 422 } 423 //conflict check 424 if($DATE != 0 && $INFO['meta']['date']['modified'] > $DATE ) 425 return 'conflict'; 426 427 //save it 428 saveWikiText($ID,con($PRE,$TEXT,$SUF,true),$SUM,$INPUT->bool('minor')); //use pretty mode for con 429 //unlock it 430 unlock($ID); 431 432 //delete draft 433 act_draftdel($act); 434 session_write_close(); 435 436 // when done, show page 437 return 'show'; 438} 439 440/** 441 * Revert to a certain revision 442 * 443 * @author Andreas Gohr <andi@splitbrain.org> 444 * 445 * @param string $act action command 446 * @return string action command 447 */ 448function act_revert($act){ 449 global $ID; 450 global $REV; 451 global $lang; 452 /* @var Input $INPUT */ 453 global $INPUT; 454 // FIXME $INFO['writable'] currently refers to the attic version 455 // global $INFO; 456 // if (!$INFO['writable']) { 457 // return 'show'; 458 // } 459 460 // when no revision is given, delete current one 461 // FIXME this feature is not exposed in the GUI currently 462 $text = ''; 463 $sum = $lang['deleted']; 464 if($REV){ 465 $text = rawWiki($ID,$REV); 466 if(!$text) return 'show'; //something went wrong 467 $sum = sprintf($lang['restored'], dformat($REV)); 468 } 469 470 // spam check 471 472 if (checkwordblock($text)) { 473 msg($lang['wordblock'], -1); 474 return 'edit'; 475 } 476 477 saveWikiText($ID,$text,$sum,false); 478 msg($sum,1); 479 480 //delete any draft 481 act_draftdel($act); 482 session_write_close(); 483 484 // when done, show current page 485 $INPUT->server->set('REQUEST_METHOD','post'); //should force a redirect 486 $REV = ''; 487 return 'show'; 488} 489 490/** 491 * Do a redirect after receiving post data 492 * 493 * Tries to add the section id as hash mark after section editing 494 * 495 * @param string $id page id 496 * @param string $preact action command before redirect 497 */ 498function act_redirect($id,$preact){ 499 global $PRE; 500 global $TEXT; 501 502 $opts = array( 503 'id' => $id, 504 'preact' => $preact 505 ); 506 //get section name when coming from section edit 507 if($PRE && preg_match('/^\s*==+([^=\n]+)/',$TEXT,$match)){ 508 $check = false; //Byref 509 $opts['fragment'] = sectionID($match[0], $check); 510 } 511 512 trigger_event('ACTION_SHOW_REDIRECT',$opts,'act_redirect_execute'); 513} 514 515/** 516 * Execute the redirect 517 * 518 * @param array $opts id and fragment for the redirect and the preact 519 */ 520function act_redirect_execute($opts){ 521 $go = wl($opts['id'],'',true); 522 if(isset($opts['fragment'])) $go .= '#'.$opts['fragment']; 523 524 //show it 525 send_redirect($go); 526} 527 528/** 529 * Handle 'login', 'logout' 530 * 531 * @author Andreas Gohr <andi@splitbrain.org> 532 * 533 * @param string $act action command 534 * @return string action command 535 */ 536function act_auth($act){ 537 global $ID; 538 global $INFO; 539 /* @var Input $INPUT */ 540 global $INPUT; 541 542 //already logged in? 543 if($INPUT->server->has('REMOTE_USER') && $act=='login'){ 544 return 'show'; 545 } 546 547 //handle logout 548 if($act=='logout'){ 549 $lockedby = checklock($ID); //page still locked? 550 if($lockedby == $INPUT->server->str('REMOTE_USER')){ 551 unlock($ID); //try to unlock 552 } 553 554 // do the logout stuff 555 auth_logoff(); 556 557 // rebuild info array 558 $INFO = pageinfo(); 559 560 act_redirect($ID,'login'); 561 } 562 563 return $act; 564} 565 566/** 567 * Handle 'edit', 'preview', 'recover' 568 * 569 * @author Andreas Gohr <andi@splitbrain.org> 570 * 571 * @param string $act action command 572 * @return string action command 573 */ 574function act_edit($act){ 575 global $ID; 576 global $INFO; 577 578 global $TEXT; 579 global $RANGE; 580 global $PRE; 581 global $SUF; 582 global $REV; 583 global $SUM; 584 global $lang; 585 global $DATE; 586 587 if (!isset($TEXT)) { 588 if ($INFO['exists']) { 589 if ($RANGE) { 590 list($PRE,$TEXT,$SUF) = rawWikiSlices($RANGE,$ID,$REV); 591 } else { 592 $TEXT = rawWiki($ID,$REV); 593 } 594 } else { 595 $TEXT = pageTemplate($ID); 596 } 597 } 598 599 //set summary default 600 if(!$SUM){ 601 if($REV){ 602 $SUM = sprintf($lang['restored'], dformat($REV)); 603 }elseif(!$INFO['exists']){ 604 $SUM = $lang['created']; 605 } 606 } 607 608 // Use the date of the newest revision, not of the revision we edit 609 // This is used for conflict detection 610 if(!$DATE) $DATE = @filemtime(wikiFN($ID)); 611 612 //check if locked by anyone - if not lock for my self 613 //do not lock when the user can't edit anyway 614 if ($INFO['writable']) { 615 $lockedby = checklock($ID); 616 if($lockedby) return 'locked'; 617 618 lock($ID); 619 } 620 621 return $act; 622} 623 624/** 625 * Export a wiki page for various formats 626 * 627 * Triggers ACTION_EXPORT_POSTPROCESS 628 * 629 * Event data: 630 * data['id'] -- page id 631 * data['mode'] -- requested export mode 632 * data['headers'] -- export headers 633 * data['output'] -- export output 634 * 635 * @author Andreas Gohr <andi@splitbrain.org> 636 * @author Michael Klier <chi@chimeric.de> 637 * 638 * @param string $act action command 639 * @return string action command 640 */ 641function act_export($act){ 642 global $ID; 643 global $REV; 644 global $conf; 645 global $lang; 646 647 $pre = ''; 648 $post = ''; 649 $headers = array(); 650 651 // search engines: never cache exported docs! (Google only currently) 652 $headers['X-Robots-Tag'] = 'noindex'; 653 654 $mode = substr($act,7); 655 switch($mode) { 656 case 'raw': 657 $headers['Content-Type'] = 'text/plain; charset=utf-8'; 658 $headers['Content-Disposition'] = 'attachment; filename='.noNS($ID).'.txt'; 659 $output = rawWiki($ID,$REV); 660 break; 661 case 'xhtml': 662 $pre .= '<!DOCTYPE html>' . DOKU_LF; 663 $pre .= '<html lang="'.$conf['lang'].'" dir="'.$lang['direction'].'">' . DOKU_LF; 664 $pre .= '<head>' . DOKU_LF; 665 $pre .= ' <meta charset="utf-8" />' . DOKU_LF; 666 $pre .= ' <title>'.$ID.'</title>' . DOKU_LF; 667 668 // get metaheaders 669 ob_start(); 670 tpl_metaheaders(); 671 $pre .= ob_get_clean(); 672 673 $pre .= '</head>' . DOKU_LF; 674 $pre .= '<body>' . DOKU_LF; 675 $pre .= '<div class="dokuwiki export">' . DOKU_LF; 676 677 // get toc 678 $pre .= tpl_toc(true); 679 680 $headers['Content-Type'] = 'text/html; charset=utf-8'; 681 $output = p_wiki_xhtml($ID,$REV,false); 682 683 $post .= '</div>' . DOKU_LF; 684 $post .= '</body>' . DOKU_LF; 685 $post .= '</html>' . DOKU_LF; 686 break; 687 case 'xhtmlbody': 688 $headers['Content-Type'] = 'text/html; charset=utf-8'; 689 $output = p_wiki_xhtml($ID,$REV,false); 690 break; 691 default: 692 $output = p_cached_output(wikiFN($ID,$REV), $mode, $ID); 693 $headers = p_get_metadata($ID,"format $mode"); 694 break; 695 } 696 697 // prepare event data 698 $data = array(); 699 $data['id'] = $ID; 700 $data['mode'] = $mode; 701 $data['headers'] = $headers; 702 $data['output'] =& $output; 703 704 trigger_event('ACTION_EXPORT_POSTPROCESS', $data); 705 706 if(!empty($data['output'])){ 707 if(is_array($data['headers'])) foreach($data['headers'] as $key => $val){ 708 header("$key: $val"); 709 } 710 print $pre.$data['output'].$post; 711 exit; 712 } 713 return 'show'; 714} 715 716/** 717 * Handle sitemap delivery 718 * 719 * @author Michael Hamann <michael@content-space.de> 720 * 721 * @param string $act action command 722 */ 723function act_sitemap($act) { 724 global $conf; 725 726 if ($conf['sitemap'] < 1 || !is_numeric($conf['sitemap'])) { 727 http_status(404); 728 print "Sitemap generation is disabled."; 729 exit; 730 } 731 732 $sitemap = Sitemapper::getFilePath(); 733 if (Sitemapper::sitemapIsCompressed()) { 734 $mime = 'application/x-gzip'; 735 }else{ 736 $mime = 'application/xml; charset=utf-8'; 737 } 738 739 // Check if sitemap file exists, otherwise create it 740 if (!is_readable($sitemap)) { 741 Sitemapper::generate(); 742 } 743 744 if (is_readable($sitemap)) { 745 // Send headers 746 header('Content-Type: '.$mime); 747 header('Content-Disposition: attachment; filename='.utf8_basename($sitemap)); 748 749 http_conditionalRequest(filemtime($sitemap)); 750 751 // Send file 752 //use x-sendfile header to pass the delivery to compatible webservers 753 http_sendfile($sitemap); 754 755 readfile($sitemap); 756 exit; 757 } 758 759 http_status(500); 760 print "Could not read the sitemap file - bad permissions?"; 761 exit; 762} 763 764/** 765 * Handle page 'subscribe' 766 * 767 * Throws exception on error. 768 * 769 * @author Adrian Lang <lang@cosmocode.de> 770 * 771 * @param string $act action command 772 * @return string action command 773 * @throws Exception if (un)subscribing fails 774 */ 775function act_subscription($act){ 776 global $lang; 777 global $INFO; 778 global $ID; 779 /* @var Input $INPUT */ 780 global $INPUT; 781 782 // subcriptions work for logged in users only 783 if(!$INPUT->server->str('REMOTE_USER')) return 'show'; 784 785 // get and preprocess data. 786 $params = array(); 787 foreach(array('target', 'style', 'action') as $param) { 788 if ($INPUT->has("sub_$param")) { 789 $params[$param] = $INPUT->str("sub_$param"); 790 } 791 } 792 793 // any action given? if not just return and show the subscription page 794 if(empty($params['action']) || !checkSecurityToken()) return $act; 795 796 // Handle POST data, may throw exception. 797 trigger_event('ACTION_HANDLE_SUBSCRIBE', $params, 'subscription_handle_post'); 798 799 $target = $params['target']; 800 $style = $params['style']; 801 $action = $params['action']; 802 803 // Perform action. 804 $sub = new Subscription(); 805 if($action == 'unsubscribe'){ 806 $ok = $sub->remove($target, $INPUT->server->str('REMOTE_USER'), $style); 807 }else{ 808 $ok = $sub->add($target, $INPUT->server->str('REMOTE_USER'), $style); 809 } 810 811 if($ok) { 812 msg(sprintf($lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']), 813 prettyprint_id($target)), 1); 814 act_redirect($ID, $act); 815 } else { 816 throw new Exception(sprintf($lang["subscr_{$action}_error"], 817 hsc($INFO['userinfo']['name']), 818 prettyprint_id($target))); 819 } 820 821 // Assure that we have valid data if act_redirect somehow fails. 822 $INFO['subscribed'] = $sub->user_subscription(); 823 return 'show'; 824} 825 826/** 827 * Validate POST data 828 * 829 * Validates POST data for a subscribe or unsubscribe request. This is the 830 * default action for the event ACTION_HANDLE_SUBSCRIBE. 831 * 832 * @author Adrian Lang <lang@cosmocode.de> 833 * 834 * @param array &$params the parameters: target, style and action 835 * @throws Exception 836 */ 837function subscription_handle_post(&$params) { 838 global $INFO; 839 global $lang; 840 /* @var Input $INPUT */ 841 global $INPUT; 842 843 // Get and validate parameters. 844 if (!isset($params['target'])) { 845 throw new Exception('no subscription target given'); 846 } 847 $target = $params['target']; 848 $valid_styles = array('every', 'digest'); 849 if (substr($target, -1, 1) === ':') { 850 // Allow “list” subscribe style since the target is a namespace. 851 $valid_styles[] = 'list'; 852 } 853 $style = valid_input_set('style', $valid_styles, $params, 854 'invalid subscription style given'); 855 $action = valid_input_set('action', array('subscribe', 'unsubscribe'), 856 $params, 'invalid subscription action given'); 857 858 // Check other conditions. 859 if ($action === 'subscribe') { 860 if ($INFO['userinfo']['mail'] === '') { 861 throw new Exception($lang['subscr_subscribe_noaddress']); 862 } 863 } elseif ($action === 'unsubscribe') { 864 $is = false; 865 foreach($INFO['subscribed'] as $subscr) { 866 if ($subscr['target'] === $target) { 867 $is = true; 868 } 869 } 870 if ($is === false) { 871 throw new Exception(sprintf($lang['subscr_not_subscribed'], 872 $INPUT->server->str('REMOTE_USER'), 873 prettyprint_id($target))); 874 } 875 // subscription_set deletes a subscription if style = null. 876 $style = null; 877 } 878 879 $params = compact('target', 'style', 'action'); 880} 881 882//Setup VIM: ex: et ts=2 : 883