1<?php 2/** 3 * XML feed export 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9 if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__)).'/'); 10 require_once(DOKU_INC.'inc/init.php'); 11 require_once(DOKU_INC.'inc/common.php'); 12 require_once(DOKU_INC.'inc/parserutils.php'); 13 require_once(DOKU_INC.'inc/feedcreator.class.php'); 14 require_once(DOKU_INC.'inc/auth.php'); 15 require_once(DOKU_INC.'inc/pageutils.php'); 16 17 //close session 18 session_write_close(); 19 20 21 $num = $_REQUEST['num']; 22 $type = $_REQUEST['type']; 23 $mode = $_REQUEST['mode']; 24 $minor = $_REQUEST['minor']; 25 $ns = $_REQUEST['ns']; 26 $ltype = $_REQUEST['linkto']; 27 28 if($type == '') 29 $type = $conf['rss_type']; 30 31 switch ($type){ 32 case 'rss': 33 $type = 'RSS0.91'; 34 $mime = 'text/xml'; 35 break; 36 case 'rss2': 37 $type = 'RSS2.0'; 38 $mime = 'text/xml'; 39 break; 40 case 'atom': 41 $type = 'ATOM0.3'; 42 $mime = 'application/xml'; 43 break; 44 case 'atom1': 45 $type = 'ATOM1.0'; 46 $mime = 'application/atom+xml'; 47 break; 48 default: 49 $type = 'RSS1.0'; 50 $mime = 'application/xml'; 51 } 52 53 // the feed is dynamic - we need a cache for each combo 54 // (but most people just use the default feed so it's still effective) 55 $cache = getCacheName($num.$type.$mode.$ns.$ltype.$_SERVER['REMOTE_USER'],'.feed'); 56 57 // check cacheage and deliver if nothing has changed since last 58 // time or the update interval has not passed, also handles conditional requests 59 header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 60 header('Pragma: public'); 61 header('Content-Type: application/xml; charset=utf-8'); 62 $cmod = @filemtime($cache); // 0 if not exists 63 if($cmod && (($cmod+$conf['rss_update']>time()) || ($cmod>@filemtime($conf['changelog'])))){ 64 http_conditionalRequest($cmod); 65 print io_readFile($cache); 66 exit; 67 } else { 68 http_conditionalRequest(time()); 69 } 70 71 // create new feed 72 $rss = new DokuWikiFeedCreator(); 73 $rss->title = $conf['title'].(($ns) ? ' '.$ns : ''); 74 $rss->link = DOKU_URL; 75 $rss->syndicationURL = DOKU_URL.'feed.php'; 76 $rss->cssStyleSheet = DOKU_URL.'lib/styles/feed.css'; 77 78 $image = new FeedImage(); 79 $image->title = $conf['title']; 80 $image->url = DOKU_URL."lib/images/favicon.ico"; 81 $image->link = DOKU_URL; 82 $rss->image = $image; 83 84 if($mode == 'list'){ 85 rssListNamespace($rss,$ns); 86 }else{ 87 rssRecentChanges($rss,$num,$ltype,$ns,$minor); 88 } 89 90 $feed = $rss->createFeed($type,'utf-8'); 91 92 // save cachefile 93 io_saveFile($cache,$feed); 94 95 // finally deliver 96 print $feed; 97 98// ---------------------------------------------------------------- // 99 100/** 101 * Add recent changed to a feed object 102 * 103 * @author Andreas Gohr <andi@splitbrain.org> 104 */ 105function rssRecentChanges(&$rss,$num,$ltype,$ns,$minor){ 106 global $conf; 107 global $auth; 108 109 if(!$num) $num = $conf['recent']; 110 $guardmail = ($conf['mailguard'] != '' && $conf['mailguard'] != 'none'); 111 112 113 $flags = RECENTS_SKIP_DELETED; 114 if(!$minor) $flags += RECENTS_SKIP_MINORS; 115 116 $recents = getRecents(0,$num,$ns,$flags); 117 118 //this can take some time if a lot of recaching has to be done 119 @set_time_limit(90); // set max execution time 120 121 foreach($recents as $recent){ 122 123 $item = new FeedItem(); 124 $item->title = $recent['id']; 125 $xhtml = p_wiki_xhtml($recent['id'],'',false); 126 127 if($conf['useheading']) { 128 $matches = array(); 129 if(preg_match('|<h([1-9])>(.*?)</h\1>|', $xhtml, $matches)) 130 $item->title = trim($matches[2]); 131 } 132 if(!empty($recent['sum'])){ 133 $item->title .= ' - '.strip_tags($recent['sum']); 134 } 135 136 $desc = cleanDesc($xhtml); 137 138 if(empty($ltype)) 139 $ltype = $conf['rss_linkto']; 140 141 switch ($ltype){ 142 case 'page': 143 $item->link = wl($recent['id'],'rev='.$recent['date'],true); 144 break; 145 case 'rev': 146 $item->link = wl($recent['id'],'do=revisions&rev='.$recent['date'],true); 147 break; 148 case 'current': 149 $item->link = wl($recent['id'], '', true); 150 break; 151 case 'diff': 152 default: 153 $item->link = wl($recent['id'],'rev='.$recent['date'].'&do=diff'.$recent['date'],true); 154 } 155 156 $item->description = $desc; 157 $item->date = date('r',$recent['date']); 158 $cat = getNS($recent['id']); 159 if($cat) $item->category = $cat; 160 161 $user = null; 162 $user = @$recent['user']; // the @ spares time repeating lookup 163 $item->author = ''; 164 165 if($user){ 166 $userInfo = $auth->getUserData($user); 167 $item->author = $userInfo['name']; 168 if($guardmail) { 169 //cannot obfuscate because some RSS readers may check validity 170 $item->authorEmail = $user.'@'.$recent['ip']; 171 }else{ 172 $item->authorEmail = $userInfo['mail']; 173 } 174 }else{ 175 $item->authorEmail = 'anonymous@'.$recent['ip']; 176 } 177 $rss->addItem($item); 178 } 179} 180 181/** 182 * Add all pages of a namespace to a feedobject 183 * 184 * @author Andreas Gohr <andi@splitbrain.org> 185 */ 186function rssListNamespace(&$rss,$ns){ 187 require_once(DOKU_INC.'inc/search.php'); 188 global $conf; 189 190 $ns=':'.cleanID($ns); 191 $ns=str_replace(':','/',$ns); 192 193 $data = array(); 194 sort($data); 195 search($data,$conf['datadir'],'search_list','',$ns); 196 foreach($data as $row){ 197 $item = new FeedItem(); 198 199 $id = $row['id']; 200 $date = filemtime(wikiFN($id)); 201 $xhtml = p_wiki_xhtml($id,'',false); 202 $desc = cleanDesc($xhtml); 203 $item->title = $id; 204 205 if($conf['useheading']) { 206 $matches = array(); 207 if(preg_match('|<h([1-9])>(.*?)</h\1>|', $xhtml, $matches)) 208 $item->title = trim($matches[2]); 209 } 210 211 $item->link = wl($id,'rev='.$date,true); 212 $item->description = $desc; 213 $item->date = date('r',$date); 214 $rss->addItem($item); 215 } 216} 217 218/** 219 * Clean description for feed inclusion 220 * 221 * Removes HTML tags and line breaks and trims the text to 222 * 250 chars 223 * 224 * @author Andreas Gohr <andi@splitbrain.org> 225 */ 226function cleanDesc($desc){ 227 //start description at text of first paragraph 228 $matches = array(); 229 if(preg_match('/<p>|<p\s.*?>/', $desc, $matches, PREG_OFFSET_CAPTURE)) 230 $desc = substr($desc, $matches[0][1]); 231 232 //remove TOC 233 $desc = preg_replace('!<div class="toc">.*?(</div>\n</div>)!s','',$desc); 234 $desc = strip_tags($desc); 235 $desc = preg_replace('/[\n\r\t]/',' ',$desc); 236 $desc = preg_replace('/ /',' ',$desc); 237 $desc = utf8_substr($desc,0,250); 238 $desc = $desc.'...'; 239 return $desc; 240} 241 242?> 243