| [ Index ] |
|
Code source de Serendipity 1.2 |
1 <?php # $Id: functions_entries.inc.php 1708 2007-06-05 07:58:47Z garvinhicking $ 2 # Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team) 3 # All rights reserved. See LICENSE file for licensing details 4 5 if (IN_serendipity !== true) { 6 die ("Don't hack!"); 7 } 8 9 if (defined('S9Y_FRAMEWORK_ENTRIES')) { 10 return; 11 } 12 @define('S9Y_FRAMEWORK_ENTRIES', true); 13 14 /** 15 * Delete a category or range of categories 16 * 17 * @access public 18 * @param string Holds the SQL string to pass to the 'BETWEEN' command. Like '1 5' would delete categories 1-5. 19 * @param string Holds the optional SQL string that contains an extra safety check so that only categories can be deleted if the user is an author of the category. 20 * @return array The DB result 21 */ 22 function serendipity_deleteCategory($category_range, $admin_category) { 23 global $serendipity; 24 25 if (!serendipity_checkPermission('adminCategoriesDelete')) { 26 return false; 27 } 28 29 serendipity_plugin_api::hook_event('backend_category_delete', $category_range); 30 31 return serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}category WHERE category_left BETWEEN {$category_range} {$admin_category}"); 32 } 33 34 /** 35 * Fetch a SQL ID subset of the category tree 36 * 37 * @access public 38 * @param int The Id of the parent category to fetch categorie childs from. (0: all) 39 * @return array An associative array of the left and right category next to the chosen one 40 */ 41 function serendipity_fetchCategoryRange($categoryid) { 42 global $serendipity; 43 44 $res =& serendipity_db_query("SELECT category_left, category_right, hide_sub FROM {$serendipity['dbPrefix']}category WHERE categoryid='". (int)$categoryid ."'"); 45 if (!is_array($res) || !isset($res[0]['category_left']) || !isset($res[0]['category_right'])) { 46 $res = array(array('category_left' => 0, 'category_right' => 0)); 47 } 48 49 if ($res[0]['hide_sub'] == 1) { 50 // Set ranges only to own category. Patch by netmorix 51 return array('category_left' => $res[0]['category_left'], 'category_right' => $res[0]['category_left']); 52 } else { 53 return array('category_left' => $res[0]['category_left'], 'category_right' => $res[0]['category_right']); 54 } 55 } 56 57 /** 58 * Returns SQL code to use when fetching entries that are contained within multiple categories 59 * 60 * @access public 61 * @param string A listing of category ids to check, separated by ";" 62 * @param boolean Toggle whether to include or exclude entries of this category 63 * @return string Returns the SQL code for selecting entries of the calculated categories 64 */ 65 function serendipity_getMultiCategoriesSQL($cats, $invert = false) { 66 global $serendipity; 67 68 $mcategories = explode(';', $cats); 69 $cat_sql_array = array(); 70 foreach($mcategories AS $categoryid) { 71 $categoryid = (int)$categoryid; 72 73 if ($categoryid != 0) { 74 $cat_sql_array[] = " (c.category_left " . ($invert ? " NOT " : "") . " BETWEEN " . implode(' AND ', serendipity_fetchCategoryRange($categoryid)) . ')'; 75 } 76 } 77 78 if (count($cat_sql_array) < 1) { 79 return ''; 80 } 81 82 return '(' . implode(($invert ? ' AND ' : ' OR '), $cat_sql_array) . ')'; 83 } 84 85 /** 86 * Return the category properties of a specific category 87 * 88 * Either use the first or the second parameter to select a category by ID or name. It's not 89 * meant to be used with both parameters specified. 90 * 91 * @access public 92 * @param int The ID of the category to fetch 93 * @param string The Name of a category to fetch 94 * @return array Returns an array of category properties 95 */ 96 function serendipity_fetchCategoryInfo($categoryid, $categoryname = '') { 97 global $serendipity; 98 99 if (!empty($categoryname)) { 100 $query = "SELECT 101 c.authorid, 102 c.categoryid, 103 c.category_name, 104 c.category_description, 105 c.category_icon, 106 c.parentid, 107 c.hide_sub 108 FROM {$serendipity['dbPrefix']}category AS c 109 WHERE category_name = '" . serendipity_db_escape_string($categoryname) . "'"; 110 111 $ret =& serendipity_db_query($query); 112 return $ret[0]; 113 } else { 114 $query = "SELECT 115 c.authorid, 116 c.categoryid, 117 c.category_name, 118 c.category_description, 119 c.category_icon, 120 c.parentid, 121 c.hide_sub 122 FROM {$serendipity['dbPrefix']}category AS c 123 WHERE categoryid = " . (int)$categoryid; 124 125 $ret =& serendipity_db_query($query); 126 return $ret[0]; 127 } 128 } 129 130 /** 131 * Fetch a list of all category properties to a specific entry ID 132 * 133 * @access public 134 * @param int The ID of the entry 135 * @return array The array of associated categories to that entry 136 */ 137 function &serendipity_fetchEntryCategories($entryid) { 138 global $serendipity; 139 140 if (is_numeric($entryid)) { 141 $query = "SELECT 142 c.categoryid, 143 c.category_name, 144 c.category_description, 145 c.category_icon, 146 c.parentid 147 FROM {$serendipity['dbPrefix']}category AS c 148 INNER JOIN {$serendipity['dbPrefix']}entrycat AS ec 149 ON ec.categoryid = c.categoryid 150 WHERE ec.entryid = {$entryid} 151 ORDER BY c.category_name ASC"; 152 153 $cat =& serendipity_db_query($query); 154 if (!is_array($cat)) { 155 $arr = array(); 156 return $arr; 157 } else { 158 return $cat; 159 } 160 } 161 } 162 163 164 /** 165 * Fetch a list of entries 166 * 167 * The most central and versatile function of Serendipity, allows you to fetch entries 168 * depending on a LOT of options. 169 * One of the parameters missing is a restriction by category. You need to do that by 170 * setting the superglobal $serendipity['GET']['category'] to the category you want to fetch. 171 * Separate multiple categories by ";". 172 * Other "external" variables that affect this function are: 173 * $serendipity['short_archives'] - Indicates if the short archive listing is wanted, without the full entry text 174 * $serendipity['range'] - If $range is not used, the time restriction is fetched from this array, which holds a start timestamp and end timestamp. 175 * $serendipity['GET']['category'] - The category ID to restrict fetching entries from (can be seperated by ";") 176 * $serendipity['GET']['hide_category']- The category ID to NOT fetch entries from (can be seperated by ";") 177 * $serendipity['GET']['viewAuthor'] - Only fetch entries by this author 178 * $serendipity['GET']['page'] - The page number to show entries, for pagination 179 * 180 * If you want to use any of these options, set the variable before calling serendipity_fetchEntries(). You can reset the variables to their original content after the function call, if you need to. 181 * 182 * Several options perform different commands when different types are passed, like the $range 183 * parameter which can either be a string or an array with START/END range. 184 * 185 * @access public 186 * @param mixed Restricts fetching entries to a specific timespan. Behaves differently depending on the type: 187 * Numeric: 188 * YYYYMMDD - Shows all entries from YYYY-MM-DD. 189 * If DD is "00", it will show all entries from that month. 190 * If DD is any other number, it will show entries of that specific day. 191 * 2-Dimensional Array: 192 * Key #0 - Specifies the start timestamp (unix seconds) 193 * Key #1 - Specifies the end timestamp (unix seconds) 194 * Other (null, 3-dimensional Array, ...): 195 * Entries newer than $modified_since will be fetched 196 * @param boolean Indicates if the full entry will be fetched (body+extended: TRUE), or only the body (FALSE). 197 * @param string Holds a "Y" or "X, Y" string that tells which entries to fetch. X is the first entry offset, Y is number of entries. If not set, the global fetchLimit will be applied (15 entries by default) 198 * @param boolean Indicates whether drafts should be fetched (TRUE) or not 199 * @param int Holds a unix timestamp to be used in conjunction with $range, to fetch all entries newer than this timestamp 200 * @param string Holds the SQL "ORDER BY" statement. 201 * @param string Can contain any SQL code to inject into the central SQL statement for fetching the entry 202 * @param boolean If set to TRUE, all entries will be fetched from scratch and any caching is ignored 203 * @param boolean If set to TRUE, all sticky entries will NOT be fetched. 204 * @param string Can contain a SQL statement on which keys to select. Plugins can also set this, pay attention! 205 * @param string Can contain a SQL statement on how to group the query. Plugins can also set this, pay attention! 206 * @param string If set to "array", the array of entries will be returned. "flat-array" will only return the articles without their entryproperties. "single" will only return a 1-dimensional array. "query" will only return the used SQL. 207 * @return array Holds the super-array of all entries with all additional information 208 */ 209 function &serendipity_fetchEntries($range = null, $full = true, $limit = '', $fetchDrafts = false, $modified_since = false, $orderby = 'timestamp DESC', $filter_sql = '', $noCache = false, $noSticky = false, $select_key = null, $group_by = null, $returncode = 'array') { 210 global $serendipity; 211 212 $cond = array(); 213 $cond['orderby'] = $orderby; 214 if (isset($serendipity['short_archives']) && $serendipity['short_archives']) { 215 // In the short listing of all titles for a month, we don't want to have a limit applied. And we don't need/want toe 216 // full article body (consumes memory) 217 $limit = ''; 218 $full = false; 219 } 220 221 if ($full === true) { 222 $noCache = true; // So no entryproperties related to body/extended caching will be loaded 223 $body = ', e.body, e.extended'; 224 } else { 225 $body = ''; 226 } 227 228 if ($fetchDrafts === false) { 229 $drafts = "isdraft = 'false'"; 230 } 231 232 if ($limit != '') { 233 $serendipity['fetchLimit'] = $limit; 234 } 235 236 /* Attempt to grab range from $serendipity, if $range is not an array or null */ 237 if (!is_array($range) && !is_null($range) && isset($serendipity['range'])) { 238 $range = $serendipity['range']; 239 } 240 241 if (is_numeric($range)) { 242 $year = (int)substr($range, 0, 4); 243 $month = (int)substr($range, 4, 2); 244 $day = (int)substr($range, 6, 2); 245 246 $startts = serendipity_serverOffsetHour(mktime(0, 0, 0, $month, ($day == 0 ? 1 : $day), $year), true); 247 248 if ($day == 0) { 249 $month++; 250 } else { 251 $day++; 252 } 253 254 $endts = serendipity_serverOffsetHour(mktime(0, 0, 0, $month, ($day == 0 ? 1 : $day), $year), true); 255 256 $cond['and'] = " WHERE e.timestamp >= $startts AND e.timestamp <= $endts"; 257 } elseif (is_array($range) && count($range)==2) { 258 $startts = serendipity_serverOffsetHour((int)$range[0], true); 259 $endts = serendipity_serverOffsetHour((int)$range[1], true); 260 $cond['and'] = " WHERE e.timestamp >= $startts AND e.timestamp <= $endts"; 261 } else { 262 if ($modified_since) { 263 $unix_modified = strtotime($modified_since); 264 if ($unix_modified != -1) { 265 $cond['and'] = ' WHERE last_modified >= ' . (int)$unix_modified; 266 if (!empty($limit)) { 267 $limit = ($limit > $serendipity['max_fetch_limit'] ? $limit : $serendipity['max_fetch_limit']); 268 } 269 $cond['orderby'] = 'last_modified DESC'; 270 } 271 } 272 } 273 274 if (!empty($drafts)) { 275 if (!empty($cond['and'])) { 276 $cond['and'] .= " AND $drafts"; 277 } else { 278 $cond['and'] = "WHERE $drafts"; 279 } 280 } 281 282 if (isset($serendipity['GET']['viewAuthor'])) { 283 $multiauthors = explode(';', $serendipity['GET']['viewAuthor']); 284 $multiauthors_sql = array(); 285 foreach($multiauthors AS $multiauthor) { 286 $multiauthors_sql[] = 'e.authorid = ' . (int)$multiauthor; 287 } 288 289 $cond['and'] .= ' AND (' . implode(' OR ', $multiauthors_sql) . ')'; 290 } 291 292 $cat_sql = ''; 293 if (isset($serendipity['GET']['category'])) { 294 $cat_sql = serendipity_getMultiCategoriesSQL($serendipity['GET']['category']); 295 } elseif (isset($serendipity['GET']['hide_category'])) { 296 $cat_sql = serendipity_getMultiCategoriesSQL($serendipity['GET']['hide_category'], true); 297 } 298 299 if (!empty($cat_sql)) { 300 if (!empty($cond['and'])) { 301 $cond['and'] .= " AND ($cat_sql)"; 302 } else { 303 $cond['and'] = "WHERE ($cat_sql)"; 304 } 305 } 306 307 if (!empty($limit)) { 308 if (isset($serendipity['GET']['page']) && $serendipity['GET']['page'] > 1 && !strstr($limit, ',')) { 309 $limit = serendipity_db_limit(($serendipity['GET']['page']-1) * $limit, $limit); 310 } 311 312 $limit = serendipity_db_limit_sql($limit); 313 } 314 315 if (isset($serendipity['GET']['adminModule']) && $serendipity['GET']['adminModule'] == 'entries' && !serendipity_checkPermission('adminEntriesMaintainOthers')) { 316 if (!empty($cond['and'])) { 317 $cond['and'] .= " AND e.authorid = '" . $serendipity['authorid'] . "'"; 318 } else { 319 $cond['and'] = "WHERE e.authorid = '" . $serendipity['authorid'] . "'"; 320 } 321 } 322 323 if (!isset($serendipity['GET']['adminModule']) && !serendipity_db_bool($serendipity['showFutureEntries'])) { 324 if (!empty($cond['and'])) { 325 $cond['and'] .= " AND e.timestamp <= " . serendipity_db_time(); 326 } else { 327 $cond['and'] = "WHERE e.timestamp <= " . serendipity_db_time(); 328 } 329 } 330 331 if (!empty($filter_sql)) { 332 if (!empty($cond['and'])) { 333 $cond['and'] .= ' AND ' . $filter_sql; 334 } else { 335 $cond['and'] = 'WHERE ' . $filter_sql; 336 } 337 } 338 339 serendipity_plugin_api::hook_event('frontend_fetchentries', $cond, array('noCache' => $noCache, 'noSticky' => $noSticky, 'source' => 'entries')); 340 341 if ($serendipity['dbType'] == 'postgres' || 342 $serendipity['dbType'] == 'pdo-postgres') { 343 $group = ''; 344 $distinct = 'DISTINCT'; 345 } else { 346 $group = 'GROUP BY e.id'; 347 $distinct = ''; 348 } 349 350 if (!is_null($group_by)) { 351 $group = $group_by; 352 } 353 354 if (is_null($select_key)) { 355 $select_key = "$distinct 356 {$cond['addkey']} 357 358 e.id, 359 e.title, 360 e.timestamp, 361 e.comments, 362 e.exflag, 363 e.authorid, 364 e.trackbacks, 365 e.isdraft, 366 e.allow_comments, 367 e.last_modified, 368 369 a.realname AS author, 370 a.username AS loginname, 371 a.email"; 372 } 373 374 serendipity_ACL_SQL($cond); 375 376 // Store the unique query condition for entries for later reference, like getting the total article count. 377 $serendipity['fullCountQuery'] = " 378 FROM 379 {$serendipity['dbPrefix']}entries AS e 380 LEFT JOIN {$serendipity['dbPrefix']}authors a 381 ON e.authorid = a.authorid 382 LEFT JOIN {$serendipity['dbPrefix']}entrycat ec 383 ON e.id = ec.entryid 384 LEFT JOIN {$serendipity['dbPrefix']}category c 385 ON ec.categoryid = c.categoryid 386 {$cond['joins']} 387 {$cond['and']}"; 388 389 $query = "SELECT $select_key 390 $body 391 {$serendipity['fullCountQuery']} 392 $group 393 ORDER BY {$cond['orderby']} 394 $limit"; 395 396 // DEBUG: 397 // die($query); 398 $fetch_single = ($returncode == 'single' ? true: false); 399 400 if ($returncode == 'query') { 401 return $query; 402 } 403 404 $ret =& serendipity_db_query($query, $fetch_single, 'assoc'); 405 406 if (is_string($ret)) { 407 die("Query failed: $ret"); 408 } 409 410 if (is_array($ret) && $returncode == 'array') { 411 // The article's query LIMIT operates on a flattened entries layer so that 412 // an article having 5 associated categories won't count as 5 entries. 413 // But to store the expanded list of categories, we need to send a new 414 // query once for all entries we have just fetched. 415 // First code for this was sending 15 queries for 15 fetched entries, 416 // this is now limited to just one query per fetched articles group 417 418 serendipity_fetchEntryData($ret); 419 } 420 421 return $ret; 422 } 423 424 /** 425 * Fetch special entry data and attach it to a superarray of entries. 426 * 427 * Fetches all additional information like plugins, extended properties, additional categories for each entry. 428 * 429 * @access private 430 * @see serendipity_fetchEntries() 431 * @param array The array of entries where the output will be merged to (referenced) 432 * @return null 433 */ 434 function serendipity_fetchEntryData(&$ret) { 435 global $serendipity; 436 437 $search_ids = array(); // An array to hold all ids of the entry we want to fetch. 438 $assoc_ids = array(); // A temporary key association container to not have to loop through the return array once again. 439 440 foreach($ret AS $i => $entry) { 441 $search_ids[] = $entry['id']; 442 $ret[$i]['categories'] = array(); // make sure every article gets its category association 443 $assoc_ids[$entry['id']] = $i; // store temporary reference 444 } 445 446 serendipity_plugin_api::hook_event('frontend_entryproperties', $ret, $assoc_ids); 447 448 $query = "SELECT 449 ec.entryid, 450 c.categoryid, 451 c.category_name, 452 c.category_description, 453 c.category_icon, 454 c.parentid 455 FROM {$serendipity['dbPrefix']}category AS c 456 LEFT JOIN {$serendipity['dbPrefix']}entrycat AS ec 457 ON ec.categoryid = c.categoryid 458 WHERE " . serendipity_db_in_sql('ec.entryid', $search_ids) . " 459 ORDER BY c.category_name ASC"; 460 461 $search_ret =& serendipity_db_query($query, false, 'assoc'); 462 463 if (is_array($search_ret)) { 464 foreach($search_ret AS $i => $entry) { 465 $ret[$assoc_ids[$entry['entryid']]]['categories'][] = $entry; 466 } 467 } 468 } 469 470 /** 471 * Fetch a single entry by a specific condition 472 * 473 * @access public 474 * @param string The column to compare $val against (like 'id') 475 * @param string The value of the colum $key to compare with (like '4711') 476 * @param boolean Indicates if the full entry will be fetched (body+extended: TRUE), or only the body (FALSE). 477 * @param string Indicates whether drafts should be fetched 478 * @return 479 */ 480 function &serendipity_fetchEntry($key, $val, $full = true, $fetchDrafts = 'false') { 481 global $serendipity; 482 483 $cond = array(); 484 $cond['and'] = " "; // intentional dummy string to attach dummy AND parts to the WHERE clauses 485 486 if ($fetchDrafts == 'false') { 487 $cond['and'] = " AND e.isdraft = 'false' " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . serendipity_db_time() : ''); 488 } 489 490 if (isset($serendipity['GET']['adminModule']) && $serendipity['GET']['adminModule'] == 'entries' && !serendipity_checkPermission('adminEntriesMaintainOthers')) { 491 $cond['and'] = " AND e.authorid = '" . $serendipity['authorid'] . "'"; 492 } 493 494 serendipity_ACL_SQL($cond, true); 495 496 serendipity_plugin_api::hook_event('frontend_fetchentry', $cond, array('noSticky' => true)); 497 498 $querystring = "SELECT e.id, 499 e.title, 500 e.timestamp, 501 e.body, 502 e.comments, 503 e.trackbacks, 504 e.extended, 505 e.exflag, 506 e.authorid, 507 e.isdraft, 508 e.allow_comments, 509 e.last_modified, 510 e.moderate_comments, 511 512 a.realname AS author, 513 a.username AS loginname, 514 a.email 515 FROM 516 {$serendipity['dbPrefix']}entries e 517 LEFT JOIN {$serendipity['dbPrefix']}authors a 518 ON e.authorid = a.authorid 519 {$cond['joins']} 520 WHERE 521 e.$key LIKE '" . serendipity_db_escape_string($val) . "' 522 {$cond['and']} 523 LIMIT 1"; 524 525 $ret =& serendipity_db_query($querystring, true, 'assoc'); 526 527 if (is_array($ret)) { 528 $ret['categories'] =& serendipity_fetchEntryCategories($ret['id']); 529 $ret['properties'] =& serendipity_fetchEntryProperties($ret['id']); 530 $stack = array(); 531 $stack[0] = &$ret; 532 $assoc_ids = array($ret['id'] => 0); 533 serendipity_plugin_api::hook_event('frontend_entryproperties', $stack, $assoc_ids); 534 } 535 536 return $ret; 537 } 538 539 /** 540 * Fetches additional entry properties for a specific entry ID 541 * 542 * @access public 543 * @param int The ID of the entry to fetch additonal data for 544 * @return array The array of given properties to an entry 545 */ 546 function &serendipity_fetchEntryProperties($id) { 547 global $serendipity; 548 549 $parts = array(); 550 serendipity_plugin_api::hook_event('frontend_entryproperties_query', $parts); 551 552 $_properties =& serendipity_db_query("SELECT property, value FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int)$id . " " . $parts['and']); 553 if (!is_array($_properties)) { 554 $properties = array(); 555 } else { 556 $properties =& $_properties; 557 } 558 559 $property = array(); 560 foreach($properties AS $idx => $row) { 561 $property[$row['property']] =& $row['value']; 562 } 563 564 return $property; 565 } 566 567 /** 568 * Fetch a list of available categories for an author 569 * 570 * @access public 571 * @param mixed If set, the list of categories will be fetched according to the author id. If not set, all categories will be fetched. If set to "all", then all categories will be fetched. 572 * @param string Restrict the list to be returned to a specific category NAME. 573 * @param string The SQL query part for ORDER BY of the categories 574 * @param string The ACL artifact condition. If set to "write" only categories will be shown that the author can write to. If set to "read", only categories will be show that the author can read or write to. 575 * @return array Returns the array of categories 576 */ 577 function &serendipity_fetchCategories($authorid = null, $name = null, $order = null, $artifact_mode = 'write') { 578 global $serendipity; 579 580 if ($name === null) { 581 $name = ''; 582 } 583 584 if ($order === null) { 585 $order = 'category_name ASC'; 586 } 587 588 if (!isset($authorid) || $authorid === null) { 589 $authorid = ((isset($serendipity['authorid']) && !empty($serendipity['GET']['adminModule'])) ? $serendipity['authorid'] : 1); 590 } 591 592 if (isset($serendipity['authorid']) && !empty($serendipity['GET']['adminModule']) && $authorid != $serendipity['authorid'] && !serendipity_checkPermission('adminCategoriesMaintainOthers')) { 593 $authorid = $serendipity['authorid']; 594 } 595 596 $where = ''; 597 598 if ($authorid === -1 OR $authorid === 0) { 599 $sql_groupid = '0'; 600 } else { 601 $sql_groupid = 'ag.groupid'; 602 } 603 604 if ($authorid != 'all' && is_numeric($authorid)) { 605 $sql_authorid = $authorid; 606 if (!serendipity_checkPermission('adminCategoriesMaintainOthers', $authorid)) { 607 $where = " WHERE (c.authorid = $authorid OR c.authorid = 0) "; 608 $where .= "OR ( 609 acl.artifact_type = 'category' 610 AND acl.artifact_mode = '" . serendipity_db_escape_string($artifact_mode) . "' 611 ) "; 612 613 } 614 } else { 615 $sql_authorid = 'c.authorid'; 616 $where = ''; 617 } 618 619 if (!empty($name)) { 620 if ($where == '') { 621 $where = ' WHERE '; 622 } else { 623 $where = ' AND '; 624 } 625 626 $where .= " c.category_name = '" . serendipity_db_escape_string($name) . "'"; 627 } 628 629 if ($serendipity['dbType'] == 'postgres' || 630 $serendipity['dbType'] == 'pdo-postgres') { 631 $group = ''; 632 $distinct = 'DISTINCT'; 633 } else { 634 $group = 'GROUP BY c.categoryid'; 635 $distinct = ''; 636 } 637 638 $querystring = "SELECT $distinct c.categoryid, 639 c.category_name, 640 c.category_icon, 641 c.category_description, 642 c.authorid, 643 c.category_left, 644 c.category_right, 645 c.parentid, 646 647 a.username, 648 a.username AS loginname, 649 a.realname 650 FROM {$serendipity['dbPrefix']}category AS c 651 LEFT OUTER JOIN {$serendipity['dbPrefix']}authors AS a 652 ON c.authorid = a.authorid 653 LEFT OUTER JOIN {$serendipity['dbPrefix']}authorgroups AS ag 654 ON ag.authorid = $sql_authorid 655 LEFT OUTER JOIN {$serendipity['dbPrefix']}access AS acl 656 ON ($sql_groupid = acl.groupid AND acl.artifact_id = c.categoryid) 657 $where 658 $group"; 659 if (!empty($order)) { 660 $querystring .= "\n ORDER BY $order"; 661 } 662 663 $ret =& serendipity_db_query($querystring); 664 if (is_string($ret)) { 665 echo "Query failed: $ret"; 666 } 667 return $ret; 668 } 669 670 /** 671 * Rebuild the Category Nested Set tree 672 * 673 * @access public 674 * @see Based on http://www.sitepoint.com/article/hierarchical-data-database/1 675 * @param int The ID of the parent category to rebuild 676 * @param int The ID of the next left category 677 * @return int Returns the new ID 678 */ 679 function serendipity_rebuildCategoryTree($parent = 0, $left = 0) { 680 global $serendipity; 681 $right = $left + 1; 682 683 $result = serendipity_db_query("SELECT categoryid FROM {$serendipity['dbPrefix']}category WHERE parentid = '" . (int)$parent . "'"); 684 if ( is_array($result) ) { 685 foreach ( $result as $category ) { 686 $right = serendipity_rebuildCategoryTree($category['categoryid'], $right); 687 } 688 } 689 if ( $parent > 0 ) { 690 serendipity_db_query("UPDATE {$serendipity['dbPrefix']}category SET category_left='{$left}', category_right='{$right}' WHERE categoryid='{$parent}'"); 691 } 692 693 return $right + 1; 694 } 695 696 /** 697 * Searches the list of entries by a specific term 698 * 699 * @todo: Allow to show results of staticpage plugins or others 700 * @access public 701 * @param string The searchterm (may contain wildcards) 702 * @param int Restrict the number of results [also uses $serendipity['GET']['page'] for pagination] 703 * @return array Returns the superarray of entries found 704 */ 705 function &serendipity_searchEntries($term, $limit = '') { 706 global $serendipity; 707 708 if ($limit == '') { 709 $limit = $serendipity['fetchLimit']; 710 } 711 712 if (isset($serendipity['GET']['page']) && $serendipity['GET']['page'] > 1 && !strstr($limit, ',')) { 713 $limit = serendipity_db_limit(($serendipity['GET']['page']-1) * $limit, $limit); 714 } 715 716 $limit = serendipity_db_limit_sql($limit); 717 718 $term = serendipity_db_escape_string($term); 719 $cond = array(); 720 if ($serendipity['dbType'] == 'postgres' || 721 $serendipity['dbType'] == 'pdo-postgres') { 722 $group = ''; 723 $distinct = 'DISTINCT'; 724 $cond['find_part'] = "(title ILIKE '%$term%' OR body ILIKE '%$term%' OR extended ILIKE '%$term%')"; 725 } elseif ($serendipity['dbType'] == 'sqlite' || $serendipity['dbType'] == 'sqlite3') { 726 // Very extensive SQLite search. There currently seems no other way to perform fulltext search in SQLite 727 // But it's better than no search at all :-D 728 $group = 'GROUP BY e.id'; 729 $distinct = ''; 730 $term = serendipity_mb('strtolower', $term); 731 $cond['find_part'] = "(lower(title) LIKE '%$term%' OR lower(body) LIKE '%$term%' OR lower(extended) LIKE '%$term%')"; 732 } else { 733 $group = 'GROUP BY e.id'; 734 $distinct = ''; 735 $term = str_replace('"', '"', $term); 736 if (preg_match('@["\+\-\*~<>\(\)]+@', $term)) { 737 $cond['find_part'] = "MATCH(title,body,extended) AGAINST('$term' IN BOOLEAN MODE)"; 738 } else { 739 $cond['find_part'] = "MATCH(title,body,extended) AGAINST('$term')"; 740 } 741 } 742 743 $cond['and'] = " AND isdraft = 'false' " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND timestamp <= " . serendipity_db_time() : ''); 744 serendipity_plugin_api::hook_event('frontend_fetchentries', $cond, array('source' => 'search', 'term' => $term)); 745 serendipity_ACL_SQL($cond, 'limited'); 746 747 $serendipity['fullCountQuery'] = " 748 FROM 749 {$serendipity['dbPrefix']}entries e 750 LEFT JOIN {$serendipity['dbPrefix']}authors a 751 ON e.authorid = a.authorid 752 LEFT JOIN {$serendipity['dbPrefix']}entrycat ec 753 ON e.id = ec.entryid 754 {$cond['joins']} 755 WHERE 756 ({$cond['find_part']}) 757 {$cond['and']}"; 758 759 $querystring = "SELECT $distinct 760 e.id, 761 e.authorid, 762 a.realname AS author, 763 e.allow_comments, 764 e.moderate_comments, 765 a.email, 766 e.timestamp, 767 e.comments, 768 e.title, 769 e.body, 770 e.extended, 771 e.trackbacks, 772 e.exflag 773 {$serendipity['fullCountQuery']} 774 $group 775 ORDER BY timestamp DESC 776 $limit"; 777 778 $search =& serendipity_db_query($querystring); 779 780 if (is_array($search)) { 781 serendipity_fetchEntryData($search); 782 } 783 784 return $search; 785 } 786 787 /** 788 * Creates the Footer below the entries, with pagination options and parses it to Smarty 789 * 790 * The list of total entries is calculated from the serendipity_getTotelEntries() function 791 * 792 * @param string suffix for URLs 793 * @param int Amount of total entries 794 * @access public 795 * @see serendipity_getTotalEntries() 796 * @return null 797 */ 798 function serendipity_printEntryFooter($suffix = '.html', $totalEntries = null) { 799 global $serendipity; 800 801 if ($totalEntries === null) { 802 $totalEntries = serendipity_getTotalEntries(); 803 } 804 $totalPages = ceil($totalEntries / $serendipity['fetchLimit']); 805 806 if (!isset($serendipity['GET']['page'])) { 807 $serendipity['GET']['page'] = 1; 808 } 809 810 if ($totalPages <= 0 ) { 811 $totalPages = 1; 812 } 813 814 if ($serendipity['GET']['page'] > 1) { 815 $uriArguments = $serendipity['uriArguments']; 816 $uriArguments[] = 'P'. ($serendipity['GET']['page'] - 1); 817 $serendipity['smarty']->assign('footer_prev_page', serendipity_rewriteURL(implode('/', $uriArguments) . $suffix)); 818 } 819 820 $uriArguments = $serendipity['uriArguments']; 821 $uriArguments[] = 'P%s'; 822 $serendipity['smarty']->assign('footer_totalEntries', $totalEntries); 823 $serendipity['smarty']->assign('footer_totalPages', $totalPages); 824 $serendipity['smarty']->assign('footer_currentPage', $serendipity['GET']['page']); 825 $serendipity['smarty']->assign('footer_pageLink', serendipity_rewriteURL(implode('/', $uriArguments) . $suffix)); 826 $serendipity['smarty']->assign('footer_info', sprintf(PAGE_BROWSE_ENTRIES, (int)$serendipity['GET']['page'], $totalPages, $totalEntries)); 827 828 if ($serendipity['GET']['page'] < $totalPages) { 829 $uriArguments = $serendipity['uriArguments']; 830 $uriArguments[] = 'P'. ($serendipity['GET']['page'] + 1); 831 $serendipity['smarty']->assign('footer_next_page', serendipity_rewriteURL(implode('/', $uriArguments) . $suffix)); 832 } 833 } 834 835 /** 836 * Calculates the amount of available entries. 837 * 838 * This function uses the SQL query portion of the central serendipity_fetchEntries() query 839 * and modifies it with different GROUP statements to calculate the number of entries. 840 * 841 * @access public 842 * @see serendipity_fetchEntries() 843 * @return int The number of total entries 844 */ 845 function serendipity_getTotalEntries() { 846 global $serendipity; 847 848 // The unique query condition was built previously in serendipity_fetchEntries() 849 if ($serendipity['dbType'] == 'sqlite' || $serendipity['dbType'] == 'sqlite3') { 850 $querystring = "SELECT count(e.id) {$serendipity['fullCountQuery']} GROUP BY e.id"; 851 } else { 852 $querystring = "SELECT count(distinct e.id) {$serendipity['fullCountQuery']}"; 853 } 854 855 $query =& serendipity_db_query($querystring); 856 857 if (is_array($query) && isset($query[0])) { 858 if ($serendipity['dbType'] == 'sqlite' || $serendipity['dbType'] == 'sqlite3') { 859 return count($query); 860 } else { 861 return $query[0][0]; 862 } 863 } 864 865 return 0; 866 } 867 868 /** 869 * Passes the list of fetched entries from serendipity_fetchEntries() on to the Smarty layer 870 * 871 * This function contains all the core logic to group and prepare entries to be shown in your 872 * $entries.tpl template. It groups them by date, so that you can easily loop on the set of 873 * entries. 874 * This function is not only used for printing all entries, but also for printing individual 875 * entries. 876 * Several central Event hooks are executed here for the whole page flow, like header+footer data. 877 * 878 * @see serendipity_fetchEntries() 879 * @see serendipity_searchEntries() 880 * @access public 881 * @param array The array of entries with all of its data 882 * @param boolean Toggle whether the extended portion of an entry is requested (via $serendipity['GET']['id'] single entry view) 883 * @param boolean Indicates if this is a preview 884 * @param string The name of the SMARTY block that this gets parsed into 885 * @param boolean Indicates whether the assigned smarty variables should be parsed 886 * @param boolean Indicates whether to apply footer/header event hooks 887 * @param boolean Indicates whether the pagination footer should be displayed 888 * @param mixed Indicates whether the input $entries array is already grouped in preparation for the smarty $entries output array [TRUE], or if it shall be grouped by date [FALSE] or if a plugin hook shall be executed to modify data ['plugin']. This setting can also be superseded by a 'entry_display' hook. 889 * @return 890 */ 891 function serendipity_printEntries($entries, $extended = 0, $preview = false, $smarty_block = 'ENTRIES', $smarty_fetch = true, $use_hooks = true, $use_footer = true, $use_grouped_array = false) { 892 global $serendipity; 893 894 if ($use_hooks) { 895 $addData = array('extended' => $extended, 'preview' => $preview); 896 serendipity_plugin_api::hook_event('entry_display', $entries, $addData); 897 898 if (isset($entries['clean_page']) && $entries['clean_page'] === true) { 899 if ($serendipity['view'] == '404') { 900 $serendipity['view'] = 'plugin'; 901 } 902 903 $serendipity['smarty']->assign(array( 904 'plugin_clean_page' => true, 905 'view' => $serendipity['view']) 906 ); 907 serendipity_smarty_fetch($smarty_block, 'entries.tpl', true); 908 return; // no display of this item 909 } 910 } 911 912 913 // We shouldn't return here, because we want Smarty to handle the output 914 if (!is_array($entries) || $entries[0] == false || !isset($entries[0]['timestamp'])) { 915 $entries = array(); 916 } 917 918 // A plugin executed in entry_display should be able to change the way of ordering entries. Forward-Thinking. ;) 919 if (isset($entries['use_grouped_array'])) { 920 $use_grouped_array = $entries['use_grouped_array']; 921 } 922 923 if ($use_grouped_array === false) { 924 // Use grouping by date (default) 925 $dategroup = array(); 926 for ($x = 0, $num_entries = count($entries); $x < $num_entries; $x++) { 927 if (!empty($entries[$x]['properties']['ep_is_sticky']) && serendipity_db_bool($entries[$x]['properties']['ep_is_sticky'])) { 928 $entries[$x]['is_sticky'] = true; 929 $key = 'sticky'; 930 } else { 931 $key = date('Ymd', serendipity_serverOffsetHour($entries[$x]['timestamp'])); 932 } 933 934 $dategroup[$key]['date'] = $entries[$x]['timestamp']; 935 $dategroup[$key]['is_sticky'] = (isset($entries[$x]['is_sticky']) && serendipity_db_bool($entries[$x]['is_sticky']) ? true : false); 936 $dategroup[$key]['entries'][] = &$entries[$x]; 937 } 938 } elseif ($use_grouped_array === 'plugin') { 939 // Let a plugin do the grouping 940 serendipity_plugin_api::hook_event('entry_groupdata', $entries); 941 $dategroup =& $entries; 942 } else { 943 $dategroup =& $entries; 944 } 945 946 foreach($dategroup as $properties) { 947 foreach($properties['entries'] as $x => $_entry) { 948 $entry = &$properties['entries'][$x]; // PHP4 Compat 949 950 if (!empty($entry['properties']['ep_cache_body'])) { 951 $entry['body'] = &$entry['properties']['ep_cache_body']; 952 $entry['is_cached'] = true; 953 } 954 955 //--JAM: Highlight-span search terms 956 if ($serendipity['action'] == 'search') { 957 $searchterms = str_replace('"', '', $serendipity['GET']['searchterms']); 958 $searchterms = explode($searchterms, ' '); 959 foreach($searchterms as $searchdx => $searchterm) { 960 $searchclass = "foundterm foundterm".$searchdx; 961 $entry['body'] = preg_replace('/('.$searchterm.')/mi', '<span class="'.$searchclass.'">\1</span>', $entry['body']); 962 } 963 } 964 965 if (!empty($entry['properties']['ep_cache_extended'])) { 966 $entry['extended'] = &$entry['properties']['ep_cache_extended']; 967 $entry['is_cached'] = true; 968 } 969 970 if ($preview) { 971 $entry['author'] = $entry['realname']; 972 $entry['authorid'] = $serendipity['authorid']; 973 } 974 975 $addData = array('from' => 'functions_entries:printEntries'); 976 if ($entry['is_cached']) { 977 $addData['no_scramble'] = true; 978 } 979 serendipity_plugin_api::hook_event('frontend_display', $entry, $addData); 980 981 if ($preview) { 982 $entry['author'] = $entry['realname']; 983 $entry['authorid'] = $serendipity['authorid']; 984 } 985 986 $authorData = array( 987 'authorid' => $entry['authorid'], 988 'username' => $entry['loginname'], 989 'email' => $entry['email'], 990 'realname' => $entry['author'] 991 ); 992 993 $entry['link'] = serendipity_archiveURL($entry['id'], $entry['title'], 'serendipityHTTPPath', true, array('timestamp' => $entry['timestamp'])); 994 $entry['commURL'] = serendipity_archiveURL($entry['id'], $entry['title'], 'baseURL', false, array('timestamp' => $entry['timestamp'])); 995 $entry['html_title']= $entry['title']; 996 $entry['title'] = htmlspecialchars($entry['title']); 997 998 $entry['title_rdf'] = preg_replace('@-{2,}@', '-', $entry['html_title']); 999 $entry['rdf_ident'] = serendipity_archiveURL($entry['id'], $entry['title_rdf'], 'baseURL', true, array('timestamp' => $entry['timestamp'])); 1000 $entry['link_rdf'] = serendipity_rewriteURL(PATH_FEEDS . '/ei_'. $entry['id'] .'.rdf'); 1001 $entry['title_rdf'] = htmlspecialchars($entry['title_rdf']); 1002 1003 $entry['link_allow_comments'] = $serendipity['baseURL'] . 'comment.php?serendipity[switch]=enable&serendipity[entry]=' . $entry['id']; 1004 $entry['link_deny_comments'] = $serendipity['baseURL'] . 'comment.php?serendipity[switch]=disable&serendipity[entry]=' . $entry['id']; 1005 $entry['allow_comments'] = serendipity_db_bool($entry['allow_comments']); 1006 $entry['moderate_comments'] = serendipity_db_bool($entry['moderate_comments']); 1007 $entry['viewmode'] = ($serendipity['GET']['cview'] == VIEWMODE_LINEAR ? VIEWMODE_LINEAR : VIEWMODE_THREADED); 1008 $entry['link_popup_comments'] = $serendipity['serendipityHTTPPath'] .'comment.php?serendipity[entry_id]='. $entry['id'] .'&serendipity[type]=comments'; 1009 $entry['link_popup_trackbacks'] = $serendipity['serendipityHTTPPath'] .'comment.php?serendipity[entry_id]='. $entry['id'] .'&serendipity[type]=trackbacks'; 1010 $entry['link_edit'] = $serendipity['baseURL'] .'serendipity_admin.php?serendipity[action]=admin&serendipity[adminModule]=entries&serendipity[adminAction]=edit&serendipity[id]='. $entry['id']; 1011 $entry['link_trackback'] = $serendipity['baseURL'] .'comment.php?type=trackback&entry_id='. $entry['id']; 1012 $entry['link_viewmode_threaded'] = $serendipity['serendipityHTTPPath'] . $serendipity['indexFile'] .'?url='. $entry['commURL'] .'&serendipity[cview]='. VIEWMODE_THREADED; 1013 $entry['link_viewmode_linear'] = $serendipity['serendipityHTTPPath'] . $serendipity['indexFile'] .'?url='. $entry['commURL'] .'&serendipity[cview]='. VIEWMODE_LINEAR; 1014 $entry['link_author'] = serendipity_authorURL($authorData); 1015 1016 if (is_array($entry['categories'])) { 1017 foreach ($entry['categories'] as $k => $v) { 1018 $entry['categories'][$k]['category_link'] = serendipity_categoryURL($entry['categories'][$k]); 1019 } 1020 } 1021 1022 if (strlen($entry['extended'])) { 1023 $entry['has_extended'] = true; 1024 } 1025 1026 if (isset($entry['exflag']) && $entry['exflag'] && ($extended || $preview)) { 1027 $entry['is_extended'] = true; 1028 } 1029 1030 if (serendipity_db_bool($entry['allow_comments']) || !isset($entry['allow_comments']) || $entry['comments'] > 0) { 1031 $entry['has_comments'] = true; 1032 $entry['label_comments'] = $entry['comments'] == 1 ? COMMENT : COMMENTS; 1033 } 1034 1035 if (serendipity_db_bool($entry['allow_comments']) || !isset($entry['allow_comments']) || $entry['trackbacks'] > 0) { 1036 $entry['has_trackbacks'] = true; 1037 $entry['label_trackbacks'] = $entry['trackbacks'] == 1 ? TRACKBACK : TRACKBACKS; 1038 } 1039 1040 if ($_SESSION['serendipityAuthedUser'] === true && ($_SESSION['serendipityAuthorid'] == $entry['authorid'] || serendipity_checkPermission('adminEntriesMaintainOthers'))) { 1041 $entry['is_entry_owner'] = true; 1042 } 1043 1044 $entry['display_dat'] = ''; 1045 serendipity_plugin_api::hook_event('frontend_display:html:per_entry', $entry); 1046 $entry['plugin_display_dat'] =& $entry['display_dat']; 1047 1048 if ($preview) { 1049 ob_start(); 1050 serendipity_plugin_api::hook_event('backend_preview', $entry); 1051 $entry['backend_preview'] = ob_get_contents(); 1052 ob_end_clean(); 1053 } 1054 1055 /* IF WE ARE DISPLAYING A FULL ENTRY */ 1056 if (isset($serendipity['GET']['id'])) { 1057 $comment_add_data = array( 1058 'comments_messagestack' => (isset($serendipity['messagestack']['comments']) ? (array)$serendipity['messagestack']['comments'] : array()), 1059 'is_comment_added' => (isset($serendipity['GET']['csuccess']) && $serendipity['GET']['csuccess'] == 'true' ? true: false), 1060 'is_comment_moderate' => (isset($serendipity['GET']['csuccess']) && $serendipity['GET']['csuccess'] == 'moderate' ? true: false) 1061 ); 1062 1063 $serendipity['smarty']->assign($comment_add_data); 1064 serendipity_displayCommentForm( 1065 $entry['id'], 1066 $serendipity['serendipityHTTPPath'] . $serendipity['indexFile'] . '?url=' . $entry['commURL'], 1067 true, 1068 $serendipity['POST'], 1069 true, 1070 serendipity_db_bool($entry['moderate_comments']), 1071 $entry 1072 ); 1073 } // END FULL ENTRY LOGIC 1074 } // end foreach-loop (entries) 1075 } // end foreach-loop (dates) 1076 1077 if (!isset($serendipity['GET']['id']) && 1078 (!isset($serendipity['hidefooter']) || $serendipity['hidefooter'] == false) && 1079 ($num_entries <= $serendipity['fetchLimit']) && 1080 $use_footer) { 1081 serendipity_printEntryFooter(); 1082 } 1083 1084 $serendipity['smarty']->assign_by_ref('entries', $dategroup); 1085 unset($entries, $dategroup); 1086 1087 if (isset($serendipity['short_archives']) && $serendipity['short_archives']) { 1088 serendipity_smarty_fetch($smarty_block, 'entries_summary.tpl', true); 1089 } elseif ($smarty_fetch == true) { 1090 serendipity_smarty_fetch($smarty_block, 'entries.tpl', true); 1091 } 1092 1093 } // end function serendipity_printEntries 1094 1095 /** 1096 * Deprecated: Delete some garbage when an entry was deleted, especially static pages 1097 * 1098 * @deprecated 1099 * @access public 1100 * @param int The deleted entry ID 1101 * @param int A timestamp for the entry archive page 1102 * @return null 1103 */ 1104 function serendipity_purgeEntry($id, $timestamp = null) { 1105 global $serendipity; 1106 1107 // If pregenerate is not set, short circuit all this logic 1108 // and remove nothing. 1109 if(!isset($serendipity['pregenerate'])) { 1110 return; 1111 } 1112 1113 if (isset($timestamp)) { 1114 $dated = date('Ymd', serendipity_serverOffsetHour($timestamp)); 1115 $datem = date('Ym', serendipity_serverOffsetHour($timestamp)); 1116 1117 @unlink("{$serendipity['serendipityPath']}/".PATH_ARCHIVES."/{$dated}.html"); 1118 @unlink("{$serendipity['serendipityPath']}/".PATH_ARCHIVES."/{$datem}.html"); 1119 } 1120 1121 // Fixme (the _* part) ! 1122 @unlink("{$serendipity['serendipityPath']}/".PATH_ARCHIVES."/{$id}_*.html"); 1123 @unlink("{$serendipity['serendipityPath']}/".PATH_FEEDS."/index.rss"); 1124 @unlink("{$serendipity['serendipityPath']}/".PATH_FEEDS."/index.rss2"); 1125 @unlink("{$serendipity['serendipityPath']}/index.html"); 1126 } 1127 1128 /** 1129 * Inserts a new entry into the database or updates an existing entry 1130 * 1131 * Another central function, that parses, prepares and commits changes to an entry 1132 * 1133 * @access public 1134 * @param array The new/modified entry data. 1135 * @return mixed Integer with new entry ID if successfull, a string or array if error(s). 1136 */ 1137 function serendipity_updertEntry($entry) { 1138 global $serendipity; 1139 1140 include_once S9Y_INCLUDE_PATH . 'include/functions_entries_admin.inc.php'; 1141 1142 $errors = array(); 1143 serendipity_plugin_api::hook_event('backend_entry_updertEntry', $errors, $entry); 1144 if (count($errors) > 0) { 1145 // Return error message(s) 1146 return implode("\n", $errors); 1147 } 1148 1149 serendipity_plugin_api::hook_event('backend_entry_presave', $entry); 1150 1151 $categories = $entry['categories']; 1152 unset($entry['categories']); 1153 1154 $newEntry = 0; 1155 $exflag = 0; 1156 1157 if (isset($entry['properties'])) { 1158 unset($entry['properties']); 1159 } 1160 1161 if (!is_numeric($entry['timestamp'])) { 1162 $entry['timestamp'] = time(); 1163 } 1164 1165 /* WYSIWYG-editor inserts empty ' ' for extended body; this is reversed here */ 1166 if (isset($entry['extended']) && (trim($entry['extended']) == '' || trim($entry['extended']) == '<br />' || trim($entry['extended']) == '<p></p>' || str_replace(array("\r", "\n", "\t", "\0", "<br />", "<p>", "</p>", "<br>"), array('', '', '', '', '', '', '', ''), trim($entry['extended'])) == '')) { 1167 $entry['extended'] = ''; 1168 } 1169 1170 if (strlen($entry['extended'])) { 1171 $exflag = 1; 1172 } 1173 1174 $entry['exflag'] = $exflag; 1175 1176 if (!is_numeric($entry['id'])) { 1177 /* we need to insert */ 1178 1179 unset($entry['id']); 1180 $entry['comments'] = 0; 1181 1182 if (!isset($entry['last_modified']) || !is_numeric($entry['last_modified'])) { 1183 $entry['last_modified'] = $entry['timestamp']; 1184 } 1185 1186 // New entries need an author 1187 $entry['author'] = $serendipity['user']; 1188 if (!isset($entry['authorid']) || empty($entry['authorid'])) { 1189 $entry['authorid'] = $serendipity['authorid']; 1190 } 1191 1192 if (!$_SESSION['serendipityRightPublish']) { 1193 $entry['isdraft'] = 'true'; 1194 } 1195 1196 if(!isset($entry['allow_comments'])){ 1197 $entry['allow_comments']='false'; 1198 } 1199 if(!isset($entry['moderate_comments'])){ 1200 $entry['moderate_comments']='false'; 1201 } 1202 1203 $res = serendipity_db_insert('entries', $entry); 1204 1205 if ($res) { 1206 $entry['id'] = $serendipity['lastSavedEntry'] = serendipity_db_insert_id('entries', 'id'); 1207 if (is_array($categories)) { 1208 foreach ($categories as $cat) { 1209 if (is_numeric($cat)) { 1210 serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}entrycat (entryid, categoryid) VALUES ({$entry['id']}, {$cat})"); 1211 } 1212 } 1213 } 1214 1215 serendipity_insertPermalink($entry); 1216 } else { 1217 //Some error message here 1218 return ENTRIES_NOT_SUCCESSFULLY_INSERTED; 1219 } 1220 $newEntry = 1; 1221 } else { 1222 /* we need to update */ 1223 1224 // Get settings from entry if already in DB, which should not be alterable with POST methods 1225 $_entry = serendipity_fetchEntry('id', $entry['id'], 1, 1); 1226 $entry['authorid'] = $_entry['authorid']; 1227 1228 if (isset($serendipity['GET']['adminModule']) && $serendipity['GET']['adminModule'] == 'entries' && $entry['authorid'] != $serendipity['authorid'] && !serendipity_checkPermission('adminEntriesMaintainOthers')) { 1229 // Only chiefs and admins can change other's entry. Else update fails. 1230 return; 1231 } 1232 1233 if (!$_SESSION['serendipityRightPublish']) { 1234 $entry['isdraft'] = 'true'; 1235 } 1236 1237 if (is_array($categories)) { 1238 serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}entrycat WHERE entryid={$entry['id']}"); 1239 foreach ($categories as $cat) { 1240 serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}entrycat (entryid, categoryid) VALUES ({$entry['id']}, {$cat})"); 1241 } 1242 } 1243 1244 if (!serendipity_db_bool($entry['isdraft']) && !serendipity_db_bool($_entry['isdraft'])) { 1245 $entry['last_modified'] = time(); 1246 } 1247 1248 $res = serendipity_db_update('entries', array('id' => $entry['id']), $entry); 1249 $newEntry = 0; 1250 serendipity_updatePermalink($entry); 1251 } 1252 1253 if (is_string($res)) { 1254 return $res; 1255 } 1256 1257 // Reset session data, so that a reload to this frame should not happen! 1258 $_SESSION['save_entry']['id'] = (int)$entry['id']; 1259 1260 if (!serendipity_db_bool($entry['isdraft'])) { 1261 serendipity_plugin_api::hook_event('frontend_display', $entry, array('no_scramble' => true, 'from' => 'functions_entries:updertEntry')); 1262 $drafted_entry = $entry; 1263 } 1264 1265 serendipity_purgeEntry($entry['id'], $entry['timestamp']); 1266 1267 if (!serendipity_db_bool($entry['isdraft'])) { 1268 // When saving an entry, first all references need to be gathered. But trackbacks to them 1269 // shall only be send at the end of the execution flow. However, certain plugins depend on 1270 // the existance of handled references. Thus we store the current references at this point, 1271 // execute the plugins and then reset the found references to the original state. 1272 serendipity_handle_references($entry['id'], $serendipity['blogTitle'], $drafted_entry['title'], $drafted_entry['body'] . $drafted_entry['extended'], true); 1273 } 1274 1275 // Send publish tags if either a new article has been inserted from scratch, or if the entry was previously 1276 // stored as draft and is now published 1277 $entry['categories'] =& $categories; 1278 if (!serendipity_db_bool($entry['isdraft']) && ($newEntry || serendipity_db_bool($_entry['isdraft']))) { 1279 serendipity_plugin_api::hook_event('backend_publish', $entry, $newEntry); 1280 } else { 1281 serendipity_plugin_api::hook_event('backend_save', $entry, $newEntry); 1282 } 1283 1284 if (!serendipity_db_bool($entry['isdraft'])) { 1285 // Now that plugins are executed, we go ahead into the Temple of Doom and send possibly failing trackbacks. 1286 // First, original list of references is restored (inside the function call) 1287 serendipity_handle_references($entry['id'], $serendipity['blogTitle'], $drafted_entry['title'], $drafted_entry['body'] . $drafted_entry['extended'], false); 1288 } 1289 1290 return (int)$entry['id']; 1291 } 1292 1293 /** 1294 * Delete an entry and everything that belongs to it (comments) 1295 * 1296 * @access public 1297 * @param int The Entry ID to delete 1298 * @return mixed FALSE or NULL on error 1299 */ 1300 function serendipity_deleteEntry($id) { 1301 global $serendipity; 1302 1303 if (!is_numeric($id)) { 1304 return false; 1305 } 1306 1307 // Purge the daily/monthly entries so they can be rebuilt 1308 $result = serendipity_db_query("SELECT timestamp, authorid FROM {$serendipity['dbPrefix']}entries WHERE id = '". (int)$id ."'", true); 1309 1310 if ($result[1] != $serendipity['authorid'] && !serendipity_checkPermission('adminEntriesMaintainOthers')) { 1311 // Only admins and chief users can delete entries which do not belong to the author 1312 return; 1313 } 1314 1315 serendipity_purgeEntry($id, $result[0]); 1316 1317 serendipity_plugin_api::hook_event('backend_delete_entry', $id); 1318 serendipity_db_query("DELETE FROM {$serendipity["dbPrefix"]}entries WHERE id=$id"); 1319 serendipity_db_query("DELETE FROM {$serendipity["dbPrefix"]}entrycat WHERE entryid=$id"); 1320 serendipity_db_query("DELETE FROM {$serendipity["dbPrefix"]}entryproperties WHERE entryid=$id"); 1321 serendipity_db_query("DELETE FROM {$serendipity["dbPrefix"]}comments WHERE entry_id=$id"); 1322 serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}references WHERE entry_id='$id' AND type = ''"); 1323 serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}permalinks WHERE entry_id='$id'"); 1324 } 1325 1326 /** 1327 * Return HTML containing a list of categories 1328 * 1329 * Prints a list of categories for use in forms, the sidebar, or whereever... 1330 * 1331 * @access public 1332 * @param array An array of categories, typically gathered by serendipity_fetchCategories() 1333 * @param array An array which holds IDs which are meant to be selected within a HTML SELECT form field [used for recursion] 1334 * @param int The type of category list (0: HTML span list, 1/2: <option>s, 3: HTML Div list, 4: CSV data) to return 1335 * @param int The parent ID of a category [for recursion] 1336 * @param int The current nesting level [for recursion] 1337 * @param string Tells the function, whether or not to display the XML button for each category. 1338 * If empty, no links to the xml feeds will be displayed; If you want to, you can 1339 * pass an image here (this setting is only used, when type==3). 1340 * @param string The character to use for blank indenting 1341 * @see serendipity_fetchCategories() 1342 */ 1343 function serendipity_generateCategoryList($cats, $select = array(0), $type = 0, $id = 0, $level = 0, $xmlImg = '', $blank_char = ' ') { 1344 global $serendipity; 1345 1346 if ( !is_array($cats) || !count($cats) ) 1347 return; 1348 1349 $ret = ''; 1350 foreach ($cats as $cat) { 1351 if ($cat['parentid'] == $id) { 1352 switch ($type) { 1353 case 0: 1354 $ret .= str_repeat(' ', $level * 2).'• <span id="catItem_' . $cat['categoryid'] . '"' . (($cat['categoryid'] && in_array($cat['categoryid'], $select)) ? ' selected="selected"' : '') . '><a href="?serendipity[adminModule]=category&serendipity[cat][catid]=' . $cat['categoryid'] . '">' . (!empty($cat['category_icon']) ? '<img style="vertical-align: middle;" src="' . $cat['category_icon'] . '" border="0" alt="' . $cat['category_name'] . '"/> ' : '') . htmlspecialchars($cat['category_name']) . (!empty($cat['category_description']) ? ' - ' . htmlspecialchars($cat['category_description']) : '') . '</a></span><br/>' . "\n"; 1355 break; 1356 case 1: 1357 case 2: 1358 $ret .= '<option value="' . $cat['categoryid'] . '"' . (($cat['categoryid'] && in_array($cat['categoryid'], $select)) ? ' selected="selected"' : '') . '>'; 1359 $ret .= str_repeat(' ', $level * 2) . htmlspecialchars($cat['category_name']) . ($type == 1 && !empty($cat['category_description']) ? (' - ' . htmlspecialchars($cat['category_description'])) : ''); 1360 $ret .= '</option>'; 1361 break; 1362 case 3: 1363 $category_id = serendipity_makeFilename($cat['category_name']); 1364 if (!empty($xmlImg)) { 1365 $ret .= sprintf( 1366 '<div style="padding-bottom: 2px;">' . 1367 '<a href="%s" title="%s"><img alt="xml" src="%s" style="vertical-align: bottom; display: inline; border: 0px" /></a> %s' . 1368 '<a href="%s" title="%s">%s</a>' . 1369 '</div>', 1370 $serendipity['serendipityHTTPPath'] . 'rss.php?category=' . $cat['categoryid'] . '_' . $category_id, 1371 htmlspecialchars($cat['category_description']), 1372 $xmlImg, 1373 str_repeat(' ', $level * 3), 1374 serendipity_categoryURL($cat, 'serendipityHTTPPath'), 1375 htmlspecialchars($cat['category_description']), 1376 htmlspecialchars($cat['category_name'])); 1377 } else { 1378 $ret .= sprintf( 1379 '%s<a href="%s" title="%s">%s</a><br />', 1380 str_repeat(' ', $level * 3), 1381 serendipity_categoryURL($cat, 'serendipityHTTPPath'), 1382 htmlspecialchars($cat['category_description']), 1383 htmlspecialchars($cat['category_name'])); 1384 } 1385 break; 1386 case 4: 1387 $ret .= $cat['categoryid'] . '|||' . str_repeat($blank_char, $level * 2) . $cat['category_name'] . '@@@'; 1388 break; 1389 } 1390 $ret .= serendipity_generateCategoryList($cats, $select, $type, $cat['categoryid'], $level + 1, $xmlImg, $blank_char); 1391 } 1392 } 1393 return $ret; 1394 } 1395 1396 /** 1397 * Set category associations of a specific entry 1398 * 1399 * @access public 1400 * @param int The ID of the entry 1401 * @param array An array of category IDs that this entry is associated to. 1402 * @return null 1403 */ 1404 function serendipity_updateEntryCategories($postid, $categories) { 1405 global $serendipity; 1406 1407 if (!$postid || !$categories) { 1408 return; 1409 } 1410 1411 $query = "DELETE FROM $serendipity[dbPrefix]entrycat WHERE entryid = " . (int)$postid; 1412 serendipity_db_query($query); 1413 1414 if (!is_array($categories)) { 1415 $categories = array(0 => $categories); 1416 } 1417 1418 foreach($categories AS $idx => $cat) { 1419 $query = "INSERT INTO $serendipity[dbPrefix]entrycat (categoryid, entryid) VALUES (" . (int)$cat . ", " . (int)$postid . ")"; 1420 serendipity_db_query($query); 1421 } 1422 } 1423 1424 /** 1425 * Gather an archive listing of older entries and passes it to Smarty 1426 * 1427 * The archives are created according to the current timestamp and show the current year. 1428 * $serendipity['GET']['category'] is honoured like in serendipity_fetchEntries() 1429 * $serendipity['GET']['viewAuthor'] is honoured like in serendipity_fetchEntries() 1430 * 1431 * @access public 1432 * @return null 1433 */ 1434 function serendipity_printArchives() { 1435 global $serendipity; 1436 1437 $f = serendipity_db_query("SELECT timestamp FROM {$serendipity['dbPrefix']}entries ORDER BY timestamp ASC LIMIT 1"); 1438 switch($serendipity['calendar']) { 1439 case 'gregorian': 1440 default: 1441 $lastYear = date('Y', serendipity_serverOffsetHour($f[0][0])); 1442 $lastMonth = date('m', serendipity_serverOffsetHour($f[0][0])); 1443 $thisYear = date('Y', serendipity_serverOffsetHour()); 1444 $thisMonth = date('m', serendipity_serverOffsetHour()); 1445 break; 1446 case 'persian-utf8': 1447 require_once S9Y_INCLUDE_PATH . 'include/functions_calendars.inc.php'; 1448 $lastYear = persian_date_utf('Y', serendipity_serverOffsetHour($f[0][0])); 1449 $lastMonth = persian_date_utf('m', serendipity_serverOffsetHour($f[0][0])); 1450 $thisYear = persian_date_utf('Y', serendipity_serverOffsetHour()); 1451 $thisMonth = persian_date_utf('m', serendipity_serverOffsetHour()); 1452 break; 1453 } 1454 $max = 0; 1455 1456 if (isset($serendipity['GET']['category'])) { 1457 $cat_sql = serendipity_getMultiCategoriesSQL($serendipity['GET']['category']); 1458 $cat_get = '/C' . (int)$serendipity['GET']['category']; 1459 } else { 1460 $cat_sql = ''; 1461 $cat_get = ''; 1462 } 1463 1464 if (isset($serendipity['GET']['viewAuthor'])) { 1465 $author_get = '/A' . (int)$serendipity['GET']['viewAuthor']; 1466 } else { 1467 $author_get = ''; 1468 } 1469 1470 $q = "SELECT e.timestamp 1471 FROM {$serendipity['dbPrefix']}entries e 1472 " . (!empty($cat_sql) ? " 1473 LEFT JOIN {$serendipity['dbPrefix']}entrycat ec 1474 ON e.id = ec.entryid 1475 LEFT JOIN {$serendipity['dbPrefix']}category c 1476 ON ec.categoryid = c.categoryid" : "") . " 1477 WHERE isdraft = 'false'" 1478 . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND timestamp <= " . serendipity_db_time() : '') 1479 . (!empty($cat_sql) ? ' AND ' . $cat_sql : '') 1480 . (!empty($serendipity['GET']['viewAuthor']) ? ' AND e.authorid = ' . (int)$serendipity['GET']['viewAuthor'] : '') 1481 . (!empty($cat_sql) ? " GROUP BY e.id" : ''); 1482 $entries =& serendipity_db_query($q, false, 'assoc'); 1483 1484 $group = array(); 1485 foreach($entries AS $entry) { 1486 $group[date('Ym', $entry['timestamp'])]++; 1487 } 1488 1489 $output = array(); 1490 for ($y = $thisYear; $y >= $lastYear; $y--) { 1491 $output[$y]['year'] = $y; 1492 for ($m = 12; $m >= 1; $m--) { 1493 1494 /* If the month we are checking are in the future, we drop it */ 1495 if ($m > $thisMonth && $y == $thisYear) { 1496 continue; 1497 } 1498 1499 /* If the month is lower than the lowest month containing entries, we're done */ 1500 if ($m < $lastMonth && $y <= $lastYear) { 1501 break; 1502 } 1503 1504 switch($serendipity['calendar']) { 1505 case 'gregorian': 1506 default: 1507 $s = serendipity_serverOffsetHour(mktime(0, 0, 0, $m, 1, $y), true); 1508 $e = serendipity_serverOffsetHour(mktime(23, 59, 59, $m, date('t', $s), $y), true); 1509 break; 1510 case 'persian-utf8': 1511 require_once S9Y_INCLUDE_PATH . 'include/functions_calendars.inc.php'; 1512 $s = serendipity_serverOffsetHour(persian_mktime(0, 0, 0, $m, 1, $y), true); 1513 $e = serendipity_serverOffsetHour(persian_mktime(23, 59, 59, $m, date('t', $s), $y), true); 1514 break; 1515 } 1516 1517 $entry_count = (int)$group[$y . (strlen($m) == 1 ? '0' : '') . $m]; 1518 1519 /* A silly hack to get the maximum amount of entries per month */ 1520 if ($entry_count > $max) { 1521 $max = $entry_count; 1522 } 1523 1524 $data = array(); 1525 $data['entry_count'] = $entry_count; 1526 $data['link'] = serendipity_archiveDateUrl($y . '/'. sprintf('%02s', $m) . $cat_get . $author_get); 1527 $data['link_summary'] = serendipity_archiveDateUrl($y . '/'. sprintf('%02s', $m) . $cat_get . $author_get, true); 1528 $data['date'] = $s; 1529 $output[$y]['months'][] = $data; 1530 } 1531 } 1532 1533 $serendipity['smarty']->assign_by_ref('archives', $output); 1534 $serendipity['smarty']->assign_by_ref('max_entries', $max); 1535 1536 serendipity_smarty_fetch('ARCHIVES', 'entries_archives.tpl', true); 1537 } 1538 1539 /** 1540 * Get total count for specific objects 1541 * 1542 * @access public 1543 * @param string The type of count to show: "entries", "trackbacks", "comments" 1544 * @return string The number 1545 */ 1546 function serendipity_getTotalCount($what) { 1547 global $serendipity; 1548 1549 switch($what) { 1550 case 'comments': 1551 $res = serendipity_db_query("SELECT SUM(e.comments) AS sum 1552 FROM {$serendipity['dbPrefix']}entries AS e 1553 WHERE e.isdraft = 'false' 1554 " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . serendipity_db_time() : ''), true, 'assoc'); 1555 return $res['sum']; 1556 case 'trackbacks': 1557 $res = serendipity_db_query("SELECT SUM(e.trackbacks) AS sum 1558 FROM {$serendipity['dbPrefix']}entries AS e 1559 WHERE e.isdraft = 'false' 1560 " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . serendipity_db_time() : ''), true, 'assoc'); 1561 return $res['sum']; 1562 case 'entries': 1563 $res = serendipity_db_query("SELECT COUNT(e.id) AS sum 1564 FROM {$serendipity['dbPrefix']}entries AS e 1565 WHERE e.isdraft = 'false' 1566 " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . serendipity_db_time() : ''), true, 'assoc'); 1567 return $res['sum']; 1568 1569 } 1570 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sat Nov 24 09:00:37 2007 | par Balluche grâce à PHPXref 0.7 |
|