1<?php 2require_once(SERVERPATH . '/' . ZENFOLDER . '/template-functions.php'); 3 4/** 5 * 6 * Base feed class from which all others descend. 7 * 8 * Plugins will set the <var>feedtype</var> property to the feed desired 9 * <ul> 10 * <li>gallery</li> 11 * <li>news</li> 12 * <li>pages</li> 13 * <li>comments</li> 14 * </ul> 15 * 16 * Feed details are determined by the <var>option</var> property. 17 * Elements of this array and their meaning follow: 18 * <ul> 19 * <li>lang 20 * <ul> 21 * <li><i>locale</i></li> 22 * </ul> 23 * </li> 24 * <li>sortdir 25 * <ul> 26 * <li>desc (default) for descending order</li> 27 * <li>asc for ascending order</li> 28 * </ul> 29 * </li> 30 * <li>sortorder</li> 31 * <ul> 32 * <li><i>latest</i> (default) for the latest uploaded by id (discovery order)</li> 33 * <li><i>latest-date</i> for the latest fetched by date</li> 34 * <li><i>latest-mtime</i> for the latest fetched by mtime</li> 35 * <li><i>latest-publishdate</i> for the latest fetched by publishdate</li> 36 * <li><i>popular</i> for the most popular albums</li> 37 * <li><i>topratedv for the best voted</li> 38 * <li><i>mostrated</i> for the most voted</li> 39 * <li><i>random</i> for random order</li> 40 * <li><i>id</i> internal <var>id</var> order</li> 41 * </ul> 42 * </li> 43 * <li>albumname</li> 44 * <li>albumsmode</li> 45 * <li>folder</li> 46 * <li>size</li> 47 * <li>category</li> 48 * <il>id</li> 49 * <li>itemnumber</li> 50 * <li>type (for comments feed) 51 * <ul> 52 * <li>albums</li> 53 * <li>images</li> 54 * <li>pages</li> 55 * <li>news</li> 56 * </ul> 57 * </li> 58 * </ul> 59 * 60 * 61 * @package core 62 * @subpackage classes\objects 63 */ 64class feed { 65 66 protected $feed = 'feed'; // feed type 67 protected $mode; // feed mode 68 protected $options; // This array will store the options for the feed. 69 //general feed type gallery, news or comments 70 protected $feedtype = NULL; 71 protected $itemnumber = NULL; 72 protected $locale = NULL; // standard locale for lang parameter 73 protected $locale_xml = NULL; // xml locale within feed 74 protected $host = NULL; 75 protected $sortorder = NULL; 76 protected $sortdirection = NULL; 77 //gallery feed specific vars 78 protected $albumfolder = NULL; 79 protected $collection = NULL; 80 protected $albumpath = NULL; 81 protected $imagepath = NULL; 82 protected $imagesize = NULL; 83 protected $modrewritesuffix = NULL; 84 // Zenpage news feed specific 85 protected $catlink = NULL; 86 protected $cattitle = NULL; 87 protected $newsoption = NULL; 88 protected $titleappendix = NULL; 89 //comment feed specific 90 protected $id = NULL; 91 protected $commentfeedtype = NULL; 92 protected $itemobj = NULL; // if comments for an item its object 93 //channel vars 94 protected $channel_title = NULL; 95 protected $feeditem = array(); 96 97 /** 98 * Creates a file name from the options array 99 * 100 * @return string 101 */ 102 protected function getCacheFilename() { 103 $filename = array(); 104 foreach ($this->options as $key => $value) { 105 if (empty($value) || $key == 'albumsmode') { // supposed to be empty always 106 $filename[] = $key; 107 } else { 108 $filename[] = $value; 109 } 110 } 111 $filename = seoFriendly(implode('_', $filename)); 112 return $filename . ".xml"; 113 } 114 115 /** 116 * Starts static caching 117 * 118 */ 119 protected function startCache() { 120 $caching = getOption($this->feed . "_cache") && !zp_loggedin(); 121 if ($caching) { 122 $cachefilepath = SERVERPATH . '/' . STATIC_CACHE_FOLDER . '/' . strtolower($this->feed) . '/' . internalToFilesystem($this->getCacheFilename()); 123 if (file_exists($cachefilepath) AND time() - filemtime($cachefilepath) < getOption($this->feed . "_cache_expire")) { 124 echo file_get_contents($cachefilepath); 125 exitZP(); 126 } else { 127 if (file_exists($cachefilepath)) { 128 @chmod($cachefilepath, 0777); 129 @unlink($cachefilepath); 130 } 131 ob_start(); 132 } 133 } 134 } 135 136 /** 137 * Ends the static caching. 138 * 139 */ 140 protected function endCache() { 141 $caching = getOption($this->feed . "_cache") && !zp_loggedin(); 142 if ($caching) { 143 $cachefilepath = internalToFilesystem($this->getCacheFilename()); 144 if (!empty($cachefilepath)) { 145 $cachefilepath = SERVERPATH . '/' . STATIC_CACHE_FOLDER . '/' . strtolower($this->feed) . '/' . $cachefilepath; 146 mkdir_recursive(SERVERPATH . '/' . STATIC_CACHE_FOLDER . '/' . strtolower($this->feed) . '/', FOLDER_MOD); 147 $pagecontent = ob_get_contents(); 148 ob_end_clean(); 149 if ($fh = @fopen($cachefilepath, "w")) { 150 fputs($fh, $pagecontent); 151 fclose($fh); 152 clearstatcache(); 153 } 154 echo $pagecontent; 155 } 156 } 157 } 158 159 /** 160 * Cleans out the cache folder 161 * 162 * @param string $cachefolder the sub-folder to clean 163 */ 164 function clearCache($cachefolder = NULL) { 165 removeDir(SERVERPATH . '/' . STATIC_CACHE_FOLDER . '/' . strtolower($this->feed) . '/' . $cachefolder, true); 166 } 167 168 function __construct($options) { 169 $this->options = $options; 170 $invalid_options = array(); 171 $this->locale = $this->getLang(); 172 $this->locale_xml = strtr($this->locale, '_', '-'); 173 $this->sortdirection = $this->getSortdir(); 174 $this->sortorder = $this->getSortorder(); 175 switch ($this->feedtype) { 176 case 'comments': 177 $this->commentfeedtype = $this->getCommentFeedType(); 178 $this->id = $this->getId(); 179 $invalid_options = array('albumsmode', 'folder', 'albumname', 'category', 'size'); 180 break; 181 case 'gallery': 182 if (isset($this->options['albumsmode'])) { 183 $this->mode = 'albums'; 184 } 185 if (isset($this->options['folder'])) { 186 $this->albumfolder = $this->getAlbum('folder'); 187 $this->collection = true; 188 } else if (isset($this->options['albumname'])) { 189 $this->albumfolder = $this->getAlbum('albumname'); 190 $this->collection = false; 191 } else { 192 $this->collection = false; 193 } 194 if (is_null($this->sortorder)) { 195 if ($this->mode == "albums") { 196 $this->sortorder = getOption($this->feed . "_sortorder_albums"); 197 } else { 198 $this->sortorder = getOption($this->feed . "_sortorder"); 199 } 200 } 201 $this->imagesize = $this->getImageSize(); 202 $invalid_options = array('id', 'type', 'category'); 203 break; 204 case 'news': 205 if ($this->sortorder == 'latest') { 206 $this->sortorder = NULL; 207 } 208 $this->catlink = $this->getCategory(); 209 if (!empty($this->catlink)) { 210 $catobj = new ZenpageCategory($this->catlink); 211 $this->cattitle = $catobj->getTitle(); 212 $this->newsoption = 'category'; 213 } else { 214 $this->catlink = ''; 215 $this->cattitle = ''; 216 $this->newsoption = 'news'; 217 } 218 $invalid_options = array('folder', 'albumname', 'albumsmode', 'type', 'id', 'size'); 219 break; 220 case 'pages': 221 $invalid_options = array('folder', 'albumname', 'albumsmode', 'type', 'id', 'category', 'size'); 222 break; 223 case 'null': //we just want the class instantiated 224 return; 225 } 226 $this->unsetOptions($invalid_options); // unset invalid options that this feed type does not support 227 if (isset($this->options['itemnumber'])) { 228 $this->itemnumber = (int) $this->options['itemnumber']; 229 } else { 230 $this->itemnumber = getOption($this->feed . '_items'); 231 } 232 } 233 234 /** 235 * Validates and gets the "lang" parameter option value 236 * 237 * @global array $_zp_active_languages 238 * @return string 239 */ 240 protected function getLang() { 241 if (isset($this->options['lang'])) { 242 $langs = generateLanguageList(); 243 $valid = array_values($langs); 244 if (in_array($this->options['lang'], $valid)) { 245 return $this->options['lang']; 246 } 247 } 248 return getOption('locale'); 249 } 250 251 /** 252 * Validates and gets the "sortdir" parameter option value 253 * 254 * @return bool 255 */ 256 protected function getSortdir() { 257 $valid = array('desc', 'asc'); 258 if (isset($this->options['sortdir']) && in_array($this->options['sortdir'], $valid)) { 259 return strtolower($this->options['sortdir']) != 'asc'; 260 } 261 $this->options['sortdir'] = 'desc'; // make sure this is a valid default name 262 return true; 263 } 264 265 /** 266 * Validates and gets the "sortorder" parameter option value 267 * 268 * @return string 269 */ 270 protected function getSortorder() { 271 if (isset($this->options['sortorder'])) { 272 $valid = array('latest', 'latest-date', 'latest-mtime', 'latest-publishdate', 'popular', 'toprated', 'mostrated', 'random', 'id'); 273 if (in_array($this->options['sortorder'], $valid)) { 274 $this->options['sortdir'] = $this->options['sortorder']; // make sure this is a valid default name 275 return $this->options['sortorder']; 276 } else { 277 $this->unsetOptions(array('sortorder')); 278 } 279 } 280 return null; 281 } 282 283 /** 284 * Validates and gets the "type" parameter option value for comment feeds 285 * 286 * @return string 287 */ 288 protected function getCommentFeedType() { 289 $valid = false; 290 if (isset($this->options['type'])) { 291 $valid = array( 'all', 'album' , 'image', 'news', 'page'); 292 if (in_array($this->options['type'], $valid)) { 293 return $this->options['type']; 294 } 295 } 296 return 'all'; 297 } 298 299 /** 300 * Validates and gets the "id" parameter option value for comments feeds of a specific item 301 * 302 * @return int 303 */ 304 protected function getID() { 305 if (isset($this->options['id'])) { 306 $type = $this->getCommentFeedType(); 307 $table = ''; 308 if ($type != 'all') { 309 switch ($this->commentfeedtype) { 310 case 'album': 311 $table = 'albums'; 312 break; 313 case 'image': 314 $table = 'images'; 315 break; 316 case 'news': 317 $table = 'news'; 318 break; 319 case 'page': 320 $table = 'pages'; 321 break; 322 } 323 if ($table) { 324 $id = (int) $this->options['id']; 325 $result = query_single_row('SELECT `id` FROM ' . prefix($table) . ' WHERE id =' . $id); 326 if ($result) { 327 return $id; 328 } 329 } 330 } 331 } 332 $this->unsetOptions(array('id')); 333 return ''; 334 } 335 336 /** 337 * Validates and gets the "folder" or 'albumname" parameter option value 338 * @param string $option "folder" or "albumname" 339 * @return int 340 */ 341 protected function getAlbum($option) { 342 if (in_array($option, array('folder', 'albumname')) && isset($this->options[$option])) { 343 $albumobj = newAlbum($this->options[$option], true, true); 344 if ($albumobj->exists) { 345 return $this->options[$option]; 346 } 347 } 348 $this->unsetOptions(array($option)); 349 return ''; 350 } 351 352 /** 353 * Validates and gets the "category" parameter option value 354 * 355 * @return int 356 */ 357 protected function getCategory() { 358 if (isset($this->options['category']) && class_exists('ZenpageCategory')) { 359 $catobj = new ZenpageCategory($this->options['category']); 360 if ($catobj->exists) { 361 return $this->options['category']; 362 } 363 } 364 $this->unsetOptions(array('category')); 365 return ''; 366 } 367 368 /** 369 * Helper function that gets the images size of the "size" get parameter 370 * 371 * @return string 372 */ 373 protected function getImageSize() { 374 if (isset($this->options['size'])) { 375 $imagesize = (int) $this->options['size']; 376 } else { 377 $imagesize = NULL; 378 } 379 if ($this->mode == 'albums') { 380 if (is_null($imagesize) || $imagesize > getOption($this->feed . '_imagesize_albums')) { 381 $imagesize = getOption($this->feed . '_imagesize_albums'); // un-cropped image size 382 } 383 } else { 384 if (is_null($imagesize) || $imagesize > getOption($this->feed . '_imagesize')) { 385 $imagesize = getOption($this->feed . '_imagesize'); // un-cropped image size 386 } 387 } 388 return $imagesize; 389 } 390 391 /** 392 * Unsets certain option name indices from the $options property. 393 * @param array $options Array of option (parameter) names to be unset 394 */ 395 protected function unsetOptions($options = null) { 396 if (!empty($options)) { 397 foreach ($options as $option) { 398 unset($this->options[$option]); 399 } 400 } 401 } 402 403 protected function getChannelTitleExtra() { 404 switch ($this->sortorder) { 405 default: 406 case 'latest': 407 case 'latest-date': 408 case 'latest-mtime': 409 case 'latest-publishdate': 410 if ($this->mode == 'albums') { 411 $albumextra = ' (' . gettext('Latest albums') . ')'; //easier to understand for translators as if I would treat "images"/"albums" in one place separately 412 } else { 413 $albumextra = ' (' . gettext('Latest images') . ')'; 414 } 415 break; 416 case 'latestupdated': 417 $albumextra = ' (' . gettext('latest updated albums') . ')'; 418 break; 419 case 'popular': 420 if ($this->mode == 'albums') { 421 $albumextra = ' (' . gettext('Most popular albums') . ')'; 422 } else { 423 $albumextra = ' (' . gettext('Most popular images') . ')'; 424 } 425 break; 426 case 'toprated': 427 if ($this->mode == 'albums') { 428 $albumextra = ' (' . gettext('Top rated albums') . ')'; 429 } else { 430 $albumextra = ' (' . gettext('Top rated images') . ')'; 431 } 432 break; 433 case 'random': 434 if ($this->mode == 'albums') { 435 $albumextra = ' (' . gettext('Random albums') . ')'; 436 } else { 437 $albumextra = ' (' . gettext('Random images') . ')'; 438 } 439 break; 440 } 441 return $albumextra; 442 } 443 444 /** 445 * Gets the feed items 446 * 447 * @return array 448 */ 449 public function getitems() { 450 global $_zp_zenpage; 451 switch ($this->feedtype) { 452 case 'gallery': 453 if ($this->mode == "albums") { 454 $items = getAlbumStatistic($this->itemnumber, $this->sortorder, $this->albumfolder, $this->sortdirection); 455 } else { 456 $items = getImageStatistic($this->itemnumber, $this->sortorder, $this->albumfolder, $this->collection, 0, $this->sortdirection); 457 } 458 break; 459 case 'news': 460 switch ($this->newsoption) { 461 case "category": 462 if ($this->sortorder) { 463 $items = getZenpageStatistic($this->itemnumber, 'categories', $this->sortorder, $this->sortdirection); 464 } else { 465 $items = getLatestNews($this->itemnumber, $this->catlink, false, $this->sortdirection); 466 } 467 break; 468 default: 469 case "news": 470 if ($this->sortorder) { 471 $items = getZenpageStatistic($this->itemnumber, 'news', $this->sortorder, $this->sortdirection); 472 } else { 473 // Needed baceause type variable "news" is used by the feed item method and not set by the class method getArticles! 474 $items = getLatestNews($this->itemnumber, '', false, $this->sortdirection); 475 } 476 break; 477 } 478 break; 479 case "pages": 480 if ($this->sortorder) { 481 $items = getZenpageStatistic($this->itemnumber, 'pages', $this->sortorder, $this->sortdirection); 482 } else { 483 $items = $_zp_zenpage->getPages(NULL, false, $this->itemnumber); 484 } 485 break; 486 case 'comments': 487 switch ($this->commentfeedtype) { 488 case 'album': 489 $items = getLatestComments($this->itemnumber, 'album', $this->id); 490 break; 491 case 'image': 492 $items = getLatestComments($this->itemnumber, 'image', $this->id); 493 break; 494 case 'news': 495 case 'page': 496 if (function_exists('getLatestZenpageComments')) { 497 $items = getLatestZenpageComments($this->itemnumber, $this->commentfeedtype, $this->id); 498 } 499 break; 500 case 'gallery': 501 case 'allcomments': 502 case 'all': 503 $items = getLatestComments($this->itemnumber, 'all'); 504 $items_zenpage = array(); 505 if (function_exists('getLatestZenpageComments')) { 506 $items_zenpage = getLatestZenpageComments($this->itemnumber, 'all', $this->id); 507 $items = array_merge($items, $items_zenpage); 508 $items = sortMultiArray($items, 'date', true); 509 $items = array_slice($items, 0, $this->itemnumber); 510 } 511 break; 512 } 513 break; 514 } 515 if (isset($items)) { 516 return $items; 517 } 518 if (TEST_RELEASE) { 519 trigger_error(gettext('Bad ' . $this->feed . ' feed respectively no items available:' . $this->feedtype), E_USER_WARNING); 520 } 521 return NULL; 522 } 523 524 /** 525 * Gets the feed item data in a Zenpage news feed 526 * 527 * @param array $item Titlelink a Zenpage article or filename of an image if a combined feed 528 * @return array 529 */ 530 protected function getitemPages($item, $len) { 531 $obj = new ZenpagePage($item['titlelink']); 532 $feeditem['title'] = $feeditem['title'] = get_language_string($obj->getTitle('all'), $this->locale); 533 $feeditem['link'] = $obj->getLink(); 534 $desc = $obj->getContent($this->locale); 535 $desc = str_replace('//<![CDATA[', '', $desc); 536 $desc = str_replace('//]]>', '', $desc); 537 $feeditem['desc'] = shortenContent($desc, $len, '...'); 538 $feeditem['enclosure'] = ''; 539 $feeditem['category'] = ''; 540 $feeditem['media_content'] = ''; 541 $feeditem['media_thumbnail'] = ''; 542 $feeditem['pubdate'] = date("r", strtotime($obj->getDatetime())); 543 return zp_apply_filter('feed_page', $feeditem, $obj); 544 } 545 546 /** 547 * Gets the feed item data in a comments feed 548 * 549 * @param array $item Array of a comment 550 * @return array 551 */ 552 protected function getitemComments($item) { 553 if ($item['anon']) { 554 $author = ""; 555 } else { 556 $author = " " . gettext("by") . " " . $item['name']; 557 } 558 $commentpath = $imagetag = $title = ''; 559 $obj = null; 560 switch ($item['type']) { 561 case 'images': 562 $title = get_language_string($item['title']); 563 $obj = newImage(NULL, array('folder' => $item['folder'], 'filename' => $item['filename'])); 564 $link = $obj->getlink(); 565 $feeditem['pubdate'] = date("r", strtotime($item['date'])); 566 $category = get_language_string($item['albumtitle']); 567 $website = $item['website']; 568 $title = $category . ": " . $title; 569 $commentpath = PROTOCOL . '://' . $this->host . $link . "#zp_comment_id_" . $item['id']; 570 break; 571 case 'albums': 572 $obj = newAlbum($item['folder']); 573 $link = rtrim($obj->getLink(), '/'); 574 $feeditem['pubdate'] = date("r", strtotime($item['date'])); 575 $title = get_language_string($item['albumtitle']); 576 $website = $item['website']; 577 $commentpath = PROTOCOL . '://' . $this->host . $link . "#zp_comment_id_" . $item['id']; 578 break; 579 case 'news': 580 case 'pages': 581 if (extensionEnabled('zenpage')) { 582 $album = ''; 583 $feeditem['pubdate'] = date("r", strtotime($item['date'])); 584 $category = ''; 585 $title = get_language_string($item['title']); 586 $titlelink = $item['titlelink']; 587 $website = $item['website']; 588 if ($item['type'] == 'news') { 589 $obj = new ZenpageNews($titlelink); 590 } else { 591 $obj = new ZenpagePage($titlelink); 592 } 593 $commentpath = PROTOCOL . '://' . $this->host . html_encode($obj->getLink()) . "#zp_comment_id_" . $item['id']; 594 } else { 595 $commentpath = ''; 596 } 597 598 break; 599 } 600 $feeditem['title'] = getBare($title . $author); 601 $feeditem['link'] = $commentpath; 602 $feeditem['desc'] = $item['comment']; 603 return zp_apply_filter('feed_comment', $feeditem, $item, $obj); 604 } 605 606 static protected function feed404() { 607 header("HTTP/1.0 404 Not Found"); 608 header("Status: 404 Not Found"); 609 include(SERVERPATH . '/' . ZENFOLDER . '/404.php'); 610 exitZP(); 611 } 612 613}