[ Index ] |
|
Code source de LifeType 1.2.4 |
1 <?php 2 3 lt_include( PLOG_CLASS_PATH.'class/dao/model.class.php' ); 4 lt_include( PLOG_CLASS_PATH.'class/dao/article.class.php' ); 5 lt_include( PLOG_CLASS_PATH.'class/dao/articlestatus.class.php' ); 6 lt_include( PLOG_CLASS_PATH.'class/dao/daocacheconstants.properties.php' ); 7 8 /** 9 * \ingroup DAO 10 * 11 * Model for the Articles 12 */ 13 class Articles extends Model 14 { 15 function Articles() 16 { 17 $this->Model(); 18 19 $this->table = $this->getPrefix()."articles"; 20 $this->pk = "id"; 21 } 22 23 /** 24 * Gets an article from the database, given its id. Also manages the 25 * cache for articles. 26 * 27 * @param articleId Identifier of the article we want to fetch 28 * @return Returns an Article object or 'false' otherwise. 29 */ 30 function getArticle( $articleId ) 31 { 32 return( $this->get( "id", $articleId, CACHE_ARTICLES )); 33 } 34 35 /** 36 * Gets an article from the database, given its id 37 * 38 * @param artId Identifier of the article we want to fetch 39 * @param blogId If set, the article must belong to the given blog 40 * @return Returns an Article object or 'false' otherwise. 41 */ 42 function getBlogArticle( $artId, 43 $blogId = -1, 44 $includeHiddenFields = true, 45 $date = -1, 46 $categoryId = -1, 47 $userId = -1, 48 $status = POST_STATUS_ALL ) 49 50 { 51 $article = $this->getArticle( $artId ); 52 53 if( !$article ) 54 return false; 55 56 if( $blogId != -1 ) { 57 if( $article->getBlogId() != $blogId ) { 58 return false; 59 } 60 } 61 if( $userId != -1 ) { 62 if( $article->getUserId() != $userId ) { 63 return false; 64 } 65 } 66 if( $status != POST_STATUS_ALL && $article->getStatus() != $status ) 67 return false; 68 69 return $article; 70 } 71 72 /** 73 * Gets an article from the database, given its slug, this is used 74 * with the fancy permalinks 75 * 76 * @param artTitle Identifier of the article we want to fetch 77 * @param blogId If set, the article must belong to the given blog 78 * @return Returns an Article object or 'false' otherwise. 79 */ 80 function getBlogArticleByTitle( $articleTitle, 81 $blogId, 82 $includeHiddenFields = true, 83 $date = -1, 84 $categoryId = -1, 85 $userId = -1, 86 $status = POST_STATUS_PUBLISHED, 87 $maxDate = -1 ) 88 { 89 // load all the articles with the same title 90 $articles = $this->getMany( "slug", 91 $articleTitle, 92 CACHE_ARTICLES_BYNAME, 93 Array( CACHE_ARTICLES => "getId" )); 94 $found = false; 95 if($articles){ 96 foreach( $articles as $article ) { 97 if( $article->getBlogId() == $blogId && $this->check( $article, $date, $categoryId, 98 $status, $userId, $maxDate )) { 99 $found = true; 100 break; 101 } 102 // if not, continue with the next... 103 } 104 } 105 106 if( !$found ) { 107 $article = null; 108 } 109 110 return( $article ); 111 } 112 113 /** 114 * @private 115 */ 116 function _getBlogArticleFromQuery($query) 117 { 118 // we send the query and then fetch the first array with the result 119 $result = $this->Execute( $query ); 120 121 if( $result == false ) 122 return false; 123 124 if ( $result->RecordCount() == 0){ 125 $result->Close(); 126 return false; 127 } 128 129 $row = $result->FetchRow( $result ); 130 $article = $this->mapRow( $row ); 131 $result->Close(); 132 133 return $article; 134 } 135 136 /** 137 * Returns the article that goes after the one we give as the parameter. 138 * 139 * @param article An article object 140 * @return An Article object with the next article or false if there was no article. 141 */ 142 function getBlogNextArticle( $article ) 143 { 144 lt_include( PLOG_CLASS_PATH.'class/data/timestamp.class.php' ); 145 $blogInfo = $article->getBlogInfo(); 146 $blogSettings = $blogInfo->getSettings(); 147 148 // we need to keep the timestamp in mind 149 $date = $article->getDateObject(); 150 $articleCorrectedDate = Timestamp::getDateWithOffset( $article->getDate(), 151 -($article->getTimeOffset())); 152 153 $query = "SELECT * FROM ".$this->getPrefix()."articles 154 WHERE blog_id = ".$article->getBlogId()." AND 155 date > '".$articleCorrectedDate."' AND 156 status = ".POST_STATUS_PUBLISHED; 157 if(!$blogSettings->getValue("show_future_posts_in_calendar")) 158 $query .= " AND date <= NOW()"; 159 $query .= " ORDER BY DATE ASC LIMIT 0,1"; 160 161 $article = $this->_getBlogArticleFromQuery( $query, false ); 162 163 return( $article ); 164 } 165 166 /** 167 * Returns the article that goes before the one we give as the parameter. 168 * 169 * @param article An article object 170 * @return An Article object with the previous article or false if there was no article. 171 */ 172 function getBlogPrevArticle( $article ) 173 { 174 lt_include( PLOG_CLASS_PATH.'class/data/timestamp.class.php' ); 175 176 // we need to keep the timestamp in mind 177 $date = $article->getDateObject(); 178 $articleCorrectedDate = Timestamp::getDateWithOffset( $article->getDate(), 179 -($article->getTimeOffset())); 180 181 $query = "SELECT * FROM ".$this->getPrefix()."articles 182 WHERE blog_id = ".$article->getBlogId()." AND 183 date < '".$articleCorrectedDate."' AND 184 status = ".POST_STATUS_PUBLISHED." 185 ORDER BY DATE DESC 186 LIMIT 0,1"; 187 188 $article = $this->_getBlogArticleFromQuery( $query, false ); 189 190 return( $article ); 191 } 192 193 /** 194 * @see getBlogArticles 195 */ 196 function getNumBlogArticles( $blogId, 197 $date = -1, 198 $categoryId = 0, 199 $status = POST_STATUS_PUBLISHED, 200 $userId = 0, 201 $maxDate = 0, 202 $searchTerms = "") 203 { 204 $postStatus = $status; 205 $prefix = $this->getPrefix(); 206 $where = $this->buildWhere( $blogId, $date, -1, $categoryId, $status, $userId, $maxDate, $searchTerms ); 207 $query = "SELECT COUNT(a.id) AS total FROM {$prefix}articles a, {$prefix}articles_categories c, {$prefix}article_categories_link l WHERE $where "; 208 209 $result = $this->_db->Execute( $query ); 210 211 if( !$result ) 212 return 0; 213 214 /** 215 * :HACK: 216 * this really is a dirty hack... 217 */ 218 if( $categoryId > 0 ) { 219 $row = $result->FetchRow(); 220 $number = $row["total"]; 221 } 222 else { 223 $number = $result->RowCount(); 224 } 225 $result->Close(); 226 return( $number ); 227 } 228 229 /** 230 * @private 231 * returns true whether the given article matches the given conditions, or false otherwise 232 */ 233 function check( $article, 234 $date = -1, 235 $categoryId = 0, 236 $status = POST_STATUS_PUBLISHED, 237 $userId = 0, 238 $maxDate = 0 ) 239 { 240 if( $status != POST_STATUS_ALL ) { 241 if( $article->getStatus() != $status ) 242 return false; 243 } 244 if( $categoryId > 0 ) { 245 $found = false; 246 foreach( $article->getCategoryIds() as $catId ) { 247 if( $categoryId == $catId ) { 248 $found = true; 249 } 250 } 251 if( !$found ) 252 return false; 253 } 254 if( $userId > 0 ) { 255 if( $article->getUserId() != $userId ) 256 return false; 257 } 258 if( $date != -1 && $maxDate == 0 ) { 259 $t = $article->getDateObject(); 260 $postDate = substr($t->getTimestamp(),0,strlen($maxDate)); 261 if( $postDate != $date ) { 262 return false; 263 } 264 } 265 elseif( $maxDate > 0 && $date != -1 ) { 266 $t = $article->getDateObject(); 267 $postDate = substr($t->getTimestamp(),0,strlen($maxDate)); 268 // we need to check both ends of the range 269 if( $postDate >= $maxDate || $postDate <= $date) { 270 return false; 271 } 272 } 273 274 return( true ); 275 } 276 277 /** 278 * @see Model::getSearchCondition 279 */ 280 function getSearchConditions( $searchTerms ) 281 { 282 /** 283 * this method is kinda difficult... In order to generate a valid search condition 284 * we need to actually run the LIKE query in the {$prefix}_articles_text table first since 285 * Model::getAll() and Model::getMany() can not and will not perform JOIN operations, which is 286 * what we would need in this situation. This means that from one single complex query with 287 * LIKE and JOIN, we get two less complex queries (hopefully!) 288 * 289 * This method will return a string containing something like 290 * 291 * WHERE article_id IN (id, of, the, articles, whose, article_text, matched) 292 */ 293 294 // prepare the query string 295 lt_include( PLOG_CLASS_PATH."class/dao/searchengine.class.php" ); 296 $searchTerms = SearchEngine::adaptSearchString( $searchTerms ); 297 298 $db =& Db::getDb(); 299 if( $db->isFullTextSupported()) { 300 // fastpath when FULLTEXT is supported 301 $whereString = " MATCH(normalized_text, normalized_topic) AGAINST ('{$searchTerms}' IN BOOLEAN MODE)"; 302 } 303 else { 304 // Split the search term by space 305 $query_array = explode(' ', $searchTerms); 306 307 // For each search terms, I should make a like query for it 308 $whereString = "("; 309 $whereString .= "((normalized_topic LIKE '%{$query_array[0]}%') OR (normalized_text LIKE '%{$query_array[0]}%'))"; 310 for ( $i = 1; $i < count($query_array); $i = $i + 1) { 311 312 $whereString .= " AND ((normalized_topic LIKE '%{$query_array[$i]}%') OR (normalized_text LIKE '%{$query_array[$i]}%'))"; 313 } 314 $whereString .= " OR ((normalized_topic LIKE '%{$searchTerms}%') OR (normalized_text LIKE '%{$searchTerms}%'))"; 315 $whereString .= ")"; 316 } 317 318 $query = "SELECT article_id FROM ".$this->getPrefix()."articles_text 319 WHERE $whereString"; 320 321 // execute the query and process the result if any 322 $result = $this->Execute( $query ); 323 if( !$result ) 324 return( "" ); 325 326 $ids = Array(); 327 while( $row = $result->FetchRow()) { 328 $ids[] = $row['article_id']; 329 } 330 $result->Close(); 331 if ( !empty( $ids ) ) 332 $searchCondition = 'a.id IN ('.implode( ', ', $ids ).')'; 333 else 334 $searchCondition = 'a.id = -1'; 335 336 return( $searchCondition ); 337 } 338 339 /** 340 * builds a WHERE clause for a query 341 * 342 * @private 343 */ 344 function buildWhere( $blogId, 345 $date = -1, 346 $amount = -1, 347 $categoryId = 0, 348 $status = 0, 349 $userId = 0, 350 $maxDate = 0, 351 $searchTerms = "" ) 352 { 353 $postStatus = $status; 354 $prefix = $this->getPrefix(); 355 if($blogId == -1){ 356 $query = "a.blog_id = a.blog_id"; 357 } 358 else{ 359 $query = "a.blog_id = ".Db::qstr($blogId); 360 } 361 if( $date != -1 ) { 362 // consider the time difference 363 lt_include( PLOG_CLASS_PATH."class/dao/blogs.class.php" ); 364 $blogs = new Blogs(); 365 $blogInfo = $blogs->getBlogInfo( $blogId ); 366 $blogSettings = $blogInfo->getSettings(); 367 $timeDifference = $blogSettings->getValue( "time_offset" ); 368 $SecondsDiff = $timeDifference * 3600; 369 $query .= " AND FROM_UNIXTIME(UNIX_TIMESTAMP(a.date)+$SecondsDiff)+0 LIKE '$date%'"; 370 } 371 372 // the common part "c.id = a.category_id" is needed so that 373 // we don't get one article row as many times as the amount of categories 374 // we have... due to the sql 'join' operation we're carrying out 375 if( $categoryId == -1 ) 376 $query .= " AND c.id = l.category_id AND a.id = l.article_id "; 377 else { 378 if( $categoryId > 0 ) 379 $query .= " AND a.id = l.article_id AND l.category_id = $categoryId AND c.id = l.category_id"; 380 else { 381 $query .= " AND c.id = l.category_id AND a.id = l.article_id AND c.in_main_page = 1"; 382 } 383 } 384 385 if( $status > 0 ) 386 $query .= " AND a.status = '$postStatus'"; 387 if( $userId > 0 ) 388 $query .= " AND a.user_id = ".Db::qstr($userId); 389 if( $maxDate > 0 ) 390 $query .= " AND a.date <= '$maxDate'"; 391 392 // in case there were some search terms specified as parameters... 393 if( $searchTerms != "" ) { 394 $whereString = $this->getSearchConditions( $searchTerms ); 395 // and add it to the current search 396 $query .=" AND {$whereString} "; 397 } 398 399 if( $categoryId <= 0 ) 400 $query .= " GROUP BY a.id "; 401 402 return $query; 403 } 404 405 /** 406 * Returns all the articles for a given blog, according to the conditions specified in 407 * the call. If this function is too cumbersome to use (I reckon it might be, 408 * too many parameters that have been added over time due to new requirements). 409 * 410 * @param blogId Identifier of the blog from where we want to fetch the articles 411 * @param date date in MySQL TIMESTAMP(14) format 412 * @param amount The maximum amount of posts that we would like to be returned. 413 * @param categoryId A category identifier. If specified, only the posts of 414 the given category will be returned 415 * @param status If specified, only the posts with given status will be returned. 416 * @param userId If specified, only the posts that belong to the specified user 417 will be returned 418 * @param maxDate a date in MySQL TIMESTAMP(14) 419 * @param searchTerms in case we would like to further refine the filtering, 420 we can also use search features 421 * @return Returns an array with all the articles from the given blog 422 */ 423 424 function getBlogArticles( $blogId, 425 $date = -1, 426 $amount = -1, 427 $categoryId = 0, 428 $status = POST_STATUS_PUBLISHED, 429 $userId = 0, 430 $maxDate = 0, 431 $searchTerms = "", 432 $page = -1 ) 433 { 434 // build the query 435 // the query gets quite complicated to build because we have to take care of plenty 436 // of conditions, such as the maximum date, the amount, the category, 437 // wether the category has to be shown in the main page or not, etc... 438 $postStatus = $status; 439 $prefix = $this->getPrefix(); 440 $where = $this->buildWhere( $blogId, $date, $amount, $categoryId, $status, $userId, $maxDate, $searchTerms ); 441 $query = "SELECT a.id as id, a.id, a.date, 442 a.user_id,a.blog_id,a.status,a.properties, 443 a.num_reads, a.slug, 1 AS relevance, a.num_comments AS num_comments, 444 a.num_nonspam_comments AS num_nonspam_comments, a.num_trackbacks AS num_trackbacks, 445 a.num_nonspam_trackbacks AS num_nonspam_trackbacks, 446 a.global_category_id AS global_category_id, 447 a.in_summary_page AS in_summary_page, 448 a.modification_date AS modification_date 449 FROM {$prefix}articles a, {$prefix}articles_categories c, 450 {$prefix}article_categories_link l"; 451 if( $searchTerms != "" ) 452 $query .= ", {$prefix}articles_text t "; 453 $query .= " WHERE "; 454 if( $searchTerms != "" ) 455 $query .= " t.article_id = a.id AND "; 456 $query .= " $where"; 457 458 459 // if we're doing a search, we should sort by relevance 460 if( $searchTerms != "" ) { 461 $query .= " ORDER BY relevance"; 462 } 463 else { 464 // check article order preference, default to newest first 465 lt_include( PLOG_CLASS_PATH."class/dao/blogs.class.php" ); 466 $blogs = new Blogs(); 467 $blogInfo = $blogs->getBlogInfo( $blogId ); 468 $blogSettings = $blogInfo->getSettings(); 469 470 if($blogSettings->getValue( "articles_order" ) == 1) { 471 $query .= " ORDER BY a.date ASC"; 472 } else { 473 $query .= " ORDER BY a.date DESC"; 474 } 475 } 476 477 // we don't need limits if we're getting the posts for a given day 478 if( ($amount > 0) && ($date == -1) && ($page == -1 )) 479 $query .= " LIMIT $amount;"; 480 481 // in case we're using a paged display 482 if( $page > 0 ) { 483 $start = (($page - 1) * $amount); 484 $query .= " LIMIT $start, $amount"; 485 } 486 487 // execute the query 488 $result = $this->Execute( $query ); 489 490 if( !$result ) 491 return Array(); 492 493 if( $result->RowCount() == 0 ){ 494 $result->Close(); 495 return Array(); 496 } 497 498 $articles = Array(); 499 while( $row = $result->FetchRow()) { 500 // map the row to an object 501 $article = $this->mapRow( $row ); 502 $articles[] = $article; 503 // and cache it for later use, we might need it 504 $this->_cache->setData( $article->getId(), CACHE_ARTICLES, $article ); 505 $this->_cache->setMultipleData( $article->getPostSlug(), CACHE_ARTICLES_BYNAME, $article ); 506 } 507 508 $result->Close(); 509 510 return $articles; 511 } 512 513 /** 514 * Gets the number of posts per month per year from the database 515 * 516 * @param blogId The numeric identifier of the blog from which we'd like to 517 calculate this values 518 * @return A 2-dimensional associative array where the first index is the year and the second 519 * index is the number of the month: result[2003][11] = _posts for november 2003_ 520 */ 521 function getNumberPostsPerMonth( $blogId ) 522 { 523 524 $archives = $this->_cache->getData( $blogId, CACHE_ARTICLESPERMONTH ); 525 $arcnives = false; 526 527 if( !$archives ) { 528 lt_include( PLOG_CLASS_PATH . 'class/dao/blogs.class.php' ); 529 $blogs = new Blogs(); 530 $blogInfo = $blogs->getBlogInfo( $blogId ); 531 $blogSettings = $blogInfo->getSettings(); 532 533 $prefix = $this->getPrefix(); 534 if( $blogSettings->getValue("show_future_posts_in_calendar") ) 535 $numPostsPerMonthQuery = "SELECT COUNT(id) AS 'count', 536 YEAR(date) AS 'year', 537 MONTH(date) AS 'month' 538 FROM {$prefix}articles 539 WHERE status = 1 AND blog_id = $blogId 540 GROUP BY YEAR(date),MONTH(date) 541 ORDER BY YEAR(date) DESC,MONTH(date) DESC;"; 542 else 543 $numPostsPerMonthQuery = "SELECT COUNT(id) AS 'count', 544 YEAR(date) AS 'year', 545 MONTH(date) AS 'month' 546 FROM {$prefix}articles 547 WHERE status = 1 AND blog_id = $blogId 548 AND date <= NOW() 549 GROUP BY YEAR(date),MONTH(date) 550 ORDER BY YEAR(date) DESC,MONTH(date) DESC;"; 551 552 $result = $this->Execute( $numPostsPerMonthQuery); 553 if( $result == false ) 554 return false; 555 556 $archives = Array(); 557 while( $row = $result->FetchRow()) { 558 $archives[$row["year"]][$row["month"]] = $row["count"]; 559 } 560 $result->Close(); 561 $this->_cache->setData( $blogId, CACHE_ARTICLESPERMONTH, $archives ); 562 } 563 564 return $archives; 565 } 566 567 /** 568 * like the one above but with a few changes, such as always showing posts in the future 569 * and returning all the months in the array, even if the total amount was '0' 570 * Only used in the "editPosts" screen of the admin interface 571 */ 572 function getNumberPostsPerMonthAdmin( $blogId ) 573 { 574 $prefix = $this->getPrefix(); 575 $numPostsPerMonthQuery = "SELECT DISTINCT YEAR(date) AS year,MONTH(date) AS month 576 FROM {$prefix}articles 577 WHERE blog_id = '".Db::qstr($blogId)."' 578 ORDER BY YEAR(date) DESC,MONTH(date) DESC;"; 579 580 $result = $this->Execute( $numPostsPerMonthQuery); 581 if( !$result ) 582 return Array(); 583 584 while( $row = $result->FetchRow()) { 585 $year = $row["year"]; 586 $month = $row["month"]; 587 $archives[$year][$month] = 1; 588 } 589 $result->Close(); 590 591 return $archives; 592 } 593 594 /** 595 * The same as the one above but for just one month 596 * 597 * @param blogId The identifier of the blog from which we'd like to calculate this 598 * @param year Yeardddd 599 * @param month Month from which we'd like to calculate this 600 * @return An associative array where the index is the day of the month and the value 601 * is the number of posts made that day. 602 */ 603 function getDaysWithPosts( $blogId, $year = null , $month = null ) 604 { 605 lt_include( PLOG_CLASS_PATH.'class/data/timestamp.class.php' ); 606 $t = new Timestamp(); 607 // if month and/or year are empty, get the current ones 608 if( $year == null ) 609 $year = $t->getYear(); 610 if( $month == null ) 611 $month = $t->getMonth(); 612 613 $blogs = new Blogs(); 614 $blogInfo = $blogs->getBlogInfo( $blogId ); 615 $blogSettings = $blogInfo->getSettings(); 616 617 $timeDifference = $blogSettings->getValue( "time_offset" ); 618 619 $SecondsDiff = $timeDifference * 3600; 620 621 // check whether we're supposed to show posts that happen in the future or not 622 $prefix = $this->getPrefix(); 623 $numPostsPerDayQuery = "SELECT date 624 FROM {$prefix}articles 625 WHERE status = 1 626 AND blog_id = $blogId"; 627 if( !$blogSettings->getValue( "show_future_posts_in_calendar" )) { 628 $numPostsPerDayQuery .= " AND MONTH(FROM_UNIXTIME(UNIX_TIMESTAMP(date) + $SecondsDiff)) = $month "; 629 $numPostsPerDayQuery .= " AND YEAR(FROM_UNIXTIME(UNIX_TIMESTAMP(date) + $SecondsDiff)) = $year "; 630 $numPostsPerDayQuery .= " AND date <= NOW()"; 631 } else { 632 $numPostsPerDayQuery .= " AND MONTH(FROM_UNIXTIME(UNIX_TIMESTAMP(date) + $SecondsDiff)) = $month "; 633 $numPostsPerDayQuery .= " AND YEAR(FROM_UNIXTIME(UNIX_TIMESTAMP(date) + $SecondsDiff)) = $year "; 634 } 635 636 $result = $this->Execute( $numPostsPerDayQuery ); 637 638 if( !$result ) 639 return Array(); 640 641 lt_include( PLOG_CLASS_PATH.'class/data/timestamp.class.php' ); 642 643 $postsPerDay = Array(); 644 while( $row = $result->FetchRow()) { 645 // we can use this auxiliary function to help us... 646 $date = Timestamp::getTimestampWithOffset( $row['date'], $timeDifference ); 647 $day = $date->getDay(); 648 $postsPerDay[intval($day)] = 1; 649 } 650 $result->Close(); 651 652 return $postsPerDay; 653 } 654 655 /** 656 * adds records to the table that holds the many-to-many relationship between 657 * categories and posts in the blog. 658 * 659 * @param articleId 660 * @param categories 661 * @return True 662 */ 663 function addPostCategoriesLink( $article ) 664 { 665 lt_include( PLOG_CLASS_PATH.'class/database/db.class.php' ); 666 lt_include( PLOG_CLASS_PATH."class/dao/articlecategories.class.php" ); 667 668 $articleId = $article->getId(); 669 $categories = $article->getCategoryIds(); 670 671 // nothing to do if the $categories array is not ehem, an array :) 672 if( !is_array( $categories )) 673 return true; 674 675 $articleCategories = new ArticleCategories(); 676 foreach( $categories as $categoryId ) { 677 678 $query = "INSERT INTO ".$this->getPrefix()."article_categories_link (article_id, category_id) VALUES (". 679 "'".Db::qstr( $articleId )."', '".Db::qstr( $categoryId )."')"; 680 681 $this->Execute( $query ); 682 683 $category = $articleCategories->getCategory( $categoryId ); 684 if( $article->getStatus() == POST_STATUS_PUBLISHED ) { 685 //$category->setNumPublishedArticles( $category->getNumPublishedArticles() +1 ); 686 lt_include( PLOG_CLASS_PATH."class/data/timestamp.class.php" ); 687 $category->setLastModification( new Timestamp()); 688 } 689 //$category->setNumArticles( $category->getNumAllArticles() + 1 ); 690 $articleCategories->updateCategory( $category ); 691 } 692 693 return true; 694 } 695 696 /** 697 * removes the relationship between posts and categories from the database. This 698 * method should only be used when removing an article!! 699 */ 700 function deletePostCategoriesLink( $article ) 701 { 702 lt_include( PLOG_CLASS_PATH.'class/database/db.class.php' ); 703 lt_include( PLOG_CLASS_PATH."class/dao/articlecategories.class.php" ); 704 705 $articleId = $article->getId(); 706 $query = "DELETE FROM ".$this->getPrefix()."article_categories_link WHERE article_id = '".Db::qstr( $article->getId())."'"; 707 708 if( ( $result = $this->Execute( $query ))) { 709 // updated the category counters 710 $articleCategories = new ArticleCategories(); 711 foreach( $article->getCategories() as $category ) { 712 $articleCategories->updateCategory( $category ); 713 } 714 // clean the cache that contains the links 715 $this->_cache->removeData( $article->getId(), CACHE_ARTICLE_CATEGORIES_LINK ); 716 } 717 718 return( $result ); 719 } 720 721 /** 722 * update the links between a post and its categories 723 * (basically, we use brute force here: first remove them and then recreate them again... 724 * It takes less time than going through all of them and checking if they exist or not. 725 * 726 * @private 727 */ 728 function updatePostCategoriesLink( $article, $oldArticle = null ) 729 { 730 if( $oldArticle != null ) { 731 if( !$this->deletePostCategoriesLink( $oldArticle )) 732 return false; 733 } 734 735 return $this->addPostCategoriesLink( $article ); 736 } 737 738 /** 739 * Updates global article category counters 740 * 741 * @private 742 */ 743 function updateGlobalArticleCategoriesLink( $article, $oldArticle = null ) 744 { 745 lt_include( PLOG_CLASS_PATH."class/dao/globalarticlecategories.class.php" ); 746 $cats = new GlobalArticleCategories(); 747 $artCategory = $article->getGlobalCategory(); 748 749 if ($artCategory) { 750 $artCategory->setNumArticles( $this->getNumItems( $this->getPrefix()."articles", "global_category_id = ".$artCategory->getId())); 751 $artCategory->setNumActiveArticles( $this->getNumItems( $this->getPrefix()."articles", "global_category_id = ".$artCategory->getId()." AND status = ".POST_STATUS_PUBLISHED )); 752 $cats->updateGlobalArticleCategory( $artCategory ); 753 } 754 755 if( $oldArticle ) { 756 $oldCategory = $oldArticle->getGlobalCategory(); 757 if ($oldCategory) 758 { 759 $oldCategory->setNumArticles( $this->getNumItems( $this->getPrefix()."articles", "global_category_id = ".$oldCategory->getId())); 760 $oldCategory->setNumActiveArticles( $this->getNumItems( $this->getPrefix()."articles", "global_category_id = ".$oldCategory->getId()." AND status = ".POST_STATUS_PUBLISHED )); 761 $cats->updateGlobalArticleCategory( $oldCategory ); 762 } 763 } 764 765 return( true ); 766 } 767 768 /** 769 * Adds a new article to the database 770 * 771 * @param newArticle An Article object with all the necessary information. 772 * @return Returns true if article was added successfully or false otherwise. If successful, it will modify the parmeter 773 * passed by reference and set its database id. 774 */ 775 function addArticle( &$newArticle ) 776 { 777 // Check if we need to force the article slug to be unique 778 lt_include( PLOG_CLASS_PATH."class/config/config.class.php" ); 779 $config =& Config::getConfig(); 780 if($config->getValue("force_posturl_unique")) 781 { 782 $slug = $newArticle->getPostSlug(); 783 $i = 1; 784 // check if there already is an article with the same mangled name 785 while($this->getBlogArticleByTitle($slug, 786 $newArticle->getBlog())) 787 { 788 $i++; 789 // and if so, assign a new one 790 // if we already tried with blogname+"i" we have 791 // to strip "i" before adding it again! 792 $slug = substr($slug, 0, 793 ($i > 2) ? strlen($slug)-strlen($i-1) : strlen($slug)).$i; 794 } 795 $newArticle->setPostSlug($slug); 796 } 797 798 lt_include( PLOG_CLASS_PATH.'class/dao/customfields/customfields.class.php' ); 799 lt_include( PLOG_CLASS_PATH."class/dao/blogs.class.php" ); 800 801 $result = $this->add( $newArticle ); 802 803 if( !$result ) 804 return false; 805 806 $this->addArticleText( $newArticle ); 807 808 // and create the link between the post and its categories 809 $this->addPostCategoriesLink( $newArticle ); 810 811 // update global article categories 812 $this->updateGlobalArticleCategoriesLink( $newArticle ); 813 814 // and save the custom fields 815 $customFields = new CustomFieldsValues(); 816 $fields = $newArticle->getCustomFields(); 817 if( is_array( $fields )) { 818 foreach( $fields as $field ) { 819 $customFields->addCustomFieldValue( $field->getFieldId(), 820 $field->getValue(), 821 $newArticle->getId(), 822 $newArticle->getBlogId()); 823 } 824 } 825 826 // update the blog counters 827 if( $newArticle->getStatus() == POST_STATUS_PUBLISHED ) { 828 lt_include( PLOG_CLASS_PATH."class/data/timestamp.class.php" ); 829 $blogs = new Blogs(); 830 $blogInfo = $newArticle->getBlogInfo(); 831 $blogInfo->setTotalPosts( $blogInfo->getTotalPosts() + 1 ); 832 $blogInfo->setUpdateDate( Timestamp::getNowTimestamp()); 833 $blogs->updateBlog( $blogInfo ); 834 } 835 836 // and finally clear the cache :) 837 lt_include( PLOG_CLASS_PATH."class/dao/recentarticles.class.php" ); 838 RecentArticles::resetRecentArticlesCache( $newArticle->getBlogId()); 839 $this->_cache->removeData( $newArticle->getBlogId(), CACHE_ARTICLESPERMONTH ); 840 841 return( $newArticle->getId()); 842 } 843 844 /** 845 * saves the text of an article to the database 846 * 847 * @param newArticle the Article object that we have just saved 848 * @param 849 * @return true if successful or false otherwise 850 */ 851 function addArticleText( $newArticle ) 852 { 853 lt_include( PLOG_CLASS_PATH . 'class/data/textfilter.class.php' ); 854 855 $filter = new Textfilter(); 856 $query = "INSERT INTO ".$this->getPrefix()."articles_text (article_id, topic, text, normalized_text, normalized_topic, mangled_topic) ". 857 " VALUES ('".Db::qstr($newArticle->getId())."',". 858 "'".Db::qstr($newArticle->getTopic())."',". 859 "'".Db::qstr($newArticle->getText(false))."',". 860 "'".Db::qstr($filter->normalizeText( $newArticle->getText(false)))."',". 861 "'".Db::qstr($filter->normalizeText( $newArticle->getTopic()))."',". 862 "'')"; 863 864 return( $this->Execute( $query )); 865 } 866 867 /** 868 * returns the text fields of an article 869 * 870 * @param articleId 871 * @return an array 872 */ 873 function getArticleText( $articleId ) 874 { 875 $text = $this->_cache->getData( $articleId, CACHE_ARTICLETEXT ); 876 877 if( !$text ) { 878 $query = "SELECT text, normalized_text, topic, normalized_topic FROM ".$this->getPrefix()."articles_text ". 879 "WHERE article_id = '".Db::qstr( $articleId )."'"; 880 $result = $this->Execute( $query ); 881 $text = $result->FetchRow(); 882 $result->Close(); 883 $this->_cache->setData( $articleId, CACHE_ARTICLETEXT, $text ); 884 } 885 886 return $text; 887 } 888 889 /** 890 * updates the text of an article 891 * 892 * @param article an Article object 893 * @return true if successful or false otherwise 894 */ 895 function updateArticleText( $article ) 896 { 897 lt_include( PLOG_CLASS_PATH."class/data/textfilter.class.php" ); 898 $filter = new Textfilter(); 899 900 $query = "UPDATE ".$this->getPrefix()."articles_text SET ". 901 "topic = '".Db::qstr($article->getTopic())."', ". 902 "text = '".Db::qstr($article->getText(false))."', ". 903 "normalized_text = '".Db::qstr($filter->normalizeText( $article->getText(false)))."', ". 904 "normalized_topic = '".Db::qstr($filter->normalizeText( $article->getTopic()))."' ". 905 "WHERE article_id = '".Db::qstr( $article->getId())."'"; 906 907 $this->_cache->removeData( $article->getId(), CACHE_ARTICLETEXT ); 908 909 return($this->Execute( $query )); 910 } 911 912 /** 913 * Updates an article in the database 914 * 915 * @param article The Article object that we'd like to update in the database. 916 * @return Returns true if update was successful or false otherwise. 917 */ 918 function updateArticle( $article ) 919 { 920 // Check if we need to force the article slug to be unique 921 lt_include( PLOG_CLASS_PATH."class/config/config.class.php" ); 922 $config =& Config::getConfig(); 923 if($config->getValue("force_posturl_unique")) 924 { 925 $slug = $article->getPostSlug(); 926 $i = 1; 927 // check if there already is an article with the same mangled name 928 while($existingArticle = $this->getBlogArticleByTitle($slug, 929 $article->getBlog())) 930 { 931 // if we found ourselves, it is okay to keep using this name 932 if($existingArticle->getId() == $article->getId()) 933 break; 934 935 // found a match, so assign a new one 936 // if we already tried with slug+"i" we have 937 // to strip "i" before adding it again! 938 $i++; 939 $slug = substr($slug, 0, 940 ($i > 2) ? strlen($slug)-strlen($i-1) : strlen($slug)).$i; 941 } 942 $article->setPostSlug($slug); 943 } 944 945 // keep the old version, since we're going to need it to udpate the category counters 946 $oldArticle = $this->getArticle( $article->getId()); 947 948 // and now update the new version 949 $result = $this->update( $article ); 950 951 // we don't bother doing anything else if the query above failed... 952 if( !$result ) 953 return false; 954 955 // update the article text 956 $this->updateArticleText( $article ); 957 958 // update categories 959 if( !$this->updatePostCategoriesLink( $article, $oldArticle )) { 960 return false; 961 } 962 963 // update custom fields 964 if( !$this->updateArticleCustomFields( $article->getId(), $article->getBlogId(), 965 $article->getCustomFields())) { 966 return false; 967 } 968 969 // update global article categories 970 if( !$this->updateGlobalArticleCategoriesLink( $article, $oldArticle )) { 971 return false; 972 } 973 974 // update the blog counter 975 if( $oldArticle->getStatus() == POST_STATUS_PUBLISHED && $article->getStatus() != POST_STATUS_PUBLISHED ) { 976 lt_include( PLOG_CLASS_PATH."class/data/timestamp.class.php" ); 977 $blogs = new Blogs(); 978 $blogInfo = $article->getBlogInfo(); 979 $blogInfo->setTotalPosts( $blogInfo->getTotalPosts() - 1 ); 980 $blogInfo->setUpdateDate( Timestamp::getNowTimestamp()); 981 $blogs->updateBlog( $blogInfo ); 982 } elseif ( $oldArticle->getStatus() != POST_STATUS_PUBLISHED && $article->getStatus() == POST_STATUS_PUBLISHED ) { 983 lt_include( PLOG_CLASS_PATH."class/data/timestamp.class.php" ); 984 $blogs = new Blogs(); 985 $blogInfo = $article->getBlogInfo(); 986 $blogInfo->setTotalPosts( $blogInfo->getTotalPosts() + 1 ); 987 $blogInfo->setUpdateDate( Timestamp::getNowTimestamp()); 988 $blogs->updateBlog( $blogInfo ); 989 } 990 991 // clean up the cache 992 lt_include( PLOG_CLASS_PATH."class/dao/recentarticles.class.php" ); 993 RecentArticles::resetRecentArticlesCache( $article->getBlogId()); 994 $this->_cache->removeData( $article->getBlogId(), CACHE_ARTICLESPERMONTH ); 995 $this->_cache->removeData( $article->getId(), CACHE_ARTICLETEXT ); 996 $this->_cache->setData( $article->getId(), CACHE_ARTICLES, $article ); 997 $this->_cache->removeData( $article->getPostSlug(), CACHE_ARTICLES_BYNAME ); 998 999 return true; 1000 } 1001 1002 /** 1003 * updates the custom fields used by an article. It's actually easier to remove them all 1004 * and readd them again than check one by one if it already exists and if so updating it 1005 * and if not, add it 1006 * 1007 * @param artId 1008 * @param blogId 1009 * @param fields 1010 * @return True if successful or false otherwise 1011 */ 1012 function updateArticleCustomFields( $artId, $blogId, $fields ) 1013 { 1014 lt_include( PLOG_CLASS_PATH."class/dao/customfields/customfieldsvalues.class.php" ); 1015 $customFields = new CustomFieldsValues(); 1016 1017 // first remove the values 1018 if( !$customFields->removeArticleCustomFields( $artId )) 1019 return false; 1020 1021 foreach( $fields as $field ) { 1022 $customFields->addCustomFieldValue( $field->getFieldId(), 1023 $field->getValue(), 1024 $artId, $blogId ); 1025 } 1026 1027 return true; 1028 } 1029 1030 /** 1031 * Updates the number of times a post has been read. This method does not just increase the num_read 1032 * counter by one but it can set it to whatever we want... Usually the value we pass in '$numReads' will 1033 * be the old value + 1, but it could be whatever. 1034 * 1035 * @param articleId A valid article identifier. 1036 * @param numReads A value, meaning how many times the post has been read. 1037 * @return Returns true if successful or false otherwise. 1038 */ 1039 function updateArticleNumReads( $articleId ) 1040 { 1041 lt_include( PLOG_CLASS_PATH . 'class/database/db.class.php' ); 1042 1043 $query = "UPDATE ".$this->getPrefix()."articles SET ". 1044 " num_reads = num_reads+1, date = date". 1045 " WHERE id = '".Db::qstr( $articleId )."'"; 1046 1047 $result = $this->Execute( $query ); 1048 1049 return $result; 1050 } 1051 1052 /** 1053 * similar as the one above but it takes an article 'name' instead of an article id 1054 * @see updateArticleNumReads 1055 * @param articleName an article "name" (the post 'slug') 1056 * @return true if successful or false otherwise 1057 */ 1058 function updateArticleNumReadsByName( $articleName ) 1059 { 1060 // we have to build up the query, which will be pretty long... 1061 $query = "UPDATE ".$this->getPrefix()."articles SET ". 1062 " num_reads = num_reads+1, date = date". 1063 " WHERE slug = '".Db::qstr($articleName)."'"; 1064 1065 $result = $this->Execute( $query ); 1066 1067 return $result; 1068 } 1069 1070 /** 1071 * Removes an article from the database 1072 * 1073 * If forever == true, the article is physically removed from the database. 1074 * Otherwise, the 'status' field is set to 'deleted' 1075 * 1076 * Problem is, that MySQL will automatically update the 'date' field because he feels 1077 * like it... even if we explicitely say date = old_date... grrreat :P 1078 * 1079 * Valid article identifier, blog identifier and user identifier are required to remove an 1080 * article. It was done for security reasons and to make perfectly clear that we are removing 1081 * an article (so that we wouldn't be deleting the wrong one if there was any bug!) 1082 * 1083 * @param artId A valid article identifier 1084 * @param userid A valid user identifier 1085 * @param blogId A valid blog identifier 1086 * @param forever A boolean meaning whether the post should be removed forever or simply its status 1087 * should be set to 'deleted' 1088 * @return Returns true if successful or false otherwise. 1089 */ 1090 function deleteArticle( $artId, $userId, $blogId, $forever = false ) 1091 { 1092 lt_include( PLOG_CLASS_PATH.'class/dao/articlecomments.class.php' ); 1093 lt_include( PLOG_CLASS_PATH.'class/database/db.class.php' ); 1094 lt_include( PLOG_CLASS_PATH.'class/dao/trackbacks.class.php' ); 1095 lt_include( PLOG_CLASS_PATH.'class/dao/customfields/customfieldsvalues.class.php' ); 1096 lt_include( PLOG_CLASS_PATH.'class/dao/articlenotifications.class.php' ); 1097 1098 $article = $this->getBlogArticle( $artId, $blogId, true, -1, -1, $userId ); 1099 if( !$article ) 1100 return false; 1101 1102 if( $forever ) { 1103 // delete the text 1104 $this->deleteArticleText( $artId ); 1105 1106 // update the links with article categories 1107 $this->deletePostCategoriesLink( $article ); 1108 1109 // update global article categories 1110 $this->updateGlobalArticleCategoriesLink( $article ); 1111 1112 // update the blog counters 1113 if( $article->getStatus() == POST_STATUS_PUBLISHED ) { 1114 $blogs = new Blogs(); 1115 $blogInfo = $article->getBlogInfo(); 1116 $blogInfo->setTotalPosts( $blogInfo->getTotalPosts() - 1 ); 1117 $blogs->updateBlog( $blogInfo ); 1118 } 1119 1120 // delete the article comments 1121 $comments = new ArticleComments(); 1122 $comments->deleteArticleComments( $article->getId()); 1123 1124 // and finally, delete the article data 1125 if( !$this->delete( "id", $artId )) 1126 return false; 1127 1128 // remove all related cache 1129 lt_include( PLOG_CLASS_PATH."class/dao/recentarticles.class.php" ); 1130 RecentArticles::resetRecentArticlesCache( $blogId ); 1131 $this->_cache->removeData( $blogId, CACHE_ARTICLESPERMONTH ); 1132 $this->_cache->removeData( $artId, CACHE_ARTICLES ); 1133 } 1134 else { 1135 $article->setStatus( POST_STATUS_DELETED ); 1136 $this->updateArticle( $article ); 1137 } 1138 1139 return true; 1140 } 1141 1142 /** 1143 * removes the text of an article 1144 * 1145 * @param articleId 1146 * @private 1147 * @return true if successful or false otherwise 1148 * @see Articles::deleteArticle 1149 */ 1150 function deleteArticleText( $articleId ) 1151 { 1152 $query = "DELETE FROM ".$this->getPrefix()."articles_text WHERE article_id = '".Db::qstr( $articleId )."'"; 1153 return( $this->Execute( $query )); 1154 } 1155 1156 /** 1157 * Removes all the posts from the given blog 1158 * 1159 * @param blogId The blog identifier 1160 */ 1161 function deleteBlogPosts( $blogId ) 1162 { 1163 $query = "SELECT id, user_id, blog_id FROM ".$this->getPrefix()."articles WHERE blog_id = '".Db::qstr( $blogId )."'"; 1164 $result = $this->Execute( $query ); 1165 1166 if( !$result ) 1167 return false; 1168 1169 while( $row = $result->FetchRow()) { 1170 $this->deleteArticle( $row["id"], $row["user_id"], $row["blog_id"], true ); 1171 } 1172 1173 return true; 1174 } 1175 1176 /** 1177 * @private 1178 */ 1179 function mapRow( $query_result ) 1180 { 1181 lt_include( PLOG_CLASS_PATH."class/dao/articlecategories.class.php" ); 1182 lt_include( PLOG_CLASS_PATH.'class/data/timestamp.class.php' ); 1183 lt_include( PLOG_CLASS_PATH.'class/dao/users.class.php' ); 1184 lt_include( PLOG_CLASS_PATH.'class/dao/blogs.class.php' ); 1185 1186 $id = $query_result['id']; 1187 1188 // this is a little dirty trick or otherwise the old 1189 // that don't have the 'properties' field will not work 1190 // as they will appear to have comments disabled 1191 if( $query_result['properties'] == "" ) { 1192 $tmpArray = Array( 'comments_enabled' => true ); 1193 $query_result['properties'] = serialize($tmpArray); 1194 } 1195 1196 $blogs = new Blogs(); 1197 $blogId = $query_result['blog_id']; 1198 $blogInfo = $blogs->getBlogInfo( $blogId ); 1199 1200 $blogSettings = $blogInfo->getSettings(); 1201 1202 if( $blogSettings ) 1203 $timeDiff = $blogSettings->getValue( 'time_offset' ); 1204 else 1205 $timeDiff = 0; 1206 1207 // we can use this auxiliary function to help us... 1208 $date = Timestamp::getDateWithOffset( $query_result['date'], $timeDiff ); 1209 $modifDate = Timestamp::getDateWithOffset( $query_result['modification_date'], $timeDiff ); 1210 1211 $categories = new ArticleCategories(); 1212 $articleCategories = $categories->getArticleCategories( $query_result['id'], $query_result['blog_id'] ); 1213 $categoryIds = Array(); 1214 foreach( $articleCategories as $category ) 1215 array_push( $categoryIds, $category->getId()); 1216 1217 // get the article text 1218 $postText = $this->getArticleText( $query_result['id'] ); 1219 1220 $article = new Article( $postText['topic'], 1221 $postText['text'], 1222 $categoryIds, 1223 $query_result['user_id'], 1224 $query_result['blog_id'], 1225 $query_result['status'], 1226 $query_result['num_reads'], 1227 unserialize($query_result['properties']), 1228 $query_result['slug'], 1229 $query_result['id'] ); 1230 1231 // and fill in all the fields with the information we just got from the db 1232 $article->setDate( $date ); 1233 $article->setModificationDate( $modifDate ); 1234 $article->setTimeOffset( $timeDiff ); 1235 $article->setCategories( $articleCategories ); 1236 // get information about the categories of the article 1237 $article->setBlogInfo( $blogInfo ); 1238 /*if ( $this->users === null ) 1239 $this->users = new Users(); 1240 $article->setUserInfo( $this->users->getUserInfoFromId( $query_result['user_id'] ));*/ 1241 1242 // counters 1243 $article->setTotalComments( $query_result['num_comments'] ); 1244 $article->setNumComments( $query_result['num_nonspam_comments'] ); 1245 $article->setTotalTrackbacks( $query_result['num_trackbacks'] ); 1246 $article->setNumTrackbacks( $query_result['num_nonspam_trackbacks'] ); 1247 1248 // global article category 1249 $article->setGlobalCategoryId( $query_result['global_category_id'] ); 1250 1251 // shown in summary or not 1252 $article->setInSummary( $query_result['in_summary_page'] ); 1253 1254 return $article; 1255 1256 } 1257 } 1258 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Mon Nov 26 21:04:15 2007 | par Balluche grâce à PHPXref 0.7 |
![]() |