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