[ Index ]
 

Code source de Serendipity 1.2

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/include/ -> functions_entries.inc.php (source)

   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('&quot;', '"', $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&amp;serendipity[entry]=' . $entry['id'];
1004              $entry['link_deny_comments']     = $serendipity['baseURL'] . 'comment.php?serendipity[switch]=disable&amp;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'] .'&amp;serendipity[type]=comments';
1009              $entry['link_popup_trackbacks']  = $serendipity['serendipityHTTPPath'] .'comment.php?serendipity[entry_id]='. $entry['id'] .'&amp;serendipity[type]=trackbacks';
1010              $entry['link_edit']              = $serendipity['baseURL'] .'serendipity_admin.php?serendipity[action]=admin&amp;serendipity[adminModule]=entries&amp;serendipity[adminAction]=edit&amp;serendipity[id]='. $entry['id'];
1011              $entry['link_trackback']         = $serendipity['baseURL'] .'comment.php?type=trackback&amp;entry_id='. $entry['id'];
1012              $entry['link_viewmode_threaded'] = $serendipity['serendipityHTTPPath'] . $serendipity['indexFile'] .'?url='. $entry['commURL'] .'&amp;serendipity[cview]='. VIEWMODE_THREADED;
1013              $entry['link_viewmode_linear']   = $serendipity['serendipityHTTPPath'] . $serendipity['indexFile'] .'?url='. $entry['commURL'] .'&amp;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('&nbsp;', $level * 2).'&bull;&nbsp;<span id="catItem_' . $cat['categoryid'] . '"' . (($cat['categoryid'] && in_array($cat['categoryid'], $select)) ? ' selected="selected"' : '') . '><a href="?serendipity[adminModule]=category&amp;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('&nbsp;', $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>&#160;%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('&#160;', $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('&#160;', $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  }


Généré le : Sat Nov 24 09:00:37 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics